JavaWeb网上商城

web项目

1.关于web项目目的:
	将web阶段所有学过的知识点复习总结.
	
2.关于web项目功能:
		功能: 
		1、用户注册
		2、用户登录
		3、添加商品(上传)
		4、商品查看-- 列表查询
		5、商品详情页面
		6、将商品添加购物车
		7、查看购物车
		8、修改购物车
		9、生成订单
		10、订单查看(取消)
		11、在线支付
		12、销售榜单查看
		
		会对项目进行重构.
		会使用注解+动态代理实现细粒度权限控制.
		添加关于ajax操作
			对于订单操作时,使用ajax
		在线支付功能	

3.系统分析
	1.通过UML用例图来确定当前用户以及所具有的功能
		游客(未登录): 注册、登陆、商品查看 
		商城注册用户 : 商品查看、添加商品到购物车、购物车管理、生成订单、订单管理、在线支付 
		管理员 : 添加商品、商品管理、查看订单 、榜单查看(导出)

	2.系统设计
		1.技术选型
			JSTL + JSP + Servlet + JavaBean + BeanUtils + FileUpload + JavaMail + DBUtils(JDBC) + C3P0 +  MySQL + MyEclipse10+ Tomcat7.0 + JDK6  + Windows 
			MVC 模式
			JavaEE 三层结构
			DAO 模式 
			
		2.数据库设计
			E-R图  实体关系图.			
			对于我们当前项目有这些实体  用户  商品  购物车  订单 
			
			通过E-R图可以分析出我们数据库中表与表之间的关系,以及每一个表中的属性。
			
			create table users (
			   id int primary key auto_increment,
			   username varchar(40),
			   password varchar(100),
			   nickname varchar(40),
			   email varchar(100),
			   role varchar(100) ,
			   state int , 
			   activecode varchar(100),
			   updatetime timestamp  );
			商品表
			create table products(
			   id varchar(100) primary key , 
			   name varchar(40), 
			   price double,
			   category varchar(40),
			   pnum int ,
			   imgurl varchar(100),
			   description varchar(255));
			订单表
			create table orders(
			   id varchar(100) primary key,
			   money double,
			   receiverinfo varchar(255),
			   paystate int, 
			   ordertime timestamp,
			   user_id int , 
			   foreign key(user_id) references users(id)
			);

			用户与订单之间存在 一对多关系 : 在多方添加一方主键作为外键 
			订单和商品之间存在 多对多关系 : 创建第三张关系表,引入两张表主键作为外键 (联合主键)
			订单项
			create table orderitem(
			   order_id varchar(100),  
			   product_id varchar(100),
			   buynum int , 
			   primary key(order_id,product_id), 
			   foreign key(order_id) references orders(id), 
			   foreign key(product_id) references products(id)
			);

			设置数据库环境
			数据库 :create database estoresystem;
			
--------------------------------------------------------------------		
4.环境搭建	
	1.导入jar包
		导入mysql驱动  mysql driver / mysql-connector-java-5.0.8-bin.jar 
		导入c3p0  c3p0/c3p0-0.9.1.2.jar  将c3p0-config.xml 复制src下  将DataSourceUtils复制 cn.itcast.estore.utils  ----- 配置c3p0-config.xml数据库连接参数
		导入dbutils apache commons\dbutils\commons-dbutils-1.4.jar
		导入beanutils commons-beanutils-1.8.3.jar commons-logging-1.1.1.jar 
		导入fileupload commons-fileupload-1.2.1.jar commons-io-1.4.jar
		导入javamail mail.jar
		导入jstl jstl.jar standard.jar
	2.创建包结构
		cn.itcast.estore.web.servlet
		cn.itcast.estore.web.filter
		cn.itcast.estore.web.listener
		cn.itcast.estore.service
		cn.itcast.estore.dao
		cn.itcast.estore.domain
		cn.itcast.estore.utils

	3.创建domain
		UML中类图画法 
		
	4.工程发布
		将estore项目配置虚拟主机,以顶级域名方式进行发布 
		在浏览器上直接输入www.estore.com就可以访问到我们的工程.
		
		1.在tomcat的conf目录下的server.xml文件中配置
			1.修改tomcat的端口 80.
			2.配置虚拟主机
				
				
					
						   
					

				  
				  
			3.在C:\Windows\System32\drivers\etc\hosts文件中配置
				127.0.0.1  www.estore.com
			
			注意:在启动tomcat时,不需要将工程estore工程部署到tomcat.
			
=========================================================================================
功能实现:
	1.注册操作
		1.一次性验证码
		2.表单的js校验(非空校验)
		  服务器端的校验	
		3.全局编码过滤器
		4.密码md5加密
		5.发送激活邮件
		6.通用错误页面配置
		
	2.实现注册功能
		1.修改静态页面
			直接创建一个jsp,将page.html页面内容复制到home.jsp页面.
			在index.jsp中添加一个请求转发操作
			
			
		2.在home.jsp页面上添加一个注册的连接。
			注册	
			
		3.创建一个regist.jsp页面
			页面上有
				1.username
				2.password
				3.email
				4.nickname
				
				还需要一个repassword 确认密码
				还需要一个验证码
				
		4.在页面上产生验证码
			1.将new_words.txt复制到WEB-INF目录下.
			2.在regist.jsp页面上
				
			注意编码问题.
				
		5.完成注册的流程
			regist.jsp---->RegistServlet-----UserService   UserDao
			
			关于用户的role与state
				对于注册的用户它的role我们默认设置为"user",state默认设置为0 代表没有激活,
				对于激活码,我们在注册时,要生成。通过uuid生成.
				
			注册成功后跳转到 regist_success.jsp页面,在页面上显示3秒后跳转到首页.
				
			问题:
				1.怎样让3秒数字变化.
					通过js代码来完成
					var interval;
					window.onload = function() {
						interval = window.setInterval("fun()", 1000); //设置1秒调用一次fun函数
					};

					function fun() {
						var time = document.getElementById("s").innerHTML;

						//判断如果等于0了,不在进行调用fun函数,
						if (time == 0) {
							window.clearInterval(interval);
							return;
						}

						document.getElementById("s").innerHTML = (time - 1);
					}
				2.关于乱码处理
					使用全局的编码过滤器
					EncodingFilter完成post与get请求的编码处理.
					
		6.关于注册操作的校验问题
			1.表单校验
				就是通过js代码完成.
				1.在
2.校验,只校验非空. function checkNull(fieldName){ var value = document.getElementById(fieldName).value; var reg = /^\s*$/; //代表0个或多个空字符。 if(reg.test(value)){ document.getElementById(fieldName+"_message").innerHTML=""+fieldName+"不能为空"; return false; }else{ return true; } } 2.服务器端校验 在javaBean中做一个校验方法 public Map validation() { Map map = new HashMap(); if (username == null || username.trim().length() == 0) { map.put("regist.username.error", "用户名不能为空"); } if (password == null || password.trim().length() == 0) { map.put("regist.password.error", "密码不能为空"); } return map; } 在通过BeanUtils将请求参数封装到javaBean后,调用校验方法. 如果判断Map集合中有数据,说明存储了错误信息,就跳转到regist.jsp页面。 在页面上展示错误信息,使用jstl标签。 7.关于发送激活邮件 邮箱 [email protected] 密码 abc123 关于激活邮件发送我们在UserService中的regist方法中完成。 关于MailUtils工具使用: 1.props.setProperty("mail.host", "自己邮箱的smtp"); 2.return new PasswordAuthentication("邮箱帐户", "密码"); 3.message.setFrom(new InternetAddress("发送者邮箱")); 注意:关于发送的信息问题: String emailMsg="注册成功, 请激活, 激活码为:"+user.getActivecode(); 8.关于激活用户操作 http://www.estore.com/activeUser?activeCode=d104ed31-0a5f-4eb4-8377-085c9be5f6e4 1.创建一个ActiveUserServlet 1.得到激活码, 2.调用service中激活操作 注意:激活是有时间限制的。 3.在service中完成操作时有两件事要做: 1.根据激活码查找用户 2.判断用户激活码没有过期,进行激活操作. 9.md5加密 mysql中:UPDATE users SET PASSWORD=MD5(PASSWORD); 在UserDao的addUser方法中,对user.getPassword()使用Md5Utils工具进行加密。 10.验证码 在所有操作前,通过requst获取请求中的验证三,与session中存储的验证码进行对比。 从session中获取完成后,马上删除。 在CheckImgServlet中有一句话: request.getSession().setAttribute("checkcode_session", word); String checkCode = request.getParameter("checkcode"); String _checkCode = (String) request.getSession().getAttribute( "checkcode_session"); request.getSession().removeAttribute("checkcode_session");//从session中删除。 if (!checkCode.equals(_checkCode)) { request.setAttribute("regist.message", "验证码不正确"); request.getRequestDispatcher("/regist.jsp").forward(request, response); return; } ================================================================================================================ 2.登录操作 1.登录操作中具有的功能 1.请求信息的校验 1.客户端校验 2.服务器端校验 2.请住用户名操作 3.自动登录操作 4.注销功能 登录注意事项: 1.注意用户是否激活 2.注意密码已经进行了md5加密。 用户登录成功后,将用户存储到session中. 2.登录代码实现: 1.登录基本流程 1.在home.jsp页面上有登录窗口。 2.创建一个LoginServlet 完成登录操作 ,在LoginServlet中 1.得到用户名与密码 2.调用service完成登录操作. 3.在service中判断用户是否可以登录,以及用户是否激活 4.在home.jsp页面上,完成错误信息展示以及用户登录的提示。 ------------------------------------------------------------ 2.记住用户名操作 原理:当用户登录成功后,会判断用户是否勾选了记住用户名,如果勾选了,将用户名存储到cookie中。 下一次在访问登录页面,直接从cookie中获取用户名,显示在用户名文本框上。 问题:cookie中不能存中文? 存:Cookie cookie = new Cookie("remember", URLEncoder.encode(user.getUsername(), "utf-8")); 取: window.οnlοad=function(){//页面加载成功后跳用这个函数。 var username=document.getElementById("username"); window.de username.value=window.decodeURIComponent("${cookie.remember.value}","utf-8"); }; 关于删除cookie: 1.setMaxAge(0/-1) 0代表立即删除 -1代表关闭浏览器后才删除。 2.删除cookie时,必须与原cookie的path值一致. ----------------------------------------------------------------------- 3.自动登录 原理:用户登录成功,判断是否勾选了自动登录,如果勾选了,将用户名与密码存储到cookie中。 需要一个Filter,当用户访问工程时,在Filter中从cookie取出用户名与密码,进行登录操作。 注意事项: 1.存密码时,注意加密。 2.如果用户已经登录,不需要在登录。 3.如果访问的资源路径不需要自动登录,那么不进行自动登录。 4.第一个用户自动登录,又使用了第二个用户登录,它没有勾选自动登录, 这时,需要将自动登录的cookie删除。 ------------------------------------------------------------------------- 4.注销 在home.jsp页面有注解的连接。 注销 创建一个LogOutServlet完成注解功能 session.invalidate(); 问题:销毁session的方式: 1.关闭服务器 2.invalidate() 3.自动超时 在tomcat/conf/web.xml文件中配置超时时间 30 4.setMaxInactiveInterval(int interval) 手动设置session超时时间. 注意:如果我们有自动登录操作,那么当我们完成注销操作后,会跳转到首页, 这时,如果在cookie中存储了用户名与密码,就会进行自动登录,那么 注销的效果就看不到了,所以要看到效果,可以将自动登录的cookie删除。 问题: 1.一个用户在两个浏览器登录 要想解决,需要将数据存储到数据库中。 可以使用session共享服务器来解决. 2.两个用户在同一个浏览器登录 会出现共享session问题,简单说,第一个用户购买的物品,存储在session中。 第二个用户登录后,直接就可以看到第一个用户购买的商品。 解决方案:在每一个用户登录时,先销毁session. ============================================================================================================ 商品操作: 1.添加商品(上传操作) 1.在home.jsp页面上有一个连接 添加商品 2.创建addProduct.jsp页面 问题:页面上有什么组件? 查看products表中的数据. 文件上传时浏览器端注意: 1.method=post 2.encType="multipart/form-data" 3. 3.根据表中的数据创建Product类 private String id; // 商品编号 private String name; // 名称 private double price; // 价格 private String category; // 分类 private int pnum; // 数量 private String imgurl; // 图片路径 private String description; // 描述 4.编写AddProductServlet 完成添加商品操作----其实是文件上传. 1.DiskFileItemFactory 2.ServletFileUpload 3.FileItem 在这个servlet中要完成两件事情: 1.文件上传 问题: 1.上传文件中文名乱码 upload.setHeaderEncoding("utf-8"); 2.上传文件名称获取? item.getName(); 得到的有可能包含路径。 3.上传文件名称重复 uuid获取随机名称 4.上传文件随机目录。 通过文件名的hashCode进行计算,随机得到目录. 5.关于上传文件保存位置 对于我们这个项目,上传的商品图片,是允许浏览器直接访问的, 所以我们保存到WebRoot下的upload目录下. 2.向products表中添加数据。 1.得到所有数据封装到Product对象中. BeanUtils.populate(product,Map); 这个Map怎样得到? 手动创建一个Map 将数据手动封装. 问题:关于id怎样封装? uuid获取. 问题:关于imgurl怎样封装? map.put("imgurl", new String[]{"/upload"+uuidDir+"/"+uuidname}) 2.调用service,dao完成添加操作. 3.添加成功后,跳转到首面. --------------------------------------------------------------- 2.查看商品 1.查看全部 index.jsp---->findAllProduct------->home.jsp 1.创建一个FindAllProductServlet 在这个servlet中查询出所有商品List,将其存储到request域,在请求转发到home.jsp页面 2.在home.jsp页面展示

${p.name }

an image

价格: ¥${p.price }

速速抢购

-------------------------------------------------- 2.查看商品详细信息 它有两个入口,一个是点击速速抢购.还有点击图片也可以查看商品详细信息. 1.速速抢购 2.在图片上添加一个onclick function findProductById(id){ location.href="http://www.estore.com/findProductById?id="+id; }; 查看商品详细信息: 1.创建FindProductByIdServlet 1.得到商品id 2.根据id调用service,dao完成查询商品操作. 2.创建productInfo.jsp页面,展示商品信息 关于展示商品时, 展示商品图片,可以通过它的width与height属性来控制图片的大小。 在开发中还有另外一种处理方式:使用商品图片的缩略图。 我们可以自己编程,去获取一个上传图片的缩略图。 在展示时,直接得到它的缩略图来展示 . 在文件上传完成后,添加这两名话就会产生图片的缩略图 // 生成缩略图 PicUtils putils = new PicUtils(dest.getCanonicalPath());// 获取上传文件的绝对磁盘路径。 putils.resize(200, 200);// 就会产生一个200*200的缩略图. 使用缩略图 1.在Product类中添加一个方法 public String getImgurl_s() { int index = imgurl.lastIndexOf("."); return imgurl.substring(0, index) + "_s" + imgurl.substring(index); } 2.在productInfo.jsp页面上 ========================================================================================================== 购物车 我们使用的session来存储购物车,在数据库中没有关于购物车中商品信息。 1.添加商品到购物车 在productInfo.jsp页面有连接。 问题:怎样将商品添加到购物车,购物车我们使用什么数据结构来存储商品信息? 购物车我们使用Map 在productInfo.jsp页面上连接,它要传递商品的id function addProductToCart(id){ location.href="${pageContext.request.contextPath}/addProductToCart?id="+id; } 创建一个AddProductToCartServlet 1.根据id得到商品 2.得到购物车 3.将商品添加到购物车 注意:我们使用的购物车其实是一个HashMap,对于HashMap,它的维护主键唯一性,是使用 key值的hashCode与equals方法,也就是说,对于我们的购物车,它是使用Product的hashCode与equals方法 来保证,我们同一个商品的数量的变化. 简单说,对于我们就需要重写Product类的equals方法与hashCode方法。 -------------------------------------------------------------------------------------- 2.查看购物车商品 有两个入口 1.添加商品到购物车成功后,有提示,查看购物车 2.在首面有查看购物车 查看购物车,我们直接就是一个jsp页面上将购物车中商品展示出来就可以。 因为购物车就存储在session中. 创建一个showCart.jsp,用于展示购物车中所有商品. 购物车中商品总价怎样获取:每个商品的单价*商品数量 ----------------------------------------------------------------------------------------- 3.改变购物车中商品数量 1.加操作 点击加按钮,要访问一个servlet,在servlet中,获取购物车中商品,对其数量进行操作. function changeCount(id,count){ location.href="${pageContext.request.contextPath}/changeCount?id="+id+"&count="+count; } 在服务器端: Product p=new Product(); p.setId(id); cart.put(p, count); 问题:怎样控制边界? 如果数量减到0,相当于将商品从购物车中删除。 如果数量加到比商品库存还大,就让它等于最大值. 在js代码中控制 //控制边界 if (count <= 0) { //删除 var flag = window.confirm("要删除商品吗?"); if (flag) { count = 0; } else { count = 1; } } else if (count >= pnum) { alert("最大购物数量"+pnum); count = pnum; } 注意:如果购物数量为0,这时会以服务器端将商品从购物车中删除。 关于文本框中数量修改: 对于文本框,可以添加一个onblur事件 注意:在js中数据是无类型的,操作时,有的进修传参数,想要传递的是一个数值类型, 但是,js将其做为字符串处理了,就需要使用parseInt() parseFloat()来转换成数值类型。 数字文本框: 在这个文本框中只能输入数字,不能输入其它的字符。 原理:给文本框添加onkeydown事件,当触发事件时,获取按下的键的键码值,如果它的键码是数字0-9之间的就可以执行, 如果不是,阻止事件的默认行为 . 1.问题:怎样获取按下的键码值 2.问题:怎样阻止事件的默认行为 function a(e){ var keyCode; if(e&&e.preventDefault){ //判断是firefox浏览器 keyCode=e.which; }else{ //ie浏览器 keyCode=window.event.keyCode; } //alert(keyCode); //0-9之间的键码值是48-57 if(!(keyCode>=48&&keyCode<=57||keyCode==8)){ //阻止事件的默认行为 if(e&&e.preventDefault){ // e对象存在,preventDefault方法存在 ---- 火狐浏览器 e.preventDefault(); }else{ // 不支持e对象,或者没有preventDefault方法 ---- IE window.event.returnValue = false; } } }; --------------------------------------------------------------------------- 从购物车中删除商品 1.连接提交时,将商品id携带到服务器 删除 2.创建RemoveProductFromCartServlet 将要删除的商品从购物车中删除 // 得到要删除的商品的id String id = request.getParameter("id"); // 得到购物车,从购物车中将商品删除, Map cart = (Map) request .getSession().getAttribute("cart"); Product p = new Product(); p.setId(id); cart.remove(p); //如果购物车中无商品,将购物车删除。 if (cart.size() == 0) { request.getSession().removeAttribute("cart"); } 关于删除商品时的提示处理: 方式1: 删除 function removeProduct(id) { var flag = window.confirm("要删除商品码?"); if(flag){ //要删除 location.href="${pageContext.request.contextPath}/removeProductFromCart?id="+id; } } 方式2:通过阻止事件的默认行为来控制 删除 function deleteProduct(e) { var flag = window.confirm("要删除商品码?"); if (!flag) { //不删除,阻止连接的默认行为 执行。 //阻止事件的默认行为 if (e && e.preventDefault) { // e对象存在,preventDefault方法存在 ---- 火狐浏览器 e.preventDefault(); } else { // 不支持e对象,或者没有preventDefault方法 ---- IE window.event.returnValue = false; } } }; ============================================================================================================ 订单操作 1.生成订单 1.在showCart.jsp页面上,有一个连接,进行结算中心, 应该跳转到一个order.jsp页面.在这个页面上输入订单的相关信息。 输入完成后,提交信息,生成订单。 2.表单提交,访问一个AddOrderServlet,完成生成订单操作. 1.将数据封装到Order对象中. Order order=new Order(); //它封装了订单的 送货地址,总价. BeanUtils.populate(order, request.getParameterMap()); String id=UUID.randomUUID().toString(); order.setId(id);//封装订单的id order.setPaystate(0);//默认值为0,代表未支付。如果为1,代表支付. //封装user_id //从session中获取当前用户. User user=(User) request.getSession().getAttribute("user"); int user_id=user.getId(); order.setUser_id(user_id); 问题:我们生成订单,会对几张表操作? 1.insert into orders 2.insert into orderItem 3.update products set pnum=pnum-?; 对于订单操作,必须添加事务处理. 3.对DataSourceUtils进行修改 // 获取绑定到ThreadLocal中的Connection。 public static Connection getConnectionByTransaction() throws SQLException { Connection con = tl.get(); if (con == null) { con = dataSource.getConnection(); tl.set(con); } return con; } // 开启事务 public static void startTransaction(Connection con) throws SQLException { if (con != null) con.setAutoCommit(false); } // 事务回滚 public static void rollback(Connection con) throws SQLException { if (con != null) con.rollback(); } public static void closeConnection(Connection con) throws SQLException { if (con != null) { con.commit();// 事务提交 con.close(); tl.remove(); } } 注意:在dao中在使用QueryRunner时,不要使用有参数构造,要使用无参数构造, 使用QueryRunner的batch,update方法时,要带Connection参数,而Connection对象的获取是 通过getConnectionByTransaction来获取的。 ------------------------------------------------------------------------------ 查看订单: 1.查看订单时,如果当前用户是admin角色,可以查看所有人订单,如果用户是user,只能查看自己订单。 2.查看订单实现: 1.select * from orders;----->List 2.查询订单中商品的信息。 3.代码实现: 1.点击查看订单时,访问一个ShowOrderServlet,查询出所有订单信息 select * from orders;----->List 将List集合存储到request域,跳转到showOrder.jsp页面在,展示所有信息. response.getWriter().write("订单生成成功,查看订单"); 2.在showOrder.jsp页面展示订单: 问题:展示订单时,想要显示当前订单是哪个用户的,怎样得到用户名? 我们需要将orders与users表关联查询. String sql = "select users.username,users.nickname,orders.* from orders,users where users.id=orders.user_id "; 查询出的数据要封装到Order对象中,但是不能封装username,nickname,所以我们在Order类中添加了两个属性 private String username; private String nickname; ------------------------------------- 3.查看订单中商品详细信息 ajax完成 1.查找某一个订单中所有商品信息的sql语句 SELECT 数据 FROM orderitem,products WHERE orderitem.product_id=products.id AND orderitem.order_id="订单id"; 2.使用ajax完成操作 1.得到XMLHttpRequest对象 2.onreadstatechange 注册回调函数 3.open 4.send 5.在回调函数中操作. 我们查询出订单中商品信息,以json格式返回. --------------------------------------------------------------------------------------------- 删除订单 1.在showOrder.jsp页面有删除订单的连接。点击连接时,将订单的id传递到服务器端,完成根据id删除订单操作. 问题:如果订单是已经支付的怎样处理?如果是没有支付怎样处理? 我们人为规定,如果是已支付订单,不能删除。如果是未支付订单可以删除。 删除 删除 问题:对于未支付订单,删除时,怎样操作? 需要做三件事情: 1.delete from orders where id=?; 2.delte from orderitem where order_id=? 3.update products set pnum=pnum+? where id=?; 在操作时,需要先删除orderitem表中数据,在删除orders中数据。 我们最后要修改products表中的数据,而它需要的buynum,与product_id都是在orderitem表中存在的。 分析完成上面操作,我们在代码实现时,步骤: 1.select * from orderitem where order_id=?;----->List 2.delete from orderitem where order_id=?; 3.delete from order where id=? 4.updat products set pnum=pnum+buynum where id=product_id; 我们操作时,需要对多表进行操作,也需要事务控制. 2.代码实现: 1.在showOrder.jsp页面添加连接路径. 删除 2.创建DelOrderServlet 1.得到要删除的订单id. 2.调用service完成删除订单操作. 3.在OrderService中创建一个方法 delOrderById(String orderid); 在这个方法中. 1.开启事务 2.根据orderid查询出所有的orderitem中数据.得到一个List 3.根据orderid在orderitem中删除数据 4.根据orderid在orders事删除数据 5.根据List在products表中修改数据. 6.如果有异常,事务回滚,没问题,事务提交,资源释放。 -------------------------------------------------------------------------------- 订单支付: 在线支付---新的知识点 1.在showOrder.jsp页面,如果当订单状态是为支付,我们将其设置成一个连接。 未支付 2.创建一个pay.jsp页面,它就是我们的支付页面。 在页面上得到订单号,金额信息,并且可以选择支付的银行. 3.完成在线支付 1.什么是在线支付?在线支付实现方式? 通过网络直接完成订单的支付。 有两种方式: 1.直接与银行做对接 优点:不会有延迟。 缺点:开发,维护费用比较高.银行接口变动,需要更改。 这种方式不适合中小商户。 2.使用第三方支付 优点:方便,不用处理是怎样支付 缺点:有延迟,会收取一定费用。 这种方式比较适合中小商户。 2.我们使用的是第三方支付 现在使用的比较多第三方支付 支付宝 财富通 快钱 我们使用 易宝支付(http://www.yeepay.com/) 在线支付条件: 1.可以上网。 2.需要开通网银. 开发在线支付条件: 1.可以上网. 2.需要一个独立ip. 3.需要在易宝支付申请一个商家账号。 10001126856 在线支付流程: 查看图 -------------------------------------------- 在线支付代码实现: 1.pay.jsp,页面上有订单号,金额以及选择的银行。 2.OnLinePayServlet,这个servlet就完成数据的收集,以及向第三方支付发送数据过程. 问题: 1.要发送给第三方支付的信息有哪些? 2.发送给第三方支付的路径是什么? https://www.yeepay.com/app-merchant-proxy/node 以上这两个问题,我们需要查询易宝支付开发手册。 重要属性:p8_Url 是易宝支付反馈信息时的路径。 hmac=数据+密钥+算法. 密钥:L69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl 3.创建一个CallBackServlet,用于接收第三方支付返回的信息. http://www.estore.com/callBack?p1_MerId=10001126856&r0_Cmd=Buy&r1_Code=1&r2_TrxId=315223279200392I&r3_Amt=0.01&r4_Cur=RMB&r5_Pid=&r6_Order=asdflkasdiej&r7_Uid=&r8_MP=&r9_BType=1&ru_Trxtime=20141222105450&ro_BankOrderId=2636414683141222&rb_BankId=BOC-NET&rp_PayDate=20141222105443&rq_CardNo=&rq_SourceFee=0.0&rq_TargetFee=0.0&hmac=1852414bf1f5f63587a59e40cd8c35f2 关于第三方返回信息重点: r9_BType 交易结果返回类型 为“1”: 浏览器重定向; 如果关闭浏览器,就可能接收不到信息。 为“2”: 服务器点对点通讯.---要求必须返回一个success,否则会一直发送。 ----------------------------------------------------------------------------------------------- 在线支付完成后,修改订单的状态。 update order set paystate=1 where id=r6_order; ================================================================================================= 下载销售榜单 就是一个文件下载操作. 问题:怎样获取到下载文件中的数据? 要在已经支付的订单中查找销售的商品名称以及数量。 select products.name,sum(buynum) totalSaleNum from products,orderitem,orders where orderitem.product_id=products.id and orders.id=orderitem.order_id and orders.paystate=1 group by pname order by totalSaleNum 下载操作: 1.下载榜单连接 2.创建一个DownloadServlet 1.得到数据 2.根据数据,通过response.getWriter()流写回到浏览器端. 3. 榜单文件是什么格式? 导出Excel 使用 POI类库 csv 格式文件 , 逗号分隔文件 1) 信息当中有,在两端加 双引号 2) 信息当中有" 在之前加双引号 转义 文件下载 设置Content-Type、Content-Disposition 头信息 文件流输出 (输出文件内容) Excel 默认读取字符集gbk ============================================================================================== 权限 1.url级别权限控制(粗粒度权限控制) 原理:得到当前的访问的资源路径,得到当前用户角色,来判断当前用户是否有权限访问该资源. 1.得到资源路径. String uri=request.getRequestURI(); String contextPath=request.getContextpath(); String path=uri.substring(contextPath.length()); 2.当到当前用户角色,通过角色,判断当前用户是否有权限访问path资源。 1.得到当前用户 request.getSession().getAttribute("user"); 2.做配置文件,在配置文件中声明每个角色具有的权限。 amdin.properties user.properties. 实现: 1.添加商品----->admin 2.下载榜单----->admin 3.添加商品到购物车 购物车操作。-----user 3.关于订单操作 user admin 代码实现: 1.创建两个配置文件 user.properties 配置关于user角色具有的权限 admin.properties 配置关于admin角色具有的权限. 将配置文件放置在WEB-INF下. 2.创建一个PrivilegeFilter进行权限控制 1.在其init方法中将配置文件中内容读取出来装入到admins,users集合中。 2.得到请求资源路径,判断是否需要权限. 3.创建一个自定义异常,如果权限不足,抛出这个异常。 在web.xml文件中配置全局异常处理. cn.itcast.estore.exception.PrivilegeException /error/privilege.jsp ========================================================================================================================= 重构 1.一个请求一个servlet,现在要做一个模块一个servlet,也就是说,多个请求会访问同一个servlet. UserServlet 注册 登录 注销 激活 CartServlet 关于购物车操作 ProductServlet 关于商品操作 注意:我们重构时没有将添加商品处理. OrderServlet 关于订单操作 2.对servlet中的操作在进行一次重构 原因: 在UserServlet中 if ("regist".equals(method)) { regist(request, response); } else if ("login".equals(method)) { login(request, response); } 在ProductServlet中 if ("findProductById".equals(method)) { findProductById(request, response); } else { // 默认就是查询所有. findAllProduct(request, response); } 上面的代码,操作是一致的,我们可以在一次进行抽取。生成一个BaseServlet. 它的代码 String methodName = request.getParameter("method"); Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this, request, response); 举例分析: http://www.estore.com/user?method=login 1.知道要访问的是UserServlet。 2.因为UserServlet extends BaseServlet,这时就会访问 BaseServlet中的service方法。 3.在BaseServlet中 request.getParameter("method");--->login 4.得到指定的servlet中指定方法 Method method = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class, HttpServletResponse.class); 这句话就相当于得到了UserServlet中的login方法。 5.method.invoke(this,request,response); 这句话就相录于 UserServlet.login(request,response); ----------------------------- 以上重载操作后, 在访问servlet中的方法时,只需要 /url-pattern值?method=方法名。 2.细粒度(annotation+动态代理) 原理:在service中的方法上添加一个注解,注解的值代表的是访问这个功能所需要的权限名称。 我们的service的获取,是通过一个工厂获取的,而在工厂中返回的是service的动态代理对象。 在动态代理中去控制是否有权限访问当前操作。 代码实现: 1.创建一个注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface PrivilegeInfo { String value(); } 2.抽取service 抽取出接口.例如: public interface ProductService { // 添加商品 @PrivilegeInfo("添加商品") public void addProduct(Product p) throws Exception; // 查询所有商品 public List findAll() throws Exception; // 根据id查询商品 public Product findById(String id) throws Exception; // 下载榜单数据 @PrivilegeInfo("下载榜单") public List downloadSell(User user) throws PrivilegeException, Exception } 3.创建ServiceFactory 在serviceFactory中通过动态代理生成一个service代理对象,返回这个代理对象. 在servlet中使用的都是通过ServiceFactory获取的service对象,也就动态代理对象。 ProductService service = ProductServiceFactory.getInstance(); 4.在动态代理的InvocationHandler的invoke方法中处理。 1.判断方法上是否有注解,也就知道,是否需要权限控制. boolean flag = method .isAnnotationPresent(PrivilegeInfo.class); 2.得到注解中的属性值,也就得到了访问该方法的权限名称 String pname = method.getAnnotation( PrivilegeInfo.class).value(); 3.得到当前用户(对于需要权限控制的方法,在其方法的第一个参数,都设置为User) User user = (User) args[0]; 4.得到user的role,在权限配置文件中查找这个角色所具有的权限名称 List pnames = Arrays.asList(ResourceBundle .getBundle("privilege").getString(role) .split(",")); 5.判断这个角色是否可以执行该方法. if (!pnames.contains(pname)) { throw new PrivilegeException(); } 问题:抛出异常不跳转到指定的页面? 原因:我们操作是在invoke方法中执行的。而invoke方法抛出的是Throwable,我们自己抛出的PrivilegeException, 会被包装。 在外面捕获不到。 在开如,我们对页面的权限控制可以使用url级别,而对具体的行为执行,可能通过细粒度权限控制。

你可能感兴趣的:(Java,Server,Pages,Java,Web,servlet,JavaWeb,网上商城设计)