Tmall_Fore_Sum
总结
一、项目结构
tmall
包结构
tmall.bean 实体类
tmall.comparator 比较器
tmall.dao DAO类
tmall.filter 过滤器
tmall.servlet servlet
tmall.test 测试类
tmall.util 工具类
web目录
css css文件
img 图片资源
js js文件
admin 后台管理用到的jsp文件
include 被包含的jsp文件
二、一些典型场景
1. 购物车
对于购物车来说,购物车其实是一个处理系统
立即购买:和加入购物车不同,因为只有一个产品,所以可以获取产品直接生成OrderItem并插入库中
加入购物车:其中包含了:将用户选择的产品获取,新生成OrderItem并插入数据库中
查看购物车:获取session中的user,无则弹出登录界面,获取了user后可获取关联的所有OrderItem,传入request中,打开cart.jsp。其中cart.jsp中有一点需要注意的是,是遍历未生成订单的orderItem
购物车页面操作:增加数量,删除订单等等,需要注意的是,删除并不是真的删除,只是附上了一个delete属性。
2. 订单状态流转
生成订单:点击立即购买或者在购物车页面点击提交到结算页面,进入结算页面,然后点击生成订单。取user,address等属性,新建Order类,其中status是未支付。然后获取结算页面buy方法(一个是点击去结算)时候传出的ois,然后通过fill将数量和总价,且将所有的orderItem的id写成一样的。
确认支付:创建订单的最后一步会“return
"@forealipay?oid="
+order.getId() +
"&total="
+total;
”,所以会调用alipay的方法,直接跳转到alipay.jsp,然后再二维码界面点击确认支付,会调用payed方法,改变status,然后跳转页面。
后台发货:这是后端的内容,当状态是待发货的时候,点击发货跳转delivery方法,根据id获取order对象,然后把status改,更新数据库,跳转到orderlist的页面
确认收货:点击确认收货,confirmPay方法调用,通过oid获取o,然后fill o对象,把o放到request的o上,跳转到confirmPay.jsp,通过这个jsp回顾并且底下再次出现确认收货,点击后改变status。
评价:比较简单,通过点击评价,调用forereview方法,获取该order,然后获取产品id,获取产品的评论和数量,把产品和评价的信息放在request上,跳转到review.jsp页面,然后可以点击评价,获取o,然后修改status,更新到数据库,获取pid,然后获取评价信息,获取user,创建review对象,设置数值并且导入数据库中,跳转到reviewPage。
3. CRUD
CRUD运用在后台各种功能上,简单的说,还是基本的CRUD,SQL语句,通过filter+Servlet的模式,在这里就不多赘述了,因为基本上全是CRUD。
4. 分页
分页管理在项目中也是比较重要的,基本上后台所有的页面都有分页类Page存在,可以好好回忆一下
Page类中有这些属性,int start 开始位置;int count 每页显示的数量;int total 总共多少数据; String param 其他页面传参时用到;
Page类中有这些方法,getTotalPage 根据 每页显示的数量count以及总共有多少条数据total,计算出总共有多少页;
getLast 计算出最后一页的数值是多少 isHasPreviouse 判断是否有前一页 isHasNext 判断是否有后一页
在每次的BaseBackServlet的service方法中,因为每次都会继承这个类,所以方法每次都会调用。
int start= 0;
int count = 5;
try {
start = Integer.parseInt(request.getParameter("page.start"));
} catch (Exception e) {
}
try {
count = Integer.parseInt(request.getParameter("page.count"));
} catch (Exception e) {
}
Page page = new Page(start,count);
这个好理解,剩下的就是jsp页面上的各种方法了。具体可以看之前的category
概述一下,adminPage中,中间页
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status">
<li>
<a href="?page.start=${status.index*page.count}" class="current">${status.count}</a>
</li>
</c:forEach>
注意status.index是从0开始的,status.count是从1开始的,点击了中间的链接,就相当于传了参。0-4;5-9;这样一页页的传出来的,通过新的start,以分类为例,List<Category> cs = categoryDAO.list(page.getStart(),page.getCount());可以获得新的cs,然后传参到页面中显示。
5. 一类产品多属性配置
属性管理,准备好PropertyServlet类,属性是依附在分类上的,在分类时,点击按钮进入属性管理页面,先通过页面点击时获取分类的id,进行DAO操作,获取该分类下的所有属性,并且从此可以对其进行CRUD。
<td><a href="admin_property_list?cid=${c.id}"><span class="glyphicon glyphicon-th-list"></span></a></td>
public String list(HttpServletRequest request, HttpServletResponse response, Page page) {
int cid = Integer.parseInt(request.getParameter("cid"));
Category c = categoryDAO.get(cid);
List<Property> ps = propertyDAO.list(cid, page.getStart(),page.getCount());
int total = propertyDAO.getTotal(cid);
page.setTotal(total);
page.setParam("&cid="+c.getId());
request.setAttribute("ps", ps);
request.setAttribute("c", c);
request.setAttribute("page", page);
return "admin/listProperty.jsp";
}
Param也可以看出来用在了这里
6. 一款产品多图片维护
难点在于上传图片
public String add(HttpServletRequest request, HttpServletResponse response, Page page) {
//上传文件的输入流
InputStream is = null;
//提交上传文件时的其他参数
Map<String,String> params = new HashMap<>();
//解析上传
is = parseUpload(request, params);
//根据上传的参数生成productImage对象
String type= params.get("type");
int pid = Integer.parseInt(params.get("pid"));
Product p =productDAO.get(pid);
ProductImage pi = new ProductImage();
pi.setType(type);
pi.setProduct(p);
productImageDAO.add(pi);
//生成文件
String fileName = pi.getId()+ ".jpg";
String imageFolder;
String imageFolder_small=null;
String imageFolder_middle=null;
if(ProductImageDAO.type_single.equals(pi.getType())){
imageFolder= request.getSession().getServletContext().getRealPath("img/productSingle");
imageFolder_small= request.getSession().getServletContext().getRealPath("img/productSingle_small");
imageFolder_middle= request.getSession().getServletContext().getRealPath("img/productSingle_middle");
}
else
imageFolder= request.getSession().getServletContext().getRealPath("img/productDetail");
File f = new File(imageFolder, fileName);
f.getParentFile().mkdirs();
// 复制文件
try {
if(null!=is && 0!=is.available()){
try(FileOutputStream fos = new FileOutputStream(f)){
byte b[] = new byte[1024 * 1024];
int length = 0;
while (-1 != (length = is.read(b))) {
fos.write(b, 0, length);
}
fos.flush();
//通过如下代码,把文件保存为jpg格式
BufferedImage img = ImageUtil.change2jpg(f);
ImageIO.write(img, "jpg", f);
if(ProductImageDAO.type_single.equals(pi.getType())){
File f_small = new File(imageFolder_small, fileName);
File f_middle = new File(imageFolder_middle, fileName);
ImageUtil.resizeImage(f, 56, 56, f_small);
ImageUtil.resizeImage(f, 217, 190, f_middle);
}
}
catch(Exception e){
e.printStackTrace();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "@admin_productImage_list?pid="+p.getId();
}
接着在ProductImageServlet的add()方法中进行处理
1. parseUpload 获取上传文件的输入流
2. parseUpload 方法会修改params 参数,并且把浏览器提交的type,pid信息放在其中
3. 从params 中取出type,pid信息,并根据这个type,pid,借助productImageDAO,向数据库中插入数据。
4. 根据request.getSession().getServletContext().getRealPath( "img/productSingle"),定位到存放分类图片的目录
除了productSingle,还有productSingle_middle和productSingle_small。 因为每上传一张图片,都会有对应的正常,中等和小的三种大小图片,并且放在3个不同的目录下
5. 文件命名以保存到数据库的分类对象的id+".jpg"的格式命名
6. 根据步骤1获取的输入流,把浏览器提交的文件,复制到目标文件
7. 借助ImageUtil.change2jpg()方法把格式真正转化为jpg,而不仅仅是后缀名为.jpg
8. 再借助ImageUtil.resizeImage把正常大小的图片,改变大小之后,分别复制到productSingle_middle和productSingle_small目录下。
9. 处理完毕之后,客户端条跳转到admin_productImage_list?pid=,并带上pid。
7. 产品展示
产品展示也是重头戏,主要用到了集合包含集合的思路,
一个Cs集合,包含了所有的Category;
其中的一个Category集合中又会有所有的产品集合,CPs,这是正常的
其中的一个Category集合中又会有所有的产品集合,但是这些产品集合被装在ProByRow中,有多个pbrs集合,然后展示的时候直 接获取pbrs就可以了
8. 搜索查询
这一块没有好好做,不过看看原理基本也差不多,通过表中的关键字+模糊查询,然后把所有符合的产品放到list中,然后展示出来所有的products。当然了,会有自己的专属search页面。
9. 登录、注册
好像一般的比较简单
复杂点的是登录验证,也就是买东西加入购物车的时候进行的验证,验证之后如果没有登录,那么进行拟态登录,其中拟态登录的时候用到了JQuery中的Ajax技术,这个觉得有必要稍微提一下,具体可以看simulatieLogin
两个监听器,通过class进行监听,这两个按钮都会通过JQuery的get方法,用异步ajax的方式访问forecheckLogin,获取当前是否登录状态
如果返回的不是"success" 即表明是未登录状态,那么就会打开登录的模态窗口
$(".buyLink").click(function(){
var page = "forecheckLogin";
$.get(
page,
function(result){
if("success"==result){
var num = $(".productNumberSetting").val();
location.href= $(".buyLink").attr("href")+"&num="+num;
}
else{
$("#loginModal").modal('show');
}
}
);
return false;
});
public String checkLogin(HttpServletRequest request, HttpServletResponse response, Page page) {
User user =(User) request.getSession().getAttribute("user");
if(null!=user)
return "%success";
return "%fail";
}
10. 登录验证
哪些页面需要登录?哪些页面不需要呢?
a. 不需要登录也可以访问的
如:注册,登录,产品,首页,分类,查询等等
b. 需要登录才能够访问的
如:购买行为,加入购物车行为,查看购物车,查看我的订单等等
不需要登录也可以访问的已经确定了,但是需要登录才能够访问,截止目前为止还不能确定,所以这个过滤器就判断如果不是注册,登录,产品这些,就进行登录校验
1. 准备字符串数组 noNeedAuthPage,存放哪些不需要登录也能访问的路径
2. 获取uri
3. 去掉前缀/tmall
4. 如果访问的地址是/fore开头,又不是/foreServlet
4.1 取出fore后面的字符串,比如是forecart,那么就取出cart
4.2 判断cart是否是在noNeedAuthPage
4.2 如果不在,那么就需要进行是否登录验证
4.3 从session中取出"user"对象
4.4 如果对象不存在,就客户端跳转到login.jsp
4.5 否则就正常执行
public class ForeAuthFilter implements Filter{
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String contextPath=request.getServletContext().getContextPath();
String[] noNeedAuthPage = new String[]{
"homepage",
"checkLogin",
"register",
"loginAjax",
"login",
"product",
"category",
"search"};
String uri = request.getRequestURI();
uri =StringUtils.remove(uri, contextPath);
if(uri.startsWith("/fore")&&!uri.startsWith("/foreServlet")){
String method = StringUtils.substringAfterLast(uri,"/fore" );
if(!Arrays.asList(noNeedAuthPage).contains(method)){
User user =(User) request.getSession().getAttribute("user");
if(null==user){
response.sendRedirect("login.jsp");
return;
}
}
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
三、设计模式
1.MVC模式
这个模式贯穿了整个项目
通过模型+试图+控制器的模式,页面时输入请求,通过Servlet进行处理,然后再其中加入DAO代码对数据库进行操作, 查到数据以后再通过servlet传递到视图中去。
2. Filter+Servlet+反射
之前的Servlet开发中会发现,当数量多了以后,一个功能模块3个方法,几十个模块加起来就近百个servlet,此时配置web.xml页面和编写Servlet类都变得极其冗余和复杂,所以我们引入了新的模式,Filter+Servlet,这样极大的减轻了开发的负担
具体:当有路径访问admin_category_list时候,通过BackServletFilter,查出所有admin开头的类,然后是categary,取值比如list,会把这个list存在method中后面使用,会访问categoryServlet,但是这个Servlet会继承BaseBackServlet,Base的service方法中就可以写一些初始的东西,比如list方法,然后给与相应的返回值。
如此一来,只要web配置了路径,Filter截取并按照设计模式来,大部分的模块功能都能较为简单的解决,也为后期SSH,SSM等框架做一个基础。
3. 统一的分页查询简化开发
所有的后台都使用同一个Page类,默认显示0-5,点击href的时候,会传参,通过计算重新获取新的start等值,再在数据库中查询新的数据并且显示到分页中。
4. 模块化JSP设计
简单的说就是能用公共页面的均为公共页面,从上往下
adminHeader.jsp 页面头,
最基本的<!DOCTYPE html>声明,jsp指令
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
contentType="text/html; charset=UTF-8" 告诉浏览器使用UTF-8进行中文编码识别
pageEncoding="UTF-8" 本jsp上的中文文字,使用UTF-8进行编码
isELIgnored="false" 本jsp上会使用EL表达式
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix='fmt' %>
引入JSTL,使用c和fmt两种标准标签库
<script src="js/jquery/2.0.0/jquery.min.js"></script>
<link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
<script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
<link href="css/back/style.css" rel="stylesheet">
引入js和css
<script>
function checkEmpty(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
return true;
}
function checkNumber(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
if(isNaN(value)){
alert(name+ "必须是数字");
$("#"+id)[0].focus();
return false;
}
return true;
}
function checkInt(id, name){
var value = $("#"+id).val();
if(value.length==0){
alert(name+ "不能为空");
$("#"+id)[0].focus();
return false;
}
if(parseInt(value)!=value){
alert(name+ "必须是整数");
$("#"+id)[0].focus();
return false;
}
return true;
}
一些预定义函数
$(function(){
$("a").click(function(){
var deleteLink = $(this).attr("deleteLink");
console.log(deleteLink);
if("true"==deleteLink){
var confirmDelete = confirm("确认要删除");
if(confirmDelete)
return true;
return false;
}
});
})
对于删除链接的确认操作
adminNavigator.jsp 导航栏页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<div class="navitagorDiv">
<nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
<img style="margin-left:10px;margin-right:0px" class="pull-left" src="img/site/tmallbuy.png" height="45px">
<a class="navbar-brand" href="#nowhere">天猫后台</a>
<a class="navbar-brand" href="admin_category_list">分类管理</a>
<a class="navbar-brand" href="admin_user_list">用户管理</a>
<a class="navbar-brand" href="admin_order_list">订单管理</a>
</nav>
</div>
adminPage.jsp 页码页
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<script>
$(function(){
$("ul.pagination li.disabled a").click(function(){
return false;
});
});
</script>
<nav>
<ul class="pagination">
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
<a href="?page.start=0${page.param}" aria-label="Previous" >
<span aria-hidden="true">«</span>
</a>
</li>
<li <c:if test="${!page.hasPreviouse}">class="disabled"</c:if>>
<a href="?page.start=${page.start-page.count}${page.param}" aria-label="Previous" >
<span aria-hidden="true">‹</span>
</a>
</li>
<c:forEach begin="0" end="${page.totalPage-1}" varStatus="status">
<c:if test="${status.count*page.count-page.start<=20 && status.count*page.count-page.start>=-10}">
<li <c:if test="${status.index*page.count==page.start}">class="disabled"</c:if>>
<a
href="?page.start=${status.index*page.count}${page.param}"
<c:if test="${status.index*page.count==page.start}">class="current"</c:if>
>${status.count}</a>
</li>
</c:if>
</c:forEach>
<li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
<a href="?page.start=${page.start+page.count}${page.param}" aria-label="Next">
<span aria-hidden="true">›</span>
</a>
</li>
<li <c:if test="${!page.hasNext}">class="disabled"</c:if>>
<a href="?page.start=${page.last}${page.param}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
页脚页
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isELIgnored="false"%>
<div class="footer">
</div>
</body>
</html>
再在其中根据需要插入业务页面即可,业务页面也可以采取相同的思路
示例展示
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@include file="../include/admin/adminHeader.jsp"%>
<%@include file="../include/admin/adminNavigator.jsp"%>
<script>
$(function(){
$("#addForm").submit(function(){
if(!checkEmpty("name","分类名称"))
return false;
if(!checkEmpty("categoryPic","分类图片"))
return false;
return true;
});
});
</script>
<title>分类管理</title>
<div class="workingArea">
<h1 class="label label-info" >分类管理</h1>
<br>
<br>
<div class="listDataTableDiv">
<table class="table table-striped table-bordered table-hover table-condensed">
<thead>
<tr class="success">
<th>ID</th>
<th>图片</th>
<th>分类名称</th>
<!-- <th>属性管理</th> -->
<!-- <th>产品管理</th> -->
<th>编辑</th>
<th>删除</th>
</tr>
</thead>
<tbody>
<c:forEach items="${thecs}" var="c">
<tr>
<td>${c.id}</td>
<td><img height="40px" src="img/category/${c.id}.jpg"></td>
<td>${c.name}</td>
<%-- <td><a href="admin_property_list?cid=${c.id}"><span class="glyphicon glyphicon-th-list"></span></a></td> --%>
<%-- <td><a href="admin_product_list?cid=${c.id}"><span class="glyphicon glyphicon-shopping-cart"></span></a></td> --%>
<td><a href="admin_category_edit?id=${c.id}"><span class="glyphicon glyphicon-edit"></span></a></td>
<td><a deleteLink="true" href="admin_category_delete?id=${c.id}"><span class=" glyphicon glyphicon-trash"></span></a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div class="pageDiv">
<%@include file="../include/admin/adminPage.jsp" %>
</div>
<div class="panel panel-warning addDiv">
<div class="panel-heading">新增分类</div>
<div class="panel-body">
<form method="post" id="addForm" action="admin_category_add" enctype="multipart/form-data">
<table class="addTable">
<tr>
<td>分类名称</td>
<td><input id="name" name="name" type="text" class="form-control"></td>
</tr>
<tr>
<td>分类圖片</td>
<td>
<input id="categoryPic" accept="image/*" type="file" name="filepath" />
</td>
</tr>
<tr class="submitTR">
<td colspan="2" align="center">
<button type="submit" class="btn btn-success">提 交</button>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<%@include file="../include/admin/adminFooter.jsp"%>
以上就是暂时的一个小的总结,具体可以看Project_Tmall内容