使用SSM重构Bookstore——产品模块及购物车部分

一、产品模块

1、产品列表

主要是分页——在此使用MybatisPageHelper分页插件

POM添加依赖

<dependency>
	<groupId>com.github.pagehelpergroupId>
	<artifactId>pagehelperartifactId>
	<version>5.1.8version>
dependency>

Spring 修改 Mybatis 配置部分



<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	
	<property name="dataSource" ref="dataSource" />
	
	<property name="mapperLocations" value="classpath:Mapper/*.xml" />
	
	<property name="configuration" >
		<bean class="org.apache.ibatis.session.Configuration" >
			<property name="mapUnderscoreToCamelCase" value="true" />
			<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
			 
		bean>
	property>

	<property name="plugins" >
		<array> 
			<bean class="com.github.pagehelper.PageInterceptor">
				<property name="properties">
				<value>
				    offsetAsPageNum=true 
				    rowBoundsWithCount=true 
					reasonable=true    
				value>
				property>
			bean>
		array>
	property>
bean>

<mybatis:scan base-package="com.bookstore.Dao" />

ProductController.java

package com.bookstore.Controller;

import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.github.pagehelper.PageInfo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Controller
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/productlist")
    public String productList(@RequestParam(required = false) String catagory, @RequestParam(value="page", required = false, defaultValue = "1") Integer curPage, Model model) {
        Integer pageSize = 5;
        List<Product> productList = productService.findProductsOnPage(curPage, pageSize, catagory); //实际类型分页插件修改后的Page,它扩展了ArrayList
        model.addAttribute("pageInfo", new PageInfo(productList));
        model.addAttribute("productList", productList);
        return "product_list";
    }
}

ProductService.java

package com.bookstore.Controller;

import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

@Service
public class ProductService {

    @Autowired
    private ProductMapper productDao;

    public List<Product> findProductsOnPage(Integer curPage, Integer pageSize, String catagory) {
        ProductExample productExample = new ProductExample();
        ProductExample.Criteria criteria = productExample.createCriteria();
        if(catagory == null || "".equals(catagory)){
            criteria.andCategoryIsNotNull();
        }else{
            criteria.andCategoryEqualTo(catagory);
        }
        PageHelper.startPage(curPage, pageSize, true);
        return productDao.selectByExample(productExample); //分页时,实际返回的结果list类型是Page,如果想取出分页信息,需要强制转换为Page或者使用PageInfo(list)类包装
    }
}

JSP部分省略

2、产品详情

ProductController.java(改写后利用Json保存购物车信息)

@GetMapping("/productinfo")
public String productInfo(@RequestParam String id, Model model, HttpServletResponse response) throws JsonProcessingException, UnsupportedEncodingException {
	Product product = productService.findProductByID(id);
	if (product == null) {
		model.addAttribute("message", "该产品不存在#-1");
		return "message";
	}

	//将产品信息(排除库存)json化后返回给前端,在pnum字段添加JsonIgnore注解
	ObjectMapper mapper = new ObjectMapper();
	Cookie cookie = new Cookie("product_" + id, URLEncoder.encode(mapper.writeValueAsString(product),"UTF-8"));
	cookie.setMaxAge(30 * 24 * 60 * 60);
	response.addCookie(cookie);

	model.addAttribute("product", product);
	return "product_info";
}

ProductService.java

public Product findProductByID(String id) {
	return productDao.selectByPrimaryKey(id);
}

产品详情页购买的JS实现——使用JS创建cookie利用Json保存购物车信息

使用encodeURIComponent、decodeURIComponent编解码Cookie与tomcat有关,cookie含有某些字符时tomcat会报错故而需要转义

<script src="js/jquery.js"></script>
<script type="text/javascript">
    function getQueryVariable(variable) {   //处理URL问号后面的部分
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i = 0; i < vars.length; i++) {
            var pair = vars[i].split("=");
            if (pair[0] == variable) {
                return pair[1];
            };
        }
        return false;
    }

    var pid = getQueryVariable("id");
	cartCookie(0);
	
    function addCart() {   //添加到购物车,同步保存在服务端session的cart
        cartCookie(1);
        $.ajax({
            type: "post",
            withCredential: true,  //ajax提交带上cookie
            url: "${pageContext.request.contextPath}/changeCart"
        });
    }

    function cartCookie(bnum) {
  		var cart={};
        if(getCookie("cart")=="") {
            cart[pid] = bnum;
        }else{
            cart = JSON.parse(decodeURIComponent(getCookie("cart")));
            if(pid in cart){
                cart[pid] += bnum;
            }else{
                cart[pid] = bnum;
            }
        }
        setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
        var num = JSON.parse(decodeURIComponent(getCookie("cart")))[pid];
        if(num == 0) return;
        $("#buynum").val(num);
        $(".buy").attr("style", "display:inline");
    }

    function setCookie(cname, cvalue, exdays) {
        var d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        var expires = "expires=" + d.toGMTString();
        document.cookie = cname + "=" + cvalue + "; " + expires;
    }

    function getCookie(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i].trim();
            if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
        };
        return "";
    }
</script>

3、其他说明

原本想将产品详情部分的id改成URL网址的一部分,但改动后静态资源的相对路径变了读取不到,故而又改回来

html的链接href属性,相对路径时以"/“打头直接连接到domain即"域名+href”,不以"/“打头连接到当前目录即"当前URL(去除当前页面文件名及QueryString部分)+href”,最好还是利用浏览器工具看看其到底怎么解析地址

服务器端的相对地址指的是相对于你的web应用的地址,客户端的地址即所有的html中的相对地址都是相对于域名的

使用SSM重构Bookstore——产品模块及购物车部分_第1张图片

二、购物车部分 ——基于cookie,侧重于客户端保存信息

ShoppingController.java

添加到购物车——调整前需要多次查询数据库:

package com.bookstore.Controller;

import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
public class ShoppingController {

    @Autowired
    private ProductService productService;

    @ResponseBody
    @PostMapping("/addCart")   //@CookieValue能否和变量绑定,@SessionAttribute为空还得创建
    public int addCart(@RequestParam("id") String productID, HttpServletRequest request, HttpServletResponse response) {
      // 原来是每次调整购物车都会查询数据库,将其改造为下单时统一查询通过后再提交订单  
       Product product = productService.findProductByID(productID);
		
        @SuppressWarnings("unchecked")
        Map<Product, Integer> cart = (Map<Product, Integer>) request.getSession().getAttribute("cart");

        if (cart == null) {
            cart = new HashMap<>();
        }
		
        //从cookie中读取采购信息,原来是分cookie单独保存,得重新刷新到相关页面才会将原有的购物信息添加到session
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals( {
                cart.put(product, Integer.valueOf(cookie.getValue()));
                break;
            }
        }

        if (cart.containsKey(product)) { // 得重写Product的hashcode和equal方法,不然每次都是新的不会加1
            int num = cart.get(product) + 1;
            if (num <= product.getPnum()) {
                cart.put(product, num);
            }
        } else {
            cart.put(product, 1);
        }

        request.getSession().setAttribute("cart", cart);

        Cookie cookie = new Cookie(String.valueOf(product.getId()), String.valueOf(cart.get(product)));
        cookie.setHttpOnly(true);
        cookie.setMaxAge(15 * 24 * 60 * 60);
        response.addCookie(cookie);
        return cart.get(product);
    }
}

调整后尽量减少数据库查询,将购物车写到一个cookie里方便读取

package com.bookstore.Controller;

import com.bookstore.Domain.Product;
import com.bookstore.Service.ProductService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

@Controller
public class ShoppingController {

    @Autowired
    private ProductService productService;

    @ResponseBody
    // 返回值为void时要不加上ResponeseBody或者HttpServletResponse入参,要不Spring MVC就会查找RequestMapping中路径对应的视图  https://blog.csdn.net/yh_zeng2/article/details/75136614
    @PostMapping("/changeCart")  //名字由addCart改为changeCart
    public void changeCart(@CookieValue("cart") String cartCookie, HttpSession session) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Integer> cartID = mapper.readValue(URLDecoder.decode(cartCookie, "UTF-8"), new TypeReference<Map<String, Integer>>() {});
        session.setAttribute("cartID", cartID);   //若要持久化保存得将其写入数据库即可,这里仅由浏览器端保存
    }


    //打开购物车,基于cookie生成
    @GetMapping("/cart")
    public String showCart(@SessionAttribute Map<String, Integer> cartID, HttpServletRequest request) throws IOException {
    	Cookie[] cookies = request.getCookies();
        if(cartID == null) {  //未选购商品直接打开购物车时
            String cartCookie = "";
            for(Cookie cookie : cookies){
                if(cookie.getName().equals("cart")){
                    cartCookie = cookie.getValue();
                    break;
                }
            }
            changeCart(cartCookie, request.getSession());    //handler方法也是可以调用的
            cartID = (Map<String, Integer>) request.getSession().getAttribute("cartID");
        }
        Map<Product, Integer> cart = new HashMap<>();
        Cookie[] cookies = request.getCookies();
        ObjectMapper mapper = new ObjectMapper();
        for (String productID : cartID.keySet()) {
            for (Cookie cookie : cookies) {
                Product product;
                if(!cookie.getName().startsWith("product_"))continue;
                if (productID.equals(cookie.getName().split("_")[1])) {
                    product = mapper.readValue(URLDecoder.decode(cookie.getValue(), "UTF-8"), Product.class);
                } else {
                    product = productService.findProductByID(productID);    //排除cookie过期情况
                }
                cart.put(product, cartID.get(product.getId()));
            }
        }
        request.setAttribute("cart", cart); //不用model看看效果是否一样
        return "cart";
    }

    //结算,以显示的表单为准
    @GetMapping("/settleaccounts")
    public String settleAccounts(@RequestParam List<String> id, @RequestParam("amount") List<Integer> buyNum, Model model, HttpSession session){      // 表单是否可以将两个list转化为map呢 —— 变成 "产品id = 采购数量"
        Map<Product, Integer> cart = new HashMap<>();
        Map<String,String> msg = new HashMap<>();
        Integer length = id.size();
        for(int i=0; i<length; i++){
            if(buyNum.get(i)<=0)continue;
            Product product = productService.findProductByID(id.get(i));
            cart.put(product, buyNum.get(i));
            if(product.getPnum() < buyNum.get(i)){
               msg.put(id.get(i), "库存不足,仅剩"+ product.getPnum()+"件");
            }
        }
        if(msg.size() != 0){
            model.addAttribute("cart", cart);
            model.addAttribute("msg", msg);
            return "cart";
        }
        session.setAttribute("cart", cart);
        return "redirect:order";
    }
}
    /*  改变数量时不再查询数据库
    @ResponseBody
    @PostMapping("changeItemAmount")
    public boolean changeItemAmount(@SessionAttribute Map cart, HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        int amount = Integer.parseInt(request.getParameter("amount"));
        Product product = productService.findProductByID(id);
        cart.put(product, amount);
        if (amount == -1) { // -1时删除从购物车删除商品
            cart.remove(product);
        }
        Cookie cookie = new Cookie(String.valueOf(product.getId()), String.valueOf(cart.get(product)));
        cookie.setMaxAge(amount == -1 ? 0 : 15 * 24 * 60 * 60);
        response.addCookie(cookie);

        if (amount > product.getPnum()) {
            return false;
        }
        return true;
    }
    */

购物车相关JS代码

$("table .amount").change(function(){   //直接修改数量
	changeAmount(this);
});

$("table :button[value='-']").click(function(){   // “-”绑定事件
	var t = $(this).next();   //元素的选取定位请按需调整
	var value = $(t).val()-1;	
	if(value>=0){
		$(t).val(value);
		changeAmount(this);
	}
});

$("table :button[value='+']").click(function(){  //"-" 绑定事件
	var t = $(this).prev();
	var value = Number($(t).val())+1;	
	$(t).val(value);
	changeAmount(this);
});

$("table a.removeItem").click(function(){removeItem(this);});   // 删除操作
function removeItem(item){    //删除条目函数
	//console.log(item);
    //修改cookie并同步
    var cart = JSON.parse(decodeURIComponent(getCookie("cart")));
	delete cart[$(item).parent().siblings(".pid").children(":first").attr("value")];
	setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
	$.ajax({
            type: "post",
            withCredential: true,
            url: "${pageContext.request.contextPath}/changeCart"
	});
	//更新价格显示
	var amountPriceNode = $(item).parent().siblings(".amountPrice");
	var totalPriceNode = $("#totalPrice");
	totalPriceNode.text((Number(totalPriceNode.text())-Number(amountPriceNode.text())).toFixed(2));   //保留两位有效小数
	$(item).parent().parent().remove();
};

function changeAmount(elem){
    //控制“-”键有效性
	var amount = Number($(elem).parent().children(".amount").val());
	if(amount==0){
		$(elem).parent().children(".amount").prev().attr("disabled","disabled");
		if(confirm("是否要删除此商品?")){
			removeItem(elem);
			return;
		}
	}else{
		$(elem).parent().children(".amount").prev().removeAttr("disabled");
	};

	//调整价格显示
	var amountPriceNode = $(elem).parent().siblings(".amountPrice");
	var priceNode= $(elem).parent().siblings(".price");
	//console.log(price);
	var totalPriceNode = $("#totalPrice");
	var oldAmountPrice = Number(amountPriceNode.text());
	amountPriceNode.text((amount*Number(priceNode.text())).toFixed(2));
	totalPriceNode.text((Number(totalPriceNode.text())+Number(amountPriceNode.text())-oldAmountPrice).toFixed(2));

	// cookie部分
	var cart = JSON.parse(decodeURIComponent(getCookie("cart")));
	cart[$(elem).parent().siblings(".pid").children(":first").attr("value")] = amount;
	setCookie("cart", encodeURIComponent(JSON.stringify(cart)), 30);
	$.ajax({
            type: "post",
            withCredential: true,
            url: "${pageContext.request.contextPath}/changeCart"
	});
}

function setCookie(cname, cvalue, exdays) {
	var d = new Date();
	d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
	var expires = "expires=" + d.toGMTString();
	document.cookie = cname + "=" + cvalue + "; " + expires;
}


function getCookie(cname) {
	var name = cname + "=";
	var ca = document.cookie.split(';');
	for (var i = 0; i < ca.length; i++) {
		var c = ca[i].trim();
		if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
	};
	return "";
}

三、自作自受

写代码有时有点突发奇想,突然冒出个主意似乎这样做可以优化下,然后动手但有可能做着做着又觉得不对了删掉,但一回头却发现删掉的部分还是有用处的,如果没有保存就悲剧了,所以得用好版本管理工具 —— 我这次就有点悲剧,不大用IDE的版本管理,一般用rsync同步到git目录然后再用git管理,没保存的结果就是来回折腾浪费了好久!可以用IDE的git管理本地,需要提交到github再同步嘛

你可能感兴趣的:(JAVA,Spring)