简介

作为这篇博客花费了些许时间练习和整理,最终能掌握,也算上没有白白费时间花在这个项目上面。很庆幸自己坚持把这个项目搞定,虽然这个项目对于实际毫无意义,但对于学习阶段是算作对我的提升吧

关于发布博客我会写出项目历程,第二篇会提炼出重要的控制层部分。总体都是为了巩固和复习使用

还有最后马上要过年了,在此祝大家新年快乐

 

 

开发前奏

回顾
	Dao设计模式:泛型反射
	注解反射@Rentation@Taget
	Servlet3.0新特性
		注解支持/part文件上传/web片段/Cookie的httpOnly属性/异步支持

网上书店案例:
	用前面所学的知识
	邮件的发送
	在线支付

思维导图
网上书店
	分类管理
		1. 添加分类
		2. 查询分类
	图书管理
		3. 添加图书
		4. 查询图书
	订单管理
		11. 待处理订单
		12. 已处理订单
		
#前台页面
网上书店
	5. 网站显示
	6. 购买商品
	7. 邮件的发送
	8. 用户注册和登录
	9. 生成订单
	10. 在线支付
	
开发步骤
表结构netstore.sql
create database netstore;
use netstore;
# 分类表
create table category(
	id varchar(100) primary key,
	name varchar(100) not null unique,
	description varchar(255)
);
# 书籍表
create table book(
	id varchar(100) primary key,
	name varchar(100) not null unique,
	author varchar(100),
	price float(8,2),
	path varchar(100),
	photoFileName varchar(100),
	description varchar(255),
	categoryId varchar(100),
	constraint categoryId_fk foreign key(categoryId) references category(id)
);
# 客户表
create table customer(
	id varchar(100) primary key,
	username varchar(100) not null unique,
	password varchar(100) not null,
	phone varchar(100) not null,
	address varchar(100) not null,
	email varchar(100) not null,
	code varchar(100) unique,
	actived bit(1)
)
# 订单表
create table orders(
	ordersNum varchar(100) primary key,
	totalNum int,
	totalAmount float(10,2),
	status int,
	customerId varchar(100),
	constraint customerId_fk foreign key(customerId) references Customer(id)
);
# 订单项
create table lineItem(
	id varchar(100) primary key,
	num int,
	amount float(8,2),
	bookId varchar(100), 
	ordersNum varchar(100),
	constraint bookId_fk foreign key(bookId) references book(id),
	constraint ordersNum_fk foreign key(ordersNum) references orders(OrdersNum)
);
 

网站后台

一、 管理页面目录
1. index.jsp
	静态包含(head.jsp)<h1>欢迎使用</h1>
2. css目录
	main.css-->font-size/text-align/table/.odd/.even/span{25px}
3. head.jsp<h1>后台管理</h1><a href="">添加分类/查询分类/添加图书/查询图书/待处理订单/已处理订单
	/引入css样式
	
二、 添加分类
	1. Javabean类Category,建表sql
		private String id;
		private String name;
		private String description;
	2. 	BusinessService接口
		//添加分类
		void addCategory(Category c);
		查询所有分类
		List<Category> findAllCategories();
	3. BusinessServiceImpl实现接口
		1). private CategoryDao categoryDao = new CategoryDaoMysqlImpl();
		
		2). 工具类IdGenertor
			generateGUID();
		3). 实现addCategory()方法
			设置Id使用工具类IdGenertor
		4). 实现findAllCategories()方法	
	4. CategoryDao接口
		void save(Category c);
		List<Category> findAll();
	5. 实现CategoryDao接口CategoryDaoMysqlImpl
		拷贝jar包/DBCPUtil数据源/DBUtils工具类
		1). 实现save方法()
			qr.update(insert into category(id,name,description) values(?,?,?),,,);
		2). 实现findAll()方法
			qr.query("select * from category", new BeanListHandler<Category>(Category.class));
	6. 单元测试
		测试BusinessServiceImplTest

	7. 	建立addCategroy.jsp(异步交互)
		7.0  添加分类
			index.jsp查询分类超链接到addCategroy.jsp
		7.1 页面设计
			<table>分类名称/分类描述/保存按钮(onclick)</table>
			<span id="msg"></span>
		7.2 异步交互
			1). 导入util.js获取XmlHttpRequest引擎
			2). 页面加载window.onload
			3). 按钮点击事件onclick
			4). 获取引擎对象
			5). 绑定状态响应事件
				对服务器响应进行处理
			6). 建立连接ManagerServlet?op=addCategory
			7). xhr.setRequestHeader("Context-Type","application/x-www-form-urlencoded");
			8). 发送name+description
			
	8. 建立web层Servlet(ManagerServlet)
		1). 获取请求参数op
		2). 判断请求参数是否为addCategory
		3). 建立addCategory方法
		4). WebUtil工具类,fillBean方法(导入BeanUtils包)
		5). 使用表单提交数据封装一个Category对象
		6). service.addCategory();(这里产生异常,对异常进行处理),将提示信息写出去,交给AJax引擎处理
		
	9. 建立过滤器类,实现全站编码SetAllCharacterEncodingFilter实现Filter类
		重写doFilter方法
		1). 强转request/response
		2). 得到初始化参数encoding
		3). 判断encoding为空设置默认encoding
		4). 设置request.setCharacterEncoding();
		5). response编码
		6). 建立request包装类
			对getParameter方法进行装饰
			super.获取请求参数
			判断为空直接返回空
			判断方法是否get,如果为get编码再解码
		7). web.xml配置文件filter
		

三、 查询分类
	1. head.jsp 查询分类超链接到ManagerServlet?op=showAllCategories
	2. MangerServlet中判断请求参数是否为showAllCategories
	3. 建立showAllCategory方法
	4. 获取所有分类service.findAllCategories;
	5. 将获取的分类设置到request域中
	6. 转发到showAllCategories.jsp页面
		1). 建立表格
		<table>
			(选择/分类名称/分类描述/操作)
			遍历request域中的分类
			(id/名称/描述/修改&删除)
			设置tr颜色
		</table>

四、添加图书
#前端设计
	1. head.jsp 查询分类超链接到ManagerServlet?op=addBook
	2. 建立addBookUI方法
	3. 获取所有分类的集合
	4. 将集合设置到request域中
	5. 转发到addBookUI.jsp
	6. 建立addBookUI.jsp
		表单<form method=post enctype="multipart/form-data">
			<table>书名/作者/单价/图片/描述/
			所属分类(遍历分类集合)/保存按钮/重置按钮</table>
		</form>	
#后台处理
	1. JavaBean类Book,在数据库中建立book类表
		private String id;
		private String name;
		private String author;
		private float price;
		private String path;//存放图片的路径
		private String photoFileName;//图片名称(GUID.jpg)
		private String description;
		private String categoryId;
	2. BussinessService类中添加方法
		void addBook(Book book);//添加图书
	3. 	BussinessServiceImpl实现类
		1). private BookDao dao = new BookDaoMySqlImpl();
		2). 实现addBook方法
	4. BookDao接口添加save方法
	5. BookDaoImpl实现save方法
		1). DBUtils工具类使用QueryRunner对象
		2). save方法
			qr.update("insert into book values(?,?,?,?,?,?)")
	6. addBookUI.jsp
		action提交给ManagerServlet?op=addBook
	7. ManagerServlet类中判断请求参数是否addBook,
	8. 并建立addBook方法
		1). 判断是否multipart
		2). 不是就提示ectype取值不正确
		3). 建立FileIterfactory工厂
		4). 建立ServletFIleUpload对象
		5). 得到请求对象,并请求的对象转换成集合
		6). 建立book对象
		7). 遍历集合,判断普通表单字段和上传字段
			*如果是普通表单字段,获取字段名getFieldName(),获取值getString(request.getCharacterEncoding());
				通过BeanUtils工具类设置book中
			**如果是上传字段
				获取文件名,getName();,如果文件名不为空获取扩展名+前缀加上GUID+"\."
				将文件名设置到book中
				计算存储路径getServletContext().getRealPath();
				hashcode生成子目录,在将目录设置到book中
			***处理文件上传
		8). 保存book,service.add(book)
		9). 提示保存成功提示信息设置到request域中,转发到addBookUI.jsp,不能直接转发,要先设置一下所属分类中的集合

五、 查询图书		
# 书籍分页处理
	1. 建立Page
		private List records;//要显示的记录
		private int pageSize = 3;//每页显示的记录条数
		private int startIndex; //每页开始记录的索引
		private int totalPage; //总页数
		private int pageNum;	//当前页码
		private int totalRecords; //总记录条数
		private int prePageNum; //前一页
		private int nextPageNum;//后一页
		private String url;
		
		Page(int pageNum, int totalRecords) {
			this.pageNum = pageNum;
			this.totalRecords = totalRecords;
			
			startIndex = (pageNum-1)*pageSize;
			totalPage = (totalRecords%pageSize==0)?totalRecords/pageSize:totalRecords/pageSize+1;
		}
		
		(pageNum-1>0)?pageNum-1:1;
		(pageNum+1<totalPage)?pageNum+1:totalPage;
		
	2. BusinessService接口改造
		根据用户查看的页码查询分页信息Page
		Page findBookPageRecords(String num);
	3. BusinessServiceImpl实现类改造
		实现findBookPageRecor	ds(String num)方法
		如果num不等于空转换成int类型赋值给PageNum
		totalRecords = bookDao.getTotalRecords();
		List<Book> records = bookDao.getTotalRecords(startIndex,size);
		page.setRecordes(records);
		return page;
	4. 改造BookDao接口
		int getTotalRecords();
		添加getPageRecords(startIndex,pageSize)方法
	5. 改造BookDaoImpl实现类
		int getTotalRecords();
		List<Book> getPageRecords(startIndex,pageSize)
		qr.query(); limit
	
# 显示图书页面
	1. head.jsp中查询图书超链接到ManagerServlet?op=showAllBooks
	2. 改造ManagerServlet类,判断请求参数是否为showAllBooks,并创建showAllBooks()方法
	3. 查询分页后的书籍信息showAllBooks()方法
	4. 获取请求参数的页码
	5. 获取Page对象,service.findBookPageRecords(请求参数页码);
	6. page对象设置url(ManagerServlet?op=showAllBooks)
	7. 将page对象设置到request域中
	8. 转发到listBooks.jsp页面中
	
	(显示所有书籍列表)改造listBooks.jsp页面
	<table>
		选择/书名/作者/单价/描述/所属分类/图片/操作
		遍历page对象中的records
		*img src="${pageContext.request.contextPath}/files${b.path}/${b.filename}"
		<tr nowrap="nowrap">不换行
	</table>
	
# 显示图书加入分页
1. 改造listBooks.jsp	
	静态包含引入/commons/page.jsp
2. 将分页放到一个commons文件夹中			
第${page.pageNum}页  共 页
	上一页${pageContext.request.contextPath}${page.url}&num${page.prePageNum}/下一页
3. 改造所属分类显示名称
	1). 自定义标签类实现
	<%@taglib uri="http://www.itheima.com/jsp/myfunctions" prefix="myfn" %>
	${myfn:getCategoryName(b.categoryId)}
	2). 定义MyFunctions类
		public static String getCategoryName(categoryid) {
			Category c = service.findCategoryById(categoryid);
			return c.getName();
		}
	3). 改造Businessservice接口,根据主键查询findCategoryById(categoryid);方法
	4). 改造BusinessserviceImpl实现类
		实现根据主键查询findCategoryById(categoryid);
	5). 改造CategoryDao接口添加find(categoryid)方法
	6). 改造实现类CategoryDaoImpl的方法返回一个Category对象
	7). WEB-INF新建myfn.tld文件,在tomcat/webapps/examples目录下搜索*.tld、jsp2-example-taglib.tld
		拷贝其中的标签taglib/short-name/uri/function(name/class/function-singnature)
 

网站前台

一、 网站显示	
1. index.jsp--
	<jsp:forward page="/servlet/ClientServlet">
		<jsp:param value="showIndex" name="op"/>
	</jsp:forward>
2. 新建一个ClientServlet
	1). 得到请求参数
	2). 判断请求参数是否showIndex
	3). 建立showIndex方法
	4). 查询所有分类,service.findAllCategories();
	5). 将分类设置到request域中
	6). 得到请求页码
	7). 通过页码查找得到分页对象
	8). page对象中设置url
	9). 将page对象设置到request域中
	10). 转发到listBooks.jsp
3. 	前台listBooks.jsp
	1). 引入main.css样式
	2). <h1>欢迎光临本小店</h1>标题
	3). <a href="">首页<${pageContext.request.contextPath}
		..注册..登录..我的订单..购物车..
		所有分类<>${pageContext.request.contextPath}
		遍历分类 ${cs.name};
		<a href="${pageContext.request.contextPath}/servlet/ClientServlet?op=listBookByCategory&categoryId=${c.id}"
	4). 商品展示
		<table width="438">
			遍历page对象中的records
			<c:foreach>
			<td><img src="${pageContext.request.contextPath}/files/${b.filePath}/${b.filename}"></td>
			书名/作者/定价<strike>/跳楼价
			<链接>购买
			</c:foreach>
		</table>
	5). 静态包含page.jsp
	
	#后台处理
	6). 整理分类,改造ClientServlet
	7). 判断请求参数是否是listBookByCategory
	8). 按照分类查询所有书籍:建立listBookByCategory方法
	9). 查询所有分类,将分类集合设置到request域中
	10). 按照分类查询所有书籍(分页)
		获取请求的页码,获取请求的categoryId
		添加方法findBookPageRecords(String num, String categoryId);
		相应的添加实现类方法,完成此方法操作
		根据categoryid查询出总共有多少条
		再加上categoryid查询页的记录
	12). 根据页码和id获取Page对象
	13). 设置设置page中的url.../servlet/ClientServlet?op=listBookByCategory&categoryId="+categoryId
	14). 转发到listBooks.jsp
	
二、 购物车
# 设计购物车
1. listBooks.jsp中<${pageContext.request.contextPath}/servlet/ClientServlet?op=buyBook&bookId>购买
2. 建立购物项CartItem
	private Book book;
	private int quantity;	//数量小计
	private float amount;	//金额小计
	public CartItem(Book book) {
		this.book = book;
	}
	amount = book.getPrice()*quantity();
3. Cart购物车类
	//key : value中购物项对应书籍的ID
	private Map<String, CartItem> items = new HashMap<String, CartItem>();
	private int totalQuantity;//总数量
	private float totalAmount;//总金额
	
	总数量和总金额首先赋初始值0
	1). 总数量:遍历map集合,获取每个购物项的小计数量,相加得到总数量
	2). 总金额:遍历map集合,获取每个购物项中的小计金额,相加得到总金额
	3). 添加书籍:如果购物车中包含了这个购物项,得到该购物项修改数量原来数量+1;
	否则不包含的话,设置数量为1,将书籍添加到购物车中
	
# 购买书籍		
	1). 改造ClientServlet类
	2). 判断请求参数是否是buyBook,并创建buyBook方法
	3). buyBook方法,把商品加入购物车中
	4). 获取请求的bookId
	5). 调用业务逻辑层,得到book对象service.findBookId(bookId)
		接口中添加此方法,根据主键查找该书籍
	6). 获得购物车 request.getSession(),在session域中查找购物车
	7). 如果cart为空说明没有,就创建cart对象
	8). 将cart设置到session域中
	9). 将书添加到购物车中
	10). 提示信息购买成功,设置到request域中,转发到提示页面/msg.jsp
	11). 将listBooks.jsp中的上半部分提取到head.jsp中,以便其他页面调用
	12). listBooks.jsp静态包含/head.jsp中
	13). msg.jsp 静态包含/head.jsp中,取出request域中msg信息

# 显示购物车	
	1). 在head.jsp中添加购物车链接地址${pageContext.request.contextPath}/showCart.jsp
	2). showCart.jsp,静态包含/head.jsp <h1>您购买的商品如下</h1>
	3). 判断session域中购物车中的购物项是否为空,若为空,提示信息
	4). 不为空,显示购买商品
	<table width="438">
		<td nowrap="nowrap">
		<tr>全选  id="selectAll" onclick="selectAll(this)"
		/书名/单价/小计/数量/操作
		<tr>遍历购物车中的购物项
			<input type="checkbox" name="ids" value="${me.key}"/>
			书名+图片(50*50)/....
		<tr>删除选择的商品/总数量/总计/去结算
	</table>
	
# 删除购物车
	1). 显示购物车中删除超链接一个函数<a href="javascript:delOneItem('${me.key')">
	2). 定义delOneItem方法,提示删除window.confirm("确定要删除该项吗?");
	3). 提交给servlet。window.location.href=".../servlet/ClientServlet?op=delOneItem&bookId="+bookId);
	4). 改造ClientServlet,获取请求参数是否为delOneItem,并创建该方法
	5). 获取通过session获取购物车,获取请求参数的bookid
	6). 购物项中移除该id
	7). 重定向到.../showCart.jsp中,防止刷新重复提交问题
	
# 全选功能
	1). 定义一个方法selectAll(obj)
	2). 获取每个单选框对象,进行遍历
	3). 设置checked和全选的checked一致
	4). 定义删除选中的商品delMultiItems()方法
		首先设置一个变量selected = false
		获取每隔单选框对象,如果该checked为true,selectted为true,break
		if(!selected)提示删除前请选择
		var sure = window.confirm(确定要删除所选记录吗);
		window.forms[0].submit();
		**将表格加入一个表单,action=".../servlet/ClientServlet?op=delMultiItem" method="post"
	5). 改造ClientServlet,判断请求参数是否为delMultiItem,并创建该方法
	6). 获取请求参数ids数组
	7). 判断ids不等于空和长度大于0,条件成立,获取session对象
	8). 通过session查找购物车
	9). 遍历ids数组,通过购物项移除每隔id
		
# 修改购物项数量
	1). 数量使用<input type="text" id="quantity" name="quantity" size="3" 
	value="${me.value.quantity}" onchange="changeNum(this,${me.value.quantity},${me.key})">
	2). 定义函数 changeNum(quantityInputObj,oldQuantity,bookid)
	3). var newQuantity = quantityInputObj.value;
	4). js验证:if(!/^[1-9][0-9]*$/.test(newQuantity)) 提示输入正确数量return
	5). window.confirm(确定修改数量吗)
	6). 确认提交,将地址.../...?op=changeItemNum&num="+newQuantity+"&bookid"+bookid
	7). 取消是。将value赋值为oldQuantity
	8). 修改购物项的数量,改造ClientServlet,判断请求参数是否为changeItemNum,并创建该方法
	9). 获取bookId和数量
	10). 服务器端进行验证,将获取的数量转换成int类型
	11). session域中获取购物车,通过bookid获取购物项
	12). 设置购物项的数量
	13). 重定向到购物车页面
	
# 修改购物车数量2(把输入框修改为图标形式)
	数量前添加<img src="...../minus.jpg" onclick="minus()">
	数量后添加<img src="...../adding.jpg" onclick="adding()">
	1). minus(bookid)方法,获取id = quantity的值num
	2). 如果(--num)小于1,提示信息;return;
	3). 否则地址就提交到.../...?op=changeItemNum&num="+num+"&bookid"+bookid
	4). adding(bookid)方法一样
	
	方式2:this.nextSibling.nextSibling.getAttribute("value");
			this.previousSibling.previousSibling
			
三、 用户注册
# JavaBean开发
	1. 建立Customer类,在数据库中建立相应的表
		private String id;
		private String username;
		private String password;
		private String phone;
		private String address;
		private String email;
		private String code;	//激活码,唯一
		private boolean actived;
		
	2. 改造BusinessService接口
		//客户注册
		void customerRegist(Customer c);
		//客户登录,如果没有激活,也不能登录
		Customer customerLogin(String username,String password);
		//根据用户名和激活码激活账户
		void activeCustomer(String username,String code);
	3. 建立BusinessServiceImpl实现类
		customerRegist(Customer c)设置IdGenertor
		customerLogin()两种情况,查不到/没激活
		activeCustomer() 设置激活状态/更新
		
	4 建立CustomerDao接口/CustomerDaoImpl实现类	
		save(Customer c)
		findCustomer(String username, String password)
		findCustomerByUsernameCode(String username, String code)
		update(Customer c);	
		
# 前台页面
	1. head.jsp
		判断sessionScoper.customer若为空,注册链接/登录
		不为空,欢迎提示信息,提供注销链接
	2. 注册链接<a href="${pageContext.request.contextPaht}/regist.jsp">
	3. regist.jsp
		<form action="...../servlet/ClientServlet?op=regist" method="post">
			<table borde=1 width=438>
				用户名/密码/电话/住址/邮箱/注册按钮
			</table>
		</form>
	4. 改造ClientServlet
		1). 判断请求参数是否regist,并创建regist方法
		2). 封装信息 将请求参数封装到Customer中
		3). 生成唯一的激活码,c.setCode(IdGenertor.generateGGUID())
		4). 发送激活邮件: 单独启动一个线程
			SendMail sMail = new SendMail();
			sMail.start();
			1. 定义SendMail类继承Thread类接收Customer类,context路径
			2. 重新run方法()
			3. 创建Properties对象,设置setProperty,创建Session对象
			4. 创建MimeMessage对象
			5. 设置发送者/接收者/主题/设置内容(设置激活右键地址)
			6. 得到Transport,连接,发送消息到接收端
		5). 改造ClientServlet判断请求参数是否为active,并创建该方法
			获取username和code请求参数
			调用业务逻辑方法activeCustomer(username,code);
			输出提示激活成功,2秒后跳转到主页
			否则抛出异常,就输出服务器忙
			
		6). 调用业务方法进行注册
		7). 注册成功,提示信息,注册成功,已经发送到c.getEmail()中
	5. 登录操作
		1). 修改head.jsp中登录超链接
		2). login.jsp,提交到ClientServlet?op=login
			静态包含head.jsp页面
			<form><table>用户名/密码/登录
		3). 改造ClientServlet
			判断请求参数是否为login,并创建login方法
			1. 获取username和password
			2. 获取Customer,调用业务中方法customerLogin(username,password)方法,判断是否登录成功
			3. 如果登录不成功,提示错误密码用户名或者未激活,2秒跳转登录页面
			4. 将customer设置到session域中(可以定义一个类,属性为LOGIN_FLAG)
			5. 提示登录成功,2秒后跳到主页
	6. 注销操作,提交到ClientServlet?op=logout
		改造ClientServlet
		判断请求参数是否为logout,并创建logout方法
		移除session域中的NetStoreStatic.LOGIN_FLAG
		提示注销成功,跳转到主页
 

在线支付环节

一、 数据库表设计
	Category
		ID(PK)
		NAME
		DESCIPTION
	Book
		ID(PK)
		NAME
		AUTHOR
		PRICE
		CATEGORY_ID(FK)
	一对多
	---------------------
	Customer
		ID(PK)
		USERNAME
		...
	Orders
		ORDERSNUM(PK)	订单号
		TOTALNUM		总数量
		TOTALAMOUNT
		CUSTOMER_ID(FK)
		
	---------------------
		
		
二、 购物车结算生成订单和明细	
#JavaBean开发
	Orders订单
	private String ordersNum;
	private int totalNum;
	private floatAmount;
	
	private int status;//订单状态 0 表示未付款  1表示已付款 2表示已发货
	//多个订单可能对应同一个客户:多对一
	private Customer customer;
	//订单对应订单项
	private List<LineItem> items = new ArrayList<LineItem>();
	
	LineItem订单项类
	private String id;
	private int num;
	private float amount;

	//private Orders o; 实际业务中很少通过订单项找属于哪个订单
	private Book book;	//Many 2 one;
	
	Customer类中添加
		List<Orders> os = new ArrayList<Orders>(); //客户的订单
	
	
# ClientServlet改造	
	1. showCart.jsp中去结算生成订单?op=getOrders
	2. 判断用户是否登录,在session中查找Customer得到用户
	3. 如果为空,重定向到login.jsp
	4. 在session中查找购物车,把购物车的内容设置到Orders中
	5. 生成订单编号, generateOrdersNum(); yyyyMMdd str+System.nanoTime()
	6. 将客户设置到订单中、设置总数量、设置总金额
	7. 取出购物项,遍历购物车中的购物项
	8. 创建LineItem对象,将遍历的购物项设置到LineItem对象中
	9. 设置id/Num/amount/book/最后将订单项添加到orders中
	10. 调用业务方法生成订单genOrders(orders)
		定义OrdersDao接口save方法,实现该接口
		实现类OrdersDaoImpl,将订单保存到Orders表中,遍历订单项保存到LineItem表中
	11. 去支付,将Orders设置到request域中
	12. 转发到pay.jsp中
	
# 我的订单
	1. head.jsp中我的订单添加链接?op=showOrders(查询用户登录的订单)
	2. 判断用户是否登录,从session中取出customer
	3. 如果为空跳转到登录页,return返回
	4. 调用业务方法通过客户id查询所有订单 s.findOrdersByCustomerId
		通过指定客户的订单,按时间倒序ordersNum
	5. 将所有订单os设置到request域中
	6. 转发到showCustomerOrders.jsp
	
	# 7. showCustomerOrders.jsp
	8. 判断request域中的os是否为空,若为空提示信息
	9. 不为空显示近期订单如下:
		<table> 订单号、订单金额、订单状态、明细、操作
		遍历os(*订单状态使用c:chosse c:when)
		订单状态为未付款时,链接到去付款payUI&ordersNum=${o.ordersNum}
	10. 判断请求参数payUI, 判断用户是否登录,在session域中查找用户,如果未登录重定向到login.jsp中
	11. 获取请求参数ordersNum,根据订单号查询Orders订单findOrdersByOrdersNum(ordersNum)
	12. 将设置到request域中,转发到pay.jsp中
	
	
# pay.jsp
	参数:订单号 value="${o.ordersNum}"	/	金额 value="0.01"
	1. action="ClientServlet?op=pay", 发送支付请求
	2. ClientServlet判断支付请求是否pay
	3. 获取支付请求,orderid/money/pd_FrpId
		组织数据(orderid/money/p0_Cmd/p1_MerId/p2_Order/p3Amt/p4_Cur/p5_Pid/p6_Pcat/p7_Pdesc/p8_url/p9_SAF/pa_MP/pr_NeedResponse/hm)
	4. 设置request域中的组织数据值
	5. 转发到/sure.jsp页面中
	6. sure.jsp中<form>提交到易宝提供支付的地址,设置隐藏域获取value值
	7. 访问p8_url,支付完毕后,第三方引导客户访问的地址PaymentResponse
	8. 获取p1_MerId/r0_cmd/r1_Code....
	9. 校验数据是否正确 PaymentUtil.verifyCallback();
	10 判断返回校验结果,如果不正确,提示信息
	11. 如果校验正确,在判断"1".equals(r1_Code)和2.equals(r9_BType),输出信息
		通过订单号查找该订单r6_Order,设置状态,调用业务逻辑层更新订单信息,输出信息提示成功
		(*注意不用更新customerId,因为要看Orders有没有设置Customer)
 

后台管理权限之表结构设计

User
	ID(PK)
	USERNAME
	PASSWORD
	...
ROLE
	ID(PK)
	NAME
	DESCRIPTION
	...
User_Role
	U_ID(FK)
	R_ID(FK)
	
Menu
	ID(PK)
	NAME	添加分类
	URI		/manage/addCategory.jsp
	
Privileage
	R_ID(FK)
	M_ID(FK)
		
# 后台权限表结构
# 用户表
create table user(
	id int primary key,
	username varchar(100),
	password varchar(100),
);
insert into user values("admin",123);
insert into user values("xiaobai",123);

# 权限表
create table role(
	id int primary key,
	name varchar(100),
	description varchar(255)
);
insert into role values(1, "超级管理员", "什么都能干");
insert into role values(2, "图书管理员", "只能维护书籍");

# 关联表
create table user_role(
	u_id int,
	r_id int,
	primary key(u_id,r_id),
	constraint user_id_fk foreign key(u_id) references user(id),
	constraint role_id_fk foreign key(r_id) references role(id)
);
insert into user_role values(1,1);
insert into user_role values(2,2);

# 菜单权限
create table menu(
id int primary key,
name varchar(100),
uri varchar(255)
);
INSERT INTO `menu` VALUES (1,'添加分类','/manager/addCategory.jsp'),
(2,'查询分类','/servlet/ManageServlet?op=showAllCategory'),
(3,'添加图书','/servlet/ManageServlet?op=addBookUI'),
(4,'查询图书','/servlet/ManageServlet?op=showAllBooks'),(5,'待处理订单',NULL),
(6,'已处理订单',NULL),(7,'主页','/manager/index.jsp');

create table privileage(
	m_id int,
	r_id int,
	primary key(m_id,r_id),
	constraint menu_id_fk foreign key(m_id) references menu(id),
	constraint role_id_fk1 foreign key(r_id) references role(id)
);
insert into privileage values(1,1);
insert into privileage values(2,1);
insert into privileage values(3,1);
insert into privileage values(4,1);
insert into privileage values(5,1);
insert into privileage values(6,1);

insert into privileage values(3,2);
insert into privileage values(4,2);
insert into privileage values(7,2);

JavaBean
	# User类
	private int id;
	private String username;
	private String password;
	
	private Set<Role> roles = new HashSet<Role>();
	
	# Role类
	private int id;
	private String name;
	private String description;
	
	private Set<Menu> menus
	重写hashcode和equals方法
	
	# Menu类
	private int id;
	private String name;
	private String uri;

 

后台管理权限之过滤器控制(基于URI)

# 后台登录MangerServlet类改造
	1. head.jsp提供管理员入口链接/login/login.jsp
	2. login.jsp提交到ManagerServlet?op="login"
	3. login后台登录方法,获取用户名和密码
	4. 调用后台登录验证,得到user对象
	5. 如果user不等于空,将user设置到session域中,重定向到/manager/index.jsp
	6. 否则提示信息,错误的用户名或密码
	
# 后台页面改造head.jsp
	1. 	提示信息
	
# 使用过滤器实现过滤权限
	PrivileageFilter
	1. 配置web.xml文件
		<filter-mapping>/manager/ *
		<filter-mapping>/servlet/ManagerServlet
	2. 判断用户是否登录
		获取session域中的user
		没有登录,转向登录界面/login/login.jsp
	3. 用户对应的菜单:Set<Menu>,
		1). 创建一个存储用户对应菜单的Set集合Set<Menu>
		2). 调用业务方法查询用户的权限角色Set<Role>,然后遍历该集合,获取每一个角色,findUserRoles(user)
		3). 再通过查询每个角色业务方法,获取菜单信息一个集合	findMenuRole(role)
		4). 将菜单集合添加到用户对应的菜单中
	4. 截取用户访问的URI,看看在不在Set集合中,在就放行,不再就不放行
		1). 获取请求的URI,替换
		2). 定义一个变量记录用户访问的uri是否存在
		3). 遍历每个菜单的uri。是否和截取的uri相等,如果相等,修改标记为ture,break;
		4). 如果存在放行,不存在,就提示信息
		
	getQueryString()!= null;