经历了几个周写的易买网项目终于告一段落,我也切实感受到代码之路真的需要披荆斩棘、学无止境,我所懂得东西真的是太少太少。而项目过后,回想起来,真的是收获良多。
对于这种简单的电商网站,不需要特别多的思维逻辑,但是把所学东西实际化,还是有一点难度,下面我一一将难点和知识点剖析解决:
一、三级导航
三级导航算是本项目的第一个难点,一般写首页的都会首先触及它。而什么是三级导航呢?顾名思义,就是分为三层的导航栏,但是呢,其中层层之间有着不可遮掩的关系,一级导航下对应着只属于它的二级导航,二级导航下对应着只属于它的三级导航,而数据库里这些导航分类都在一张数据表里,只是用不同的type来区分并且用parentid来进行相邻两级,那怎样使后台一张表的数据在前台显示为分层数据呢,这里我运用了递归思想进行处理:
//首先创建实体类,在种类的实体类中加入一个字段:当前种类的泛型集合,用来存储当前种类对象的子集集合
//得到一个带有层次的一级导航集合,调用时,传值getNav(0,1)
public List<product_type> getNav(int parentid, int type) throws Exception { //创建一个存储当前分类的集合 List<product_type> lists = new ArrayList<product_type>(); //这条sql能根据不同的parentid和type查出所有的种类 String sql = "SELECT * FROM easybuy_product_category WHERE parentid=? AND TYPE=?"; //得到对应的结果集 ResultSet rs = this.executeQuery(sql, parentid, type);
//遍历结果集,创建对象给对象一一赋值 while (rs.next()) { product_type prdType = new product_type(); prdType.setId(rs.getInt("id")); prdType.setName(rs.getString("name")); prdType.setParentId(rs.getInt("parentId")); prdType.setType(rs.getInt("type")); //type值无非1,2,3这三种值,明确递归的终止条件 if (type < 3) { type++; List<product_type> nav = getNav(prdType.getId(), type);//通过再次调用当前方法,便可以查出当前对象下的子集种类集合 prdType.setChirdren(nav);//将集合赋给当前对象的属性 }
//用来解决其他对象递归时,type值已经大于3,其他对象无法查询对应子集的问题 if (type == 3) { type = 2; } else if (type == 2) { type = 1; } lists.add(prdType); } return lists; }
递归的基本思想是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。
效果图:
二、购物车创作思路
购物车板块主要分为两部分,第一部分是往购物车里添加商品,第二部分是购物车对象的区分与传递。按照老思想来说,给数据库建一张购物车表,用id进行区分,未登录状态下得购物车可以放到cookie中,这虽然是可行的,但是呢,登录状态下购物车的数据如果直接放在数据库里,频繁的操作,会对数据库产生不小的压力,所以在这里我采取了redis缓存技术。
思路设计:
1.首先我们需要创建一个购物车实体类,里面需要两个字段,一是id,二是商品的泛型集合,用来存储商品集。需要注意的是购物车类和商品类需要实现序列化接口,以保证redis缓存的实现
public class shoppingCar implements Serializable { private static final long serialVersionUID = 5339853742025233145L; private String id;//id private List<product> shoppingList=new ArrayList<product>(); //购物车集合 public String getId() { return id; } public void setId(String id) { this.id = id; } public List<product> getShoppingList() { return shoppingList; } public void setShoppingList(List<product> shoppingList) { this.shoppingList = shoppingList; } @Override public String toString() { return "shopping [id=" + id + ", shoppingList=" + shoppingList + "]"; } }
2.未登录和已登录两个状态,其实对于购物车的基本操作来说,这两个状态没什么太大区别,因为Redis是一个key-value存储系统,所以只是存储的key不一样。我这里把该步判断提成了一个方法,因为需要多次调用(这里我有一个问题,我的写法是前台传到servlet里userid,但是其实登陆成功后,会话作用域里就有,可以直接从缓存中取出userid,进行判断,如果userid不为空,则用uuid工具类产生字符串代表key);未登录下的uuid产生的key存到cookie中
// 判断用户类型 public String getID(HttpServletRequest req, HttpServletResponse resp) { // 获取用户id String userid = req.getParameter("userid"); String id = "no"; if (userid == null || userid == "") { Cookie[] cookies = req.getCookies(); for (Cookie cookie : cookies) { if (cookie.getName().equals("noLogin")) { id = cookie.getValue(); System.out.println("存在"); System.out.println(id); } } if (id.equals("no")) { System.out.println("不存在"); // 通过uuid产生新的id值 id = uuid.getUUID(); // 将未登录的标识保存在cookie中 Cookie cookie = new Cookie("noLogin", id); resp.addCookie(cookie); } } else { id = userid; } return id; }
3.添加购物车,这里情况就比较复杂,既包含第一次添加购物车和后续添加购物车的问题,又包含当后续添加购物车时,购物车中有没有当前商品的问题。
public void addShop(HttpServletRequest req, HttpServletResponse resp) throws IOException { String shopid = req.getParameter("shopid"); int num = Integer.parseInt(req.getParameter("num")); // 通过商品id得到商品对像 product prd = getOneProduct(shopid, req, resp); String id = getID(req, resp); // 根据id从缓存中取出购物车对象 shoppingCar Shopp = JSONObject.parseObject(conn.get(id), shoppingCar.class); // 第一次添加购物车 if (Shopp == null) { shoppingCar shop = new shoppingCar(); // 完备购物车对象 shop.setId(id); prd.setNumber(num); shop.getShoppingList().add(prd); // 将购物车对象保存在缓存中 conn.set(id, JSON.toJSONString(shop)); int size = shop.getShoppingList().size(); resp.getWriter().write(String.valueOf(size)); } else { Shopp.setShoppingList(ShopUpdate.ShopAdd(Shopp.getShoppingList(), prd, num));//调用工具类的方法进行后续添加商品操作 delkey.del(id);// 删除原有购物车缓存 conn.set(id, JSON.toJSONString(Shopp));// 放入新的购物车对象 int size = Shopp.getShoppingList().size(); resp.getWriter().write(String.valueOf(size)); } }
这里我把购物车的商品操作封装成一个工具类,上面就可以直接调用方法解决问题
package com.easybuy.util; import java.util.Iterator; import java.util.List; import com.easybuy.entity.product; public class ShopUpdate { //向购物车中添加商品 public static List<product> ShopAdd(List<product> list,product prd,int num){ boolean isExit=false; for (product product : list) { if (product.getId().toString().equals(prd.getId().toString())) { int index=product.getNumber(); index=index+num; product.setNumber(index); System.out.println("已存在该商品"); isExit=true; break; } } if (!isExit) { prd.setNumber(num); System.out.println("不存在该商品"); list.add(prd); } return list; } //修改购物车中的商品数量 public static int UpdateShopNum(List<product> list,String prdId,int num){ int count=0; for (product product : list) { if (product.getId().toString().equals(prdId)) { product.setNumber(num); count++; } } return count; } //删除购物车的某件商品 public static int delOneShop(List<product> list,String prdId){ int count=0; Iterator<product> iterator = list.iterator(); while(iterator.hasNext()){ product integer = iterator.next(); if(integer.getId().toString().equals(prdId)) iterator.remove(); //注意这个地方 count++; } return count; } //删除购物车中的多选商品集 public static int delMoreShop(List<product> list,String [] prdId){ int count=0; Iterator<product> iterator = list.iterator(); while(iterator.hasNext()){ product integer = iterator.next(); for (int i = 0; i < prdId.length; i++) { if(integer.getId().toString().equals(prdId[i])){ iterator.remove(); //注意这个地方 count++; } } } return count; } //购物车转换 public static List<product> noToLogin(List<product> listnew,List<product> listold){ for (product prdNew : listnew) { boolean isHaving=false; for (product prdOld : listold) { if (prdOld.getId().toString().equals(prdNew.getId().toString())) { prdOld.setNumber(prdNew.getNumber()); System.out.println("加进来存在的"); isHaving=true; break; } } if (!isHaving) { listold.add(prdNew); System.out.println("加进来的未存在"); } } return listold; } }
4.其他的购物车板块的修改删除操作,道理都差不多,逻辑都是判断用户对象,然后调用相应方法
5.未登录状态缓存清除时机:一个是由未登录状态转为登录状态,一个是未登录状态下添加购物车后,没有登录直接退出
(1).由未登录转为登录状态
从缓存中将未登录购物车数据取出,然后取出用户的购物车数据,然后将未登录数据加入登陆数据里,清除掉原来未登录的缓存。但中间还涉及到一些问题,譬如原先账户里有某件商品a,数量为5。现在未登录状态下,向购物车中添加了商品a,数量为4,这时候登录,购物车中a商品的数量应当为几?应当为4,对同一件商品的操作,取最近操作。
代码实现:
if (user != null) { //购物车缓存数据转换 //从redis中取出购物车对象 String noid="no"; Cookie[] cookies = req.getCookies(); for(Cookie cookie : cookies){ if(cookie.getName().equals("noLogin")){ noid = cookie.getValue(); //清空cookie cookie.setMaxAge(0); cookie.setPath("/"); resp.addCookie(cookie); } } shoppingCar noShopp = JSONObject.parseObject(conn.get(noid), shoppingCar.class); if (noShopp==null) { System.out.println("没有购物车"); }else{ System.out.println("有购物车"); shoppingCar userShopp = JSONObject.parseObject(conn.get(user.getId().toString()), shoppingCar.class);//原有对象的购物车 if (userShopp!=null) { List<product> noToLogin = ShopUpdate.noToLogin(noShopp.getShoppingList(), userShopp.getShoppingList()); userShopp.setShoppingList(noToLogin); System.out.println("原用户有购物车"); key.del(user.getId().toString()); conn.set(user.getId().toString(),JSON.toJSONString(userShopp)); }else { System.out.println("原用户没有购物车"); conn.set(user.getId().toString(),JSON.toJSONString(noShopp)); } long del = key.del(noid);//删除未登录购物车 } // 将用户信息保存session req.getSession().setAttribute("user", user); // 跳转到index.jsp resp.sendRedirect(""); } else { resp.sendRedirect("login.jsp"); }
(2).未登录状态下直接退出网页缓存处理
未登录状态下数据处理我想了几种思路,以供参考
第一种:给未登录状态下的redis缓存一个死key,比如:-1,但是value存储的购物车对象以uuid产生的字符串保存,这样在项目启动时,进首页前的servlet中将缓存key为-1的取出,然后从cookie中取值,如果取到的值和缓存取出的数据的id一致,代表不是首次进项目,缓存不能清,反之,清除掉-1存储的缓存。
第二种:未登录的数据可以不放缓存,放到cookie中,这样会少一部分处理
6.其实对于购物车的我还有一个想法,就是数据库建表,网页中的操作在缓存中进行,最后找时机将缓存和数据库践行对比交接。
三、第三方登录
日后再补
QQ:805678214,有事联系