JavaWeb网上商城

来源:互联网 发布:java读取zip文件夹 编辑:程序博客网 时间:2024/05/01 04:40
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.jar2.创建包结构cn.itcast.estore.web.servletcn.itcast.estore.web.filtercn.itcast.estore.web.listenercn.itcast.estore.servicecn.itcast.estore.daocn.itcast.estore.domaincn.itcast.estore.utils3.创建domainUML中类图画法 4.工程发布将estore项目配置虚拟主机,以顶级域名方式进行发布 在浏览器上直接输入www.estore.com就可以访问到我们的工程.1.在tomcat的conf目录下的server.xml文件中配置1.修改tomcat的端口 80.2.配置虚拟主机<Engine name="Catalina" defaultHost="www.estore.com"><Host name="www.estore.com"  appBase="D:\java1110\workspace\estore" unpackWARs="true" autoDeploy="true"><Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"   prefix="localhost_access_log." suffix=".txt"   pattern="%h %l %u %t "%r" %s %b" />   <Context path="" docBase="D:\java1110\workspace\estore\WebRoot"/>  </Host>  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中添加一个请求转发操作<jsp:forward page="/home.jsp"/>2.在home.jsp页面上添加一个注册的连接。<a href="${pageContext.request.contextPath}/regist.jsp">注册</a>3.创建一个regist.jsp页面页面上有1.username2.password3.email4.nickname还需要一个repassword 确认密码还需要一个验证码4.在页面上产生验证码1.将new_words.txt复制到WEB-INF目录下.2.在regist.jsp页面上<img src="${pageContext.request.contextPath}/checkImg" onclick="change();" id="cimg">注意编码问题.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.在<form onsubmit="return checkForm();">2.校验,只校验非空.function checkNull(fieldName){var value = document.getElementById(fieldName).value;var reg = /^\s*$/; //代表0个或多个空字符。if(reg.test(value)){document.getElementById(fieldName+"_message").innerHTML="<font color='red'>"+fieldName+"不能为空</font>";return false;}else{return true;}}2.服务器端校验在javaBean中做一个校验方法public Map<String, String> validation() {Map<String, String> map = new HashMap<String, String>();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.关于发送激活邮件邮箱 duhongabcdef@163.com密码  abc123关于激活邮件发送我们在UserService中的regist方法中完成。关于MailUtils工具使用:1.props.setProperty("mail.host", "自己邮箱的smtp");2.return new PasswordAuthentication("邮箱帐户", "密码");3.message.setFrom(new InternetAddress("发送者邮箱"));注意:关于发送的信息问题:String emailMsg="注册成功,请<a href='http://www.estore.com/activeUser?activeCode="+user.getActivecode()+"'>激活</a>,激活码为:"+user.getActivecode();8.关于激活用户操作http://www.estore.com/activeUser?activeCode=d104ed31-0a5f-4eb4-8377-085c9be5f6e41.创建一个ActiveUserServlet1.得到激活码,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.onload=function(){//页面加载成功后跳用这个函数。var username=document.getElementById("username");window.deusername.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页面有注解的连接。<a href="${pageContext.request.contextPath}/logout">注销</a>创建一个LogOutServlet完成注解功能session.invalidate();问题:销毁session的方式:1.关闭服务器2.invalidate()3.自动超时在tomcat/conf/web.xml文件中配置超时时间 <session-config><session-timeout>30</session-timeout></session-config>4.setMaxInactiveInterval(int interval) 手动设置session超时时间.注意:如果我们有自动登录操作,那么当我们完成注销操作后,会跳转到首页,                这时,如果在cookie中存储了用户名与密码,就会进行自动登录,那么注销的效果就看不到了,所以要看到效果,可以将自动登录的cookie删除。问题:1.一个用户在两个浏览器登录要想解决,需要将数据存储到数据库中。可以使用session共享服务器来解决.2.两个用户在同一个浏览器登录会出现共享session问题,简单说,第一个用户购买的物品,存储在session中。第二个用户登录后,直接就可以看到第一个用户购买的商品。解决方案:在每一个用户登录时,先销毁session.============================================================================================================商品操作:1.添加商品(上传操作)1.在home.jsp页面上有一个连接<a href="${pageContext.request.contextPath}/addProduct.jsp">添加商品</a>2.创建addProduct.jsp页面问题:页面上有什么组件?查看products表中的数据.文件上传时浏览器端注意:1.method=post2.encType="multipart/form-data"3.<input type="file" name="f">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.DiskFileItemFactory2.ServletFileUpload3.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<String,String[]> 将数据手动封装.问题:关于id怎样封装?uuid获取.问题:关于imgurl怎样封装?map.put("imgurl", new String[]{"/upload"+uuidDir+"/"+uuidname})2.调用service,dao完成添加操作.3.添加成功后,跳转到首面.---------------------------------------------------------------2.查看商品1.查看全部index.jsp---->findAllProduct------->home.jsp1.创建一个FindAllProductServlet在这个servlet中查询出所有商品List<Product>,将其存储到request域,在请求转发到home.jsp页面2.在home.jsp页面展示<div class="art-content-layout overview-table"><div class="art-content-layout-row"><c:forEach items="${ps}" var="p" varStatus="vs"><div class="art-layout-cell"><div class="overview-table-inner"><h4>${p.name }</h4><img src="${pageContext.request.contextPath}${p.imgurl}" width="55px" height="55px"alt="an image" class="image" /><p>价格: ¥${p.price }</p><p>速速抢购</p></div></div><c:if test="${vs.count%5==0}"></div> <!-- 判断当前已经有5个商品了,这 一行结束,在重新开启一行 --><div class="art-content-layout-row"></c:if></c:forEach><!-- end cell --></div><!-- end row --></div><!-- end table -->--------------------------------------------------2.查看商品详细信息它有两个入口,一个是点击速速抢购.还有点击图片也可以查看商品详细信息.1.<a href="${pageContext.request.contextPath}/findProductById?id=${p.id}">速速抢购</a>2.在图片上添加一个onclickfunction findProductById(id){location.href="http://www.estore.com/findProductById?id="+id;};查看商品详细信息:1.创建FindProductByIdServlet1.得到商品id2.根据id调用service,dao完成查询商品操作.2.创建productInfo.jsp页面,展示商品信息关于展示商品时,<img>展示商品图片,可以通过它的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页面上<img src="${pageContext.request.contextPath}${p.imgurl_s}">==========================================================================================================购物车我们使用的session来存储购物车,在数据库中没有关于购物车中商品信息。1.添加商品到购物车在productInfo.jsp页面有连接。问题:怎样将商品添加到购物车,购物车我们使用什么数据结构来存储商品信息?购物车我们使用Map<Product,Integer>在productInfo.jsp页面上连接,它要传递商品的idfunction addProductToCart(id){location.href="${pageContext.request.contextPath}/addProductToCart?id="+id;}创建一个AddProductToCartServlet1.根据id得到商品2.得到购物车3.将商品添加到购物车注意:我们使用的购物车其实是一个HashMap<Product,Integer>,对于HashMap,它的维护主键唯一性,是使用    key值的hashCode与equals方法,也就是说,对于我们的购物车,它是使用Product的hashCode与equals方法来保证,我们同一个商品的数量的变化.简单说,对于我们就需要重写Product类的equals方法与hashCode方法。--------------------------------------------------------------------------------------2.查看购物车商品有两个入口1.添加商品到购物车成功后,有提示,查看购物车2.在首面有查看购物车查看购物车,我们直接就是一个jsp页面上将购物车中商品展示出来就可以。因为购物车就存储在session中.创建一个showCart.jsp,用于展示购物车中所有商品.购物车中商品总价怎样获取:每个商品的单价*商品数量<c:set var="totalMoney" value="${totalMoney+c.key.price*c.value}"/>-----------------------------------------------------------------------------------------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()来转换成数值类型。数字文本框:在<input type="text">这个文本框中只能输入数字,不能输入其它的字符。原理:给文本框添加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-57if(!(keyCode>=48&&keyCode<=57||keyCode==8)){//阻止事件的默认行为if(e&&e.preventDefault){// e对象存在,preventDefault方法存在 ---- 火狐浏览器e.preventDefault();}else{// 不支持e对象,或者没有preventDefault方法 ---- IEwindow.event.returnValue = false;}}}; ---------------------------------------------------------------------------从购物车中删除商品1.连接提交时,将商品id携带到服务器<a href="${pageContext.request.contextPath}/removeProductFromCart?id=${c.key.id}">删除</a>2.创建RemoveProductFromCartServlet将要删除的商品从购物车中删除// 得到要删除的商品的idString id = request.getParameter("id");// 得到购物车,从购物车中将商品删除,Map<Product, Integer> cart = (Map<Product, Integer>) request.getSession().getAttribute("cart");Product p = new Product();p.setId(id);cart.remove(p);//如果购物车中无商品,将购物车删除。if (cart.size() == 0) {request.getSession().removeAttribute("cart");}关于删除商品时的提示处理:方式1:<a href="javascript:void(0)" onclick="removeProduct('${c.key.id}')">删除</a>function removeProduct(id) {var flag = window.confirm("要删除商品码?");if(flag){//要删除location.href="${pageContext.request.contextPath}/removeProductFromCart?id="+id;}}方式2:通过阻止事件的默认行为来控制<a href="${pageContext.request.contextPath}/removeProductFromCart?id=${c.key.id}"onclick="deleteProduct(event)">删除</a>function deleteProduct(e) {var flag = window.confirm("要删除商品码?");if (!flag) {//不删除,阻止连接的默认行为 执行。//阻止事件的默认行为if (e && e.preventDefault) {// e对象存在,preventDefault方法存在 ---- 火狐浏览器e.preventDefault();} else {// 不支持e对象,或者没有preventDefault方法 ---- IEwindow.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);//封装订单的idorder.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 orders2.insert into orderItem3.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<Order>2.查询订单中商品的信息。3.代码实现:1.点击查看订单时,访问一个ShowOrderServlet,查询出所有订单信息select * from orders;----->List<Order>将List集合存储到request域,跳转到showOrder.jsp页面在,展示所有信息.response.getWriter().write("订单生成成功,<a href='"+request.getContextPath()+"/showOrder'>查看订单</a>");<a href="${pageContext.request.contextPath}/showOrder"></a>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.open4.send5.在回调函数中操作.我们查询出订单中商品信息,以json格式返回.---------------------------------------------------------------------------------------------删除订单1.在showOrder.jsp页面有删除订单的连接。点击连接时,将订单的id传递到服务器端,完成根据id删除订单操作.问题:如果订单是已经支付的怎样处理?如果是没有支付怎样处理?我们人为规定,如果是已支付订单,不能删除。如果是未支付订单可以删除。<c:if test="${order.paystate==0}"><a href="#">删除</a></c:if><c:if test="${order.paystate!=0}">删除</c:if>问题:对于未支付订单,删除时,怎样操作?需要做三件事情: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<OrderItem>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页面添加连接路径.<a href="${pageContext.request.contextPath}/delOrder?orderid=${order.id}">删除</a>2.创建DelOrderServlet1.得到要删除的订单id.2.调用service完成删除订单操作.3.在OrderService中创建一个方法 delOrderById(String orderid);在这个方法中.1.开启事务2.根据orderid查询出所有的orderitem中数据.得到一个List<Orderitem>3.根据orderid在orderitem中删除数据4.根据orderid在orders事删除数据5.根据List<OrderItem>在products表中修改数据.6.如果有异常,事务回滚,没问题,事务提交,资源释放。--------------------------------------------------------------------------------订单支付:在线支付---新的知识点1.在showOrder.jsp页面,如果当订单状态是为支付,我们将其设置成一个连接。<a href='${pageContext.request.contextPath}/pay.jsp?orderid=${order.id}&money=${order.money}'>未支付</a>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=数据+密钥+算法.密钥:L69cl522AV6q613Ii4W6u8K6XuW8vM1N6bFgyv769220IuYe9u37N4y7rI4Pl3.创建一个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) totalSaleNumfromproducts,orderitem,orderswhereorderitem.product_id=products.idandorders.id=orderitem.order_idandorders.paystate=1group bypnameorder bytotalSaleNum下载操作:1.下载榜单连接<a href="${pageContext.request.contextPath}/download">2.创建一个DownloadServlet1.得到数据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.propertiesuser.properties.实现:1.添加商品----->admin2.下载榜单----->admin3.添加商品到购物车  购物车操作。-----user3.关于订单操作     user  admin代码实现:1.创建两个配置文件user.properties  配置关于user角色具有的权限admin.properties 配置关于admin角色具有的权限.将配置文件放置在WEB-INF下.2.创建一个PrivilegeFilter进行权限控制1.在其init方法中将配置文件中内容读取出来装入到admins,users集合中。2.得到请求资源路径,判断是否需要权限.3.创建一个自定义异常,如果权限不足,抛出这个异常。在web.xml文件中配置全局异常处理.<error-page><exception-type>cn.itcast.estore.exception.PrivilegeException</exception-type><location>/error/privilege.jsp</location></error-page>=========================================================================================================================重构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=login1.知道要访问的是UserServlet。2.因为UserServlet extends BaseServlet,这时就会访问  BaseServlet中的service方法。3.在BaseServlet中request.getParameter("method");--->login4.得到指定的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)@Inheritedpublic @interface PrivilegeInfo {String value();}2.抽取service抽取出接口.例如:public interface ProductService {// 添加商品@PrivilegeInfo("添加商品")public void addProduct(Product p) throws Exception;// 查询所有商品public List<Product> findAll() throws Exception;// 根据id查询商品public Product findById(String id) throws Exception;// 下载榜单数据@PrivilegeInfo("下载榜单")public List<Product> 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<String> pnames = Arrays.asList(ResourceBundle.getBundle("privilege").getString(role).split(","));5.判断这个角色是否可以执行该方法.if (!pnames.contains(pname)) {throw new PrivilegeException();}问题:抛出异常不跳转到指定的页面?原因:我们操作是在invoke方法中执行的。而invoke方法抛出的是Throwable,我们自己抛出的PrivilegeException,会被包装。在外面捕获不到。在开如,我们对页面的权限控制可以使用url级别,而对具体的行为执行,可能通过细粒度权限控制。

1 0
原创粉丝点击