JAVAEE宜立方商城12:购物车实现、订单确认页面展示

时间:2018-08-06
本文章向大家介绍JAVAEE宜立方商城12:购物车实现、订单确认页面展示,需要的朋友可以参考一下。

1. 学习计划

第十二天:

1、购物车实现

2、订单确认页面展示

2. 购物车的实现

2.1. 功能分析

1、购物车是一个独立的表现层工程。

2、添加购物车不要求登录。可以指定购买商品的数量。

3、展示购物车列表页面

4、修改购物车商品数量

5、删除购物车商品

2.2. 工程搭建

e3-cart-web打包方式war

可以参考e3-portal-web

2.2.1. Pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.e3mall</groupId>
        <artifactId>e3-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>cn.e3mall</groupId>
    <artifactId>e3-cart-web</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>cn.e3mall</groupId>
            <artifactId>e3-manager-interface</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- JSP相关 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- dubbo相关 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <!-- 排除依赖 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.jboss.netty</groupId>
                    <artifactId>netty</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
    <!-- 配置tomcat插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8089</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


3. 未登录状态下使用购物车 

3.1. 添加购物车

3.1.1. 功能分析

在不登陆的情况下也可以添加购物车。把购物车信息写入cookie

优点:

1、不占用服务端存储空间

2、用户体验好。

3、代码实现简单。

缺点:

1、cookie中保存的容量有限。最大4k

2、把购物车信息保存在cookie中,更换设备购物车信息不能同步。

改造商品详情页面

 

请求的url/cart/add/{itemId}

参数:

1)商品idLong itemId
2)商品数量: int num

业务逻辑:

1、cookie中查询商品列表。

2、判断商品在商品列表中是否存在。

3、如果存在,商品数量相加。

4、不存在,根据商品id查询商品信息。

5、把商品添加到购车列表。

6、把购车商品列表写入cookie

返回值:逻辑视图

Cookie保存购物车

1keyTT_CART

2Value:购物车列表转换成json数据。需要对数据进行编码。

3Cookie的有效期:保存7天。

商品列表:

List<TbItem>,每个商品数据使用TbItem保存。当根据商品id查询商品信息后,取第一张图片保存到image属性中即可。

读写cookie可以使用CookieUtils工具类实现。

3.1.2. Controller

@Controller
public class CartController {
    
    @Value("${TT_CART}")
    private String TT_CART;
    @Value("${CART_EXPIRE}")
    private Integer CART_EXPIRE;

    @Autowired
    private ItemService itemService;
    
    @RequestMapping("/cart/add/{itemId}")
    public String addCartItem(@PathVariable Long itemId, Integer num,
            HttpServletRequest request, HttpServletResponse response) {
        // 1、从cookie中查询商品列表。
        List<TbItem> cartList = getCartList(request);
        // 2、判断商品在商品列表中是否存在。
        boolean hasItem = false;
        for (TbItem tbItem : cartList) {
            //对象比较的是地址,应该是值的比较
            if (tbItem.getId() == itemId.longValue()) {
                // 3、如果存在,商品数量相加。
                tbItem.setNum(tbItem.getNum() + num);
                hasItem = true;
                break;
            }
        }
        if (!hasItem) {
            // 4、不存在,根据商品id查询商品信息。
            TbItem tbItem = itemService.getItemById(itemId);
            //取一张图片
            String image = tbItem.getImage();
            if (StringUtils.isNoneBlank(image)) {
                String[] images = image.split(",");
                tbItem.setImage(images[0]);
            }
            //设置购买商品数量
            tbItem.setNum(num);
            // 5、把商品添加到购车列表。
            cartList.add(tbItem);
        }
        // 6、把购车商品列表写入cookie。
        CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        return "cartSuccess";
    }
    
    /**
     * 从cookie中取购物车列表
     * <p>Title: getCartList</p>
     * <p>Description: </p>
     * @param request
     * @return
     */
    private List<TbItem> getCartList(HttpServletRequest request) {
        //取购物车列表
        String json = CookieUtils.getCookieValue(request, TT_CART, true);
        //判断json是否为null
        if (StringUtils.isNotBlank(json)) {
            //把json转换成商品列表返回
            List<TbItem> list = JsonUtils.jsonToList(json, TbItem.class);
            return list;
        }
        return new ArrayList<>();
    }
    
}


3.2. 展示购物车商品列表 

请求的url:/cart/cart

参数:无

返回值:逻辑视图

业务逻辑:

1、cookie中取商品列表。

2、把商品列表传递给页面。

3.2.1. Controller

@RequestMapping("/cart/cart")
    public String showCartList(HttpServletRequest request, Model model) {
        //取购物车商品列表
        List<TbItem> cartList = getCartList(request);
        //传递给页面
        model.addAttribute("cartList", cartList);
        return "cart";
    }


3.3. 修改购物车商品数量 

3.3.1. 功能分析

1、在页面中可以修改商品数量

2、重新计算小计和总计。

3、修改需要写入cookie

4、每次修改都需要向服务端发送一个ajax请求,在服务端修改cookie中的商品数量。

 

请求的url/cart/update/num/{itemId}/{num}

参数:long itemIdint num

业务逻辑:

1、接收两个参数

2、cookie中取商品列表

3、遍历商品列表找到对应商品

4、更新商品数量

5、把商品列表写入cookie

6、响应e3ResultJson数据。

返回值:

 e3ResultJson数据

3.3.2. Controller

@RequestMapping("/cart/update/num/{itemId}/{num}")
    @ResponseBody
    public e3Result updateNum(@PathVariable Long itemId, @PathVariable Integer num,
            HttpServletRequest request, HttpServletResponse response) {
        // 1、接收两个参数
        // 2、从cookie中取商品列表
        List<TbItem> cartList = getCartList(request);
        // 3、遍历商品列表找到对应商品
        for (TbItem tbItem : cartList) {
            if (tbItem.getId() == itemId.longValue()) {
                // 4、更新商品数量
                tbItem.setNum(num);
            }
        }
        // 5、把商品列表写入cookie。
        CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        // 6、响应e3Result。Json数据。
        return e3Result.ok();
    }


3.3.3. 解决请求*.html后缀无法返回json数据的问题 

springmvc中请求*.html不可以返回json数据。

修改web.xml,添加url拦截格式。

3.4. 删除购物车商品

3.4.1. 功能分析

请求的url/cart/delete/{itemId}

参数:商品id

返回值:展示购物车列表页面。Url需要做redirect跳转。

业务逻辑:

1、url中取商品id

2、cookie中取购物车商品列表

3、遍历列表找到对应的商品

4、删除商品。

5、把商品列表写入cookie

6、返回逻辑视图:在逻辑视图中做redirect跳转。

3.4.2. Controller

@RequestMapping("/cart/delete/{itemId}")
    public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request,
            HttpServletResponse response) {
        // 1、从url中取商品id
        // 2、从cookie中取购物车商品列表
        List<TbItem> cartList = getCartList(request);
        // 3、遍历列表找到对应的商品
        for (TbItem tbItem : cartList) {
            if (tbItem.getId() == itemId.longValue()) {
                // 4、删除商品。
                cartList.remove(tbItem);
                break;
            }
        }
        // 5、把商品列表写入cookie。
        CookieUtils.setCookie(request, response, TT_CART, JsonUtils.objectToJson(cartList), CART_EXPIRE, true);
        // 6、返回逻辑视图:在逻辑视图中做redirect跳转。
        return "redirect:/cart/cart.html";
    }


4. 登录状态下的购物车处理 

4.1. 功能分析

1、购物车数据保存的位置:

未登录状态下,把购物车数据保存到cookie中。

登录状态下,需要把购物车数据保存到服务端。需要永久保存,可以保存到数据库中。可以把购物车数据保存到redis中。

2、redis使用的数据类型

a) 使用hash数据类型

b) Hashkey应该是用户idHash中的field是商品idvalue可以把商品信息转换成json

3、添加购物车

登录状态下直接包商品数据保存到redis中。

未登录状态保存到cookie中。

4、如何判断是否登录?

a) cookie中取token

b) 取不到未登录

c) 取到token,到redis中查询token是否过期。

d) 如果过期,未登录状态

e) 没过期登录状态。

4.2. 判断用户是否登录

4.2.1. 功能分析

应该使用拦截器实现。

1、实现一个HandlerInterceptor接口。

2、在执行handler方法之前做业务处理

3、cookie中取token。使用CookieUtils工具类实现。

4、没有取到token,用户未登录。放行

5、取到token,调用sso系统的服务,根据token查询用户信息。

6、没有返回用户信息。登录已经过期,未登录,放行。

7、返回用户信息。用户是登录状态。可以把用户对象保存到request中,在Controller中可以通过判断request中是否包含用户对象,确定是否为登录状态。

4.2.2. LoginInterceptor

/**
 * 判断用户是否登录的拦截器
 * <p>Title: LoginInterceptor</p>
 * <p>Description: </p>
 * <p>Company: www.itcast.cn</p> 
 * @version 1.0
 */
public class LoginInterceptor implements HandlerInterceptor {
    
    @Value("${COOKIE_TOKEN_KEY}")
    private String COOKIE_TOKEN_KEY;
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 执行handler方法之前执行此方法
        // 1、实现一个HandlerInterceptor接口。
        // 2、在执行handler方法之前做业务处理
        // 3、从cookie中取token。使用CookieUtils工具类实现。
        String token = CookieUtils.getCookieValue(request, COOKIE_TOKEN_KEY);
        // 4、没有取到token,用户未登录。放行
        if (StringUtils.isBlank(token)) {
            return true;
        }
        // 5、取到token,调用sso系统的服务,根据token查询用户信息。
        E3Result e3Result = userService.getUserByToken(token);
        // 6、没有返回用户信息。登录已经过期,未登录,放行。
        if (e3Result.getStatus() != 200) {
            return true;
        }
        // 7、返回用户信息。用户是登录状态。可以把用户对象保存到request中,在Controller中可以通过判断request中是否包含用户对象,确定是否为登录状态。
        TbUser user = (TbUser) e3Result.getData();
        request.setAttribute("user", user);
        //返回true放行
        //返回false拦截
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
        // 执行handler方法之后,并且是返回ModelAndView对象之前

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 返回ModelAndView之后。可以捕获异常。

    }

}


4.2.3. Springmvc.xml配置拦截器 

<!-- 拦截器配置 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="cn.e3mall.cart.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>


4.3. 添加购物车 

4.3.1. 功能分析

登录状态下添加购物车,直接把数据保存到redis中。需要调用购物车服务,使用redishash来保存数据。

Key:用户id

Field:商品id

Value:商品对象转换成json

参数:

1、用户id

2、商品id

3、商品数量

业务逻辑:

1、根据商品id查询商品信息

2、把商品信息保存到redis

a) 判断购物车中是否有此商品

b) 如果有,数量相加

c) 如果没有,根据商品id查询商品信息。

d) 把商品信息添加到购物车

3、返回值。E3Result

4.3.2. dao

根据商品id查询商品信息,单表查询。可以使用逆向工程。

4.3.3. Service

@Service
public class CartServiceImpl implements CartService {
    
    @Value("${CART_REDIS_KEY}")
    private String CART_REDIS_KEY;

    @Autowired
    private TbItemMapper itemMapper;
    @Autowired
    private JedisClient jedisClient;
    
    @Override
    public E3Result addCart(long userId, long itemId, int num) {
        // a)判断购物车中是否有此商品
        Boolean flag = jedisClient.hexists(CART_REDIS_KEY + ":" + userId, itemId + "");
        // b)如果有,数量相加
        if (flag) {
            //从hash中取商品数据
            String json = jedisClient.hget(CART_REDIS_KEY + ":" + userId, itemId + "");
            //转换成java对象
            TbItem tbItem = JsonUtils.jsonToPojo(json, TbItem.class);
            //数量相加
            tbItem.setNum(tbItem.getNum() + num);
            //写入hash
            jedisClient.hset(CART_REDIS_KEY + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem));
            //返回添加成功
            return E3Result.ok();
        }
        // c)如果没有,根据商品id查询商品信息。
        TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
        //设置商品数量
        tbItem.setNum(num);
        String image = tbItem.getImage();
        //取一张图片
        if (StringUtils.isNotBlank(image)) {
            tbItem.setImage(image.split(",")[0]);
        }
        // d)把商品信息添加到购物车
        jedisClient.hset(CART_REDIS_KEY + ":" + userId, itemId + "", JsonUtils.objectToJson(tbItem));
        return E3Result.ok();
    }

}


发布服务: 

 

4.3.4. Controller

@RequestMapping("/cart/add/{itemId}")
    public String addCart(@PathVariable Long itemId, Integer num,
            HttpServletRequest request, HttpServletResponse response) {
        //判断用户是否为登录状态
        Object object = request.getAttribute("user");
        if (object != null) {
            TbUser user = (TbUser) object;
            //取用户id
            Long userId = user.getId();
            //添加到服务端
            E3Result e3Result = cartService.addCart(userId, itemId, num);
            return "cartSuccess";
        }
        //如果登录直接把购物车信息添加到服务端
        //如果未登录保存到cookie中
        // 1、从cookie中取购物车列表。
        List<TbItem> cartList = getItemListFromCookie(request);
        // 2、判断商品列表是否存在此商品。
        boolean falg = false;
        for (TbItem tbItem : cartList) {
            if (tbItem.getId() == itemId.longValue()) {
                //数量相加
                // 4、如果存在,数量相加。
                tbItem.setNum(tbItem.getNum() + num);
                falg = true;
                break;
            }
        }
        // 3、如果不存在添加到列表
        if (!falg) {
            //根据商品id取商品信息
            TbItem tbItem = itemService.getItemById(itemId);
            //设置数量
            tbItem.setNum(num);
            String image = tbItem.getImage();
            //取一张图片
            if (StringUtils.isNotBlank(image)) {
                tbItem.setImage(image.split(",")[0]);
            }
            //添加到列表
            cartList.add(tbItem);
        }
        // 5、把购车列表写入cookie
        CookieUtils.setCookie(request, response, COOKIE_CART_KEY, JsonUtils.objectToJson(cartList), COOKIE_CART_EXPIRE, true);
        // 6、返回逻辑视图,提示添加成功
        return "cartSuccess";
    }


4.4. 登录状态下访问购物车列表 

4.4.1. 功能分析

1、未登录状态下购物车列表是从cookie中取。

2、登录状态下购物车应该是从服务端取。

3、如果cookie中有购物车数据,应该吧cookie中的购物车和服务端合并,合并后删除cookie中的购物车数据。

4、合并购物车时,如果商品存在,数量相加,如果不存在,添加一个新的商品。

5、从服务端取购物车列表展示

4.4.2. Dao

不需要访问数据库,只需要访问redis

4.4.3. Service

1、合并购物车

参数:用户id

      List<TbItem>