手记

移动商城第七篇【购物车增删改查、提交订单】


tags: 移动商城项目


把商品加入购物车

接下来我们要做的就是将商品加入到购物车中。我们这次使用的是Cookie来将用户的信息存储起来。那为什么要用cookie呢??

  • 如果将购物车存储在Session,那么Session里边的值容易丢失。
  • 存储在Cookie中,用户不需要登陆就能够查看得到购物车的数据。

那我们现在决定将购物车存储在Cookie中了,那Cookie中保存的是怎么样的字符串数据呢???

  • 显而易见我们第一时间想到的就是JSON

JSON能够很好地将字符串转成Java对象,将Java对象转成JSON给浏览器进行显示!

看回我们的加入购物车,将什么数据加入进去呢???

我们可以将数量和SkuId加入进去就行了。有了SkuId就可以获取商品类的所有数据了!

加入购物车之前

在加入购物车之前我们需要做两件事:

  • 判断Cookie是否被禁用了【我们使用Cookie来进行保存购物车数据的、Cookie不能被禁用!】
  • 判断库存是否充足【在加入之前、查询一次库存是否足够!】

判断Cookie是否被禁用了:


    /**
     * 校验Cookie是否被禁用了。
     *
     * 其实就是新建一个Cookie,然后是否能获取得到Cookie的值。
     *  如果可以得到,那么就是Cookie没有被禁用
     *  如果没有得到,那么Cookie就被禁用了。
     * @param request
     * @param response
     * @return
     */
    String validCookie(HttpServletRequest request, HttpServletResponse response);

    public String validCookie(HttpServletRequest request, HttpServletResponse response) {

        //默认被禁用
        String result = "ban";

        //只要是本主机下的,Cookie都会被发布出去
        Cookie cookie = new Cookie("test", "test");
        cookie.setPath("/");
        response.addCookie(cookie);

        //查看是否能获取得到Cookie
        Cookie[] cookies = request.getCookies();

        if (cookies != null && cookies.length > 0) {
            for (Cookie cookie1 : cookies) {
                String name = cookie1.getName();
                String value = cookie1.getValue();
                /**
                 * 如果都拿到,那么Cookie就没有被禁用!
                 */
                if (name.equals("test") && value.equals("test")) {

                    result = "noBan";
                    return result;
                }
            }
        }
        return result;
    }

    @RequestMapping("/validateCookie.do")
    public void  validateCookie(HttpServletRequest request, HttpServletResponse response, PrintWriter writer) {
        String result = cartService.validCookie(request, response);
        writer.write(result);
    }

判断库存是否充足:

    @RequestMapping("/validateStock.do")
    public void validateStock(Long SkuId, Integer quantity,PrintWriter writer) {

        String result = "noStock";
        EbSku sku = skuService.selectByPrimaryKey(SkuId);
        //判断数量多还是库存多
        if (sku.getStockInventory() > quantity) {
            result = "hasStock";
        }
        writer.write(result);
    }

在页面上通过class属性和自定义id来获取页面上的库存和数量!

前台页面的代码如下:


        //加入购物车之前,判断库存和cookie
        function addCart() {

            var cookieResult = validateCookie();
            if (cookieResult == "ban") {
                alert("Cookie被禁用了,无法加入购物车");
                return;
            }
            //在页面上获取得到对应的skuId和数量
            var skuId = null ;
            $("#skuChange a").each(function(){
                var clazz = $(this).attr("class");
                if(clazz == "here"){
                    skuId = $(this).attr("skuId");
                }
            });
            var quantity = $("#quantity").val();
            var StockResult = validateStock(skuId,quantity);
            if (StockResult == "noStock") {
                alert("库存已经不足,无法加入购物车");
                return;
            }
            window.location.href = "http://localhost:8080/portal/cart/addCart.do";
        }
        function validateCookie() {
            var result = "";
            $.ajax({
                url: "http://localhost:8080/portal/cart/validateCookie.do",
                type: "post",
                async:false,
                success:function (responseText) {
                    result =  responseText
                },
                error:function () {
                    alert("系统错误");
                }
            });
            return result;
        }
        function validateStock(skuId,quantity) {
            var result = "";
            $.ajax({
                url: "http://localhost:8080/portal/cart/validateStock.do",
                data:{
                    skuId:skuId,
                    quantity:quantity
                },
                type: "post",
                async:false,
                success:function (responseText) {
                    result =  responseText
                },
                error:function () {
                    alert("系统错误");
                }
            });
            return result;
        }
将数据拼接成Cookie保存起来

在前台中已经通过ajax调用service来判断是否有库存和cookie是否被禁用了。接下来就将我们的skuId和quantity拼接成Cookie保存起来。也是使用ajax来将数据带过去


            //将skuId和quantity带过去给服务器
            $.ajax({
                url: "http://localhost:8080/portal/cart/addCart.do",
                data:{
                    skuId:skuId,
                    quantity:quantity
                },
                type: "post",
                success:function (responseText) {
                     if(responseText=="addSuccess"){
                         alert("成功添加到购物车中!");
                     }
                },
                error:function () {
                    alert("系统错误");
                }
            });

编写service添加购物车


    /**
     * 添加购物车,使用Cookie保存起来!
     *
     * 使用到Cookie的话,那么就需要用到request和response对象了。
     * @param request
     * @param response
     */
    void  addCart(HttpServletRequest request, HttpServletResponse response,Long skuId, Integer quantity);

把商品添加购物车中也有好几种情况:

  • 用户没有购物车
  • 购物车的商品已存在
  • 购物车的商品不存在

接下来我们来去看看购物车页面是怎么样的:

只有知道了购物车展示页面我们才知道我们的购物车实体是怎么设计的

从图上我们可以知道大多数的数据都是Sku中的,还有商品的编号和商品的名称。

于是,我们可以使用Sku作为主体,与Item来进行关联就行了。于是乎,我们可以设计购物车实体是这样的:

public class EbCart {

    private Long skuId;
    private Integer quantity;

    private EbSku sku;

    public Long getSkuId() {
        return skuId;
    }
    public void setSkuId(Long skuId) {
        this.skuId = skuId;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public EbSku getSku() {
        return sku;
    }

    public void setSku(EbSku sku) {
        this.sku = sku;
    }
}

sku实体要与Item进行关联:


    //与Item进行关联
    private EbItem item;
    public EbItem getItem() {
        return item;
    }
    public void setItem(EbItem item) {
        this.item = item;
    }

根据cookie的三种情况写service业务逻辑


 public void addCart(HttpServletRequest request, HttpServletResponse response, Long skuId, Integer quantity) {

        List<EbCart> cartList = null;

        //json的配置对象
        JsonConfig jc = new JsonConfig();
        //设置要转换的类
        jc.setRootClass(EbCart.class);
        //设置不需要转换的属性
        jc.setExcludes(new String[]{"sku"});

        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                String name = c.getName();

                //读取购物车的key,在配置文件中保存起来了。
                String cart_key = ResourcesUtils.readProp("cart_key");
                if (StringUtils.equalsIgnoreCase(name, cart_key)) {
                    //得到cookie里边的值【JSON格式的数组】
                    String value = c.getValue();

                    //将JSON数组转成是Java对象
                    JSONArray ja = JSONArray.fromObject(value);
                    cartList = (List<EbCart>) JSONSerializer.toJava(ja, jc);

                    //判断要添加的商品在购物车是否存在
                    String result = "noExist";
                    for (EbCart cart : cartList) {
                        //购物车中已存在
                        if (cart.getSkuId() == skuId) {
                            cart.setQuantity(cart.getQuantity() + quantity);
                            result = "exist";
                        }
                    }
                    //购物车中的商品不存在
                    if (StringUtils.equalsIgnoreCase("noExist", result)) {
                        EbCart ebCart = new EbCart();
                        ebCart.setQuantity(quantity);
                        ebCart.setSkuId(skuId);
                        cartList.add(ebCart);
                    }
                }
            }
        }
        //上边已经判断了购物车存在,商品是否存在的两种情况了。以下是判断购物车是否存在的问题
        //如果上边的cookie为null,转换不了List集合的话,那么我们的List集合是为空的。List集合为空的话,那么我们的购物车是不存在的。
        if (cartList == null || cartList.size() == 0) {
            cartList = new ArrayList<EbCart>();
            EbCart ebCart = new EbCart();
            ebCart.setQuantity(quantity);
            ebCart.setSkuId(skuId);
            cartList.add(ebCart);
        }

        //最后将我们的Java对象重新转成JSON,将Cookie更新
        JSONArray ja = JSONArray.fromObject(cartList, jc);
        String result = ja.toString();
        Cookie cookie = new Cookie("cart_key", result);
        cookie.setMaxAge(Integer.MAX_VALUE);
        cookie.setPath("/");
        response.addCookie(cookie);

    }

查看购物车

查看购物车并不难,和添加购物车一样。都是将Cookie的值转成是Java对象,然后将Java对象返回给页面做展示就行了

  • 要做的就是将购物车商品的信息详细查询出来!

通过skuId查询出具体的商品信息。


    EbSku selectByPrimaryKeyForDetail(Long skuId);

mapper:

  <resultMap id="detailMap" type="com.rl.ecps.model.EbSku" extends="BaseResultMap">
    <association property="item" javaType="com.rl.ecps.model.EbItem">
      <id column="ITEM_ID" property="itemId" jdbcType="DECIMAL"/>
      <result column="ITEM_NAME" property="itemName" jdbcType="VARCHAR"/>
      <result column="ITEM_NO" property="itemNo" jdbcType="VARCHAR"/>
      <result column="BRAND_ID" property="brandId" jdbcType="DECIMAL"/>
      <result column="CAT_ID" property="catId" jdbcType="DECIMAL"/>
      <result column="TAG_IMG_ID" property="tagImgId" jdbcType="DECIMAL"/>
      <result column="TAG_IMG" property="tagImg" jdbcType="DECIMAL"/>
      <result column="IS_NEW" property="isNew" jdbcType="DECIMAL"/>
      <result column="IS_GOOD" property="isGood" jdbcType="DECIMAL"/>
      <result column="IS_HOT" property="isHot" jdbcType="DECIMAL"/>
      <result column="PROMOTION" property="promotion" jdbcType="VARCHAR"/>
      <result column="AUDIT_STATUS" property="auditStatus" jdbcType="DECIMAL"/>
      <result column="SHOW_STATUS" property="showStatus" jdbcType="DECIMAL"/>
      <result column="IMGS" property="imgs" jdbcType="VARCHAR"/>
      <result column="KEYWORDS" property="keywords" jdbcType="VARCHAR"/>
      <result column="PAGE_DESC" property="pageDesc" jdbcType="VARCHAR"/>
      <result column="ITEM_RECYCLE" property="itemRecycle" jdbcType="DECIMAL"/>
      <result column="ON_SALE_TIME" property="onSaleTime" jdbcType="TIMESTAMP"/>
      <result column="CHECK_TIME" property="checkTime" jdbcType="TIMESTAMP"/>
      <result column="UPDATE_TIME" property="updateTime" jdbcType="TIMESTAMP"/>
      <result column="UPDATE_USER_ID" property="updateUserId" jdbcType="DECIMAL"/>
      <result column="CREATE_TIME" property="createTime" jdbcType="TIMESTAMP"/>
      <result column="CHECKER_USER_ID" property="checkerUserId" jdbcType="DECIMAL"/>
      <result column="FULL_PATH_DEPLOY" property="fullPathDeploy" jdbcType="VARCHAR"/>
      <result column="FULL_PATH_DEPLOY_OFFER" property="fullPathDeployOffer" jdbcType="VARCHAR"/>
      <result column="ORIGINAL_ITEM_ID" property="originalItemId" jdbcType="DECIMAL"/>
      <result column="LAST_STATUS" property="lastStatus" jdbcType="DECIMAL"/>
      <result column="MERCHANT_ID" property="merchantId" jdbcType="DECIMAL"/>
      <result column="ITEM_SORT" property="itemSort" jdbcType="DECIMAL"/>
      <result column="SALES" property="sales" jdbcType="DECIMAL"/>
      <result column="CREATE_USER_ID" property="createUserId" jdbcType="DECIMAL"/>
      <result column="SIM_LEVEL" property="simLevel" jdbcType="DECIMAL"/>
      <result column="GIFT_DESC" property="giftDesc" jdbcType="VARCHAR"/>
      <result column="GIFT_IMG" property="giftImg" jdbcType="VARCHAR"/>
      <result column="GIFT_SHOW_TYPE" property="giftShowType" jdbcType="VARCHAR"/>
      <result column="IMG_SIZE1" property="imgSize1" jdbcType="VARCHAR"/>
    </association>
  </resultMap>

  <select id="selectByPrimaryKeyForDetail" parameterType="long" resultMap="detailMap">

    SELECT *
    FROM EB_SKU sku, EB_ITEM item
    WHERE item.ITEM_ID = sku.ITEM_ID
          AND sku.SKU_ID = #{skuId}
  </select>

service查询购物车数据:


    public List<EbCart> listCart(HttpServletRequest request, HttpServletResponse response) {

        List<EbCart> cartList = null;

        //json的配置对象
        JsonConfig jc = new JsonConfig();
        //设置要转换的类
        jc.setRootClass(EbCart.class);
        //设置不需要转换的属性
        jc.setExcludes(new String[]{"sku"});

        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                String name = c.getName();

                //读取购物车的key,在配置文件中保存起来了。
                String cart_key = ResourcesUtils.readProp("cart_key");
                if (StringUtils.equalsIgnoreCase(name, cart_key)) {
                    //得到cookie里边的值【JSON格式的数组】
                    String value = c.getValue();

                    //将JSON数组转成是Java对象
                    JSONArray ja = JSONArray.fromObject(value);
                    cartList = (List<EbCart>) JSONSerializer.toJava(ja, jc);

                    //根据购物车中商品的id获取详细信息
                    for (EbCart cart : cartList) {
                        Long skuId = cart.getSkuId();
                        EbSku sku = skuService.selectByPrimaryKeyForDetail(skuId);
                        cart.setSku(sku);
                    }
                }
            }
        }
        return cartList;

    }

controller查询出商品的数量、购物车总价、返回给页面展示:


    @RequestMapping("/listCart.do")
    public String listCart(HttpServletRequest request, HttpServletResponse response, Model model) {

        List<EbCart> carts = cartService.listCart(request, response);

        //算出购物车总价和商品数量
        int itemNum = 0;
        BigDecimal totalPrice = new BigDecimal(0);
        for (EbCart cart : carts) {
            totalPrice = totalPrice.add(cart.getSku().getSkuPrice().multiply(new BigDecimal(cart.getQuantity())));

            itemNum++;
        }
        model.addAttribute("carts", carts);
        model.addAttribute("totalPrice",totalPrice);
        model.addAttribute("itemNum",itemNum);
        return "shop/car";
    }

页面展示效果:

微调数量

我们可以在页面上微调数量的。

当点击+号的时候,数量+1,当点击-号的时候,数量-1

由于我们数量的改变会导致金钱的改变,可以直接就使用页面刷新的方法来干了!因为使用Ajax的话还要去替换那些值。

对其绑定事件、交由Controller处理


function reduceNum(skuId,quantity) {

    //在减少之前,校验一下库存
    //得到对应数量框
    var val = $("#quantity").val();
    val--;

    var validateResult = validateStock(skuId,val);

    console.log(validateResult);
    if(validateResult=="noStock"){
        alert("没有库存了!");
        return ;
    }
    $("#quantity").val(val);

    //返回给后台进行页面刷新。
    window.location.href = "${path}/cart/updateQuantity.do?skuId="+skuId+"&quantity="+val;
}
function addNum(skuId,quantity) {

    //在减少之前,校验一下库存
    //得到对应数量框
    var val = $("#quantity").val();
    val++;

    var validateResult = validateStock(skuId,val);

    console.log(validateResult);
    if(validateResult=="noStock"){
        alert("没有库存了!");
        return ;
    }
    $("#quantity").val(val);

    //返回给后台进行页面刷新。
    window.location.href = "${path}/cart/updateQuantity.do?skuId="+skuId+"&quantity="+val;
}

编写更新数量的service方法:

  • 先把cookie转成是java对象
  • 判断是否有对应的skuId值,如果有,那么我们修改其数量即可。
  • 重写Cookie返回给浏览器

    public void updateQuantity(HttpServletRequest request, HttpServletResponse response, Long skuId, Integer quantity) {

        List<EbCart> cartList = null;

        //json的配置对象
        JsonConfig jc = new JsonConfig();
        //设置要转换的类
        jc.setRootClass(EbCart.class);
        //设置不需要转换的属性
        jc.setExcludes(new String[]{"sku"});

        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                String name = c.getName();

                //读取购物车的key,在配置文件中保存起来了。
                String cart_key = ResourcesUtils.readProp("cart_key");
                if (StringUtils.equalsIgnoreCase(name, cart_key)) {
                    //得到cookie里边的值【JSON格式的数组】
                    String value = c.getValue();

                    //将JSON数组转成是Java对象
                    JSONArray ja = JSONArray.fromObject(value);
                    cartList = (List<EbCart>) JSONSerializer.toJava(ja, jc);

                    //根据购物车中商品的id获取详细信息
                    for (EbCart cart : cartList) {
                        Long cartSkuId = cart.getSkuId();

                        //找到对应购物车中的商品,这里要使用longValue对数值进行比较。直接双等于是没有用的。
                        if (skuId.longValue() == cartSkuId.longValue()) {
                            cart.setQuantity(quantity);
                        }
                    }
                }
            }
        }
        //最后将我们的Java对象重新转成JSON,将Cookie更新
        JSONArray ja = JSONArray.fromObject(cartList, jc);
        String result = ja.toString();
        Cookie cookie = new Cookie("cart_key", result);
        cookie.setMaxAge(Integer.MAX_VALUE);
        cookie.setPath("/");
        response.addCookie(cookie);

    }

controller调用service的方法,重定向到购物车页面。那么我们的购物车页面拿到的就是最新的数据!


    @RequestMapping("/updateQuantity.do")
    public String updateQuantity(Long skuId, Integer quantity,HttpServletRequest request, HttpServletResponse response) {
        cartService.updateQuantity(request, response, skuId, quantity);

        return "redirect:/cart/listCart.do";
    }
结算

当我们点击结算的时候,我们要做什么事情呢??

  • 要结算了当然是需要登陆的,所以在结算之前判断该用户登陆了没有
  • 还需要再一次检查库存是否足够
  • 跳转到确认页面上。

登陆了

  • 查看库存
    • 库存足够
      • 跳转到页面上
    • 库存不够
      • 提示用户

没登陆

  • 弹出登陆页面

通过AJAX获取用户的信息,如果没有获取得到,那么说明用户还没有登陆。于是就要弹出登陆窗口

如果获取得到用户的信息了,那么就对其进行库存校验
在对购物车的商品库存校验,我们并不一定要通过前台把数据传递给后台进行处理

  • 因为我们购物车的数据和Cookie里边存储的数据是一模一样的。因此,我们可以直接解析Cookie就可以对其进行库存校验了!

function trueBuy(){

    //弹出框来对其登陆
    $.ajax({
        url:"${path}/user/getUser.do",
        type:"post",
        dataType:"text",
        success:function(responseText){
            var userObj  = $.parseJSON(responseText);
            if(userObj.user != null){
                var result = validCar();
                if(result == "success"){
                    window.location.href = "${path}/order/toSubmitOrder.do";
                }else{
                    alert(result);
                }
            }else{
                tipShow("#loginAlert");
            }
        },
        error:function(){
            alert("系统错误");
        }
    })

}
登陆

function loginAjax() {
    var username = $("#username").val();
    var password = $("#password").val();
    var captcha = $("#captcha").val();
    $.ajax({
        url:"${path}/user/loginAjax.do",
        type:"post",
        dataType:"text",
        data:{
            username:username,
            password:password,
            captcha:captcha
        },
        success:function(responseText){
            if(responseText == "caperror"){
                $("#errorName").html("验证码错误");
                $("#errorName").show(500);
            }else if(responseText == "userpasserror"){
                $("#errorName").html("用户名或者密码错误");
                $("#errorName").show(500);
            }else if(responseText == "success"){
                $("#loginAlertIs").html(username);
                tipHide("#loginAlert");
                //校验库存
                var result = validCar();
                if(result == "success"){
                    window.location.href = "${path}/order/toSubmitOrder.do";
                }else{
                    alert(result);
                }
            }
        },
        error:function(){
            alert("系统错误");
        }
    })
}
验证购物车库存是否足够

我们并不需要外界传递id和数据进来,直接使用Cookie来对其判断就行了。因为我们的数据本来就是从Cookie来的。!


function validCar(){
    var result = "success";
    $.ajax({
        url:"${path}/cart/validCar.do",
        type:"post",
        dataType:"text",
        async:false,
        success:function(responseText){
            result = responseText;
        },
        error:function(){
            alert("系统错误");
        }

    })
    return result;
}

public String validateCar(HttpServletRequest request, HttpServletResponse response) {

        List<EbCart> cartList = null;

        StringBuilder builder = null;
        //json的配置对象
        JsonConfig jc = new JsonConfig();
        //设置要转换的类
        jc.setRootClass(EbCart.class);
        //设置不需要转换的属性
        jc.setExcludes(new String[]{"sku"});

        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                String name = c.getName();

                //读取购物车的key,在配置文件中保存起来了。
                String cart_key = ResourcesUtils.readProp("cart_key");
                if (StringUtils.equalsIgnoreCase(name, cart_key)) {
                    //得到cookie里边的值【JSON格式的数组】
                    String value = c.getValue();

                    //将JSON数组转成是Java对象
                    JSONArray ja = JSONArray.fromObject(value);
                    cartList = (List<EbCart>) JSONSerializer.toJava(ja, jc);

                    //遍历购物车中的数据与库存做比较
                    for (EbCart cart : cartList) {

                        int cartQuantity = cart.getQuantity().intValue();

                        //根据购物车中商品的id获取详细信息
                        Long skuId = cart.getSkuId();
                        EbSku sku = skuService.selectByPrimaryKeyForDetail(skuId);

                        if (cartQuantity > sku.getStockInventory().intValue()) {

                            builder = new StringBuilder();

                            //这个商品的库存不足
                            String itemName = sku.getItem().getItemName();

                            builder.append(itemName);
                            List<EbSpecValue> specList = sku.getSpecList();
                            for (EbSpecValue ebSpecValue : specList) {
                                builder.append(ebSpecValue.getSpecValue());
                            }
                            builder.append("库存不足");
                            return builder.toString();
                        }
                    }
                }
            }
        }
        return "success";
    }
订单预备提交

当结算完之后会跳转到订单提交页面。首先我们来看一下订单提交页面是怎么样的:

我们需要把用户的地址、商品的信息查询出来在页面上展示!

那么在跳转到确认页面的时候,我们把数据查询出来即可!


    @RequestMapping("/toSubmitOrder.do")
    public String toSubmitOrder(HttpSession session, Model model, HttpServletRequest request,HttpServletResponse response) {

        //查询用户的地址
        TsPtlUser user = (TsPtlUser) session.getAttribute("user");
        List<EbShipAddr> userAddress = addrService.findUserAddress(user.getPtlUserId());

        //查询购物车清单的信息
        List<EbCart> carts = cartService.listCart(request, response);

        //算出购物车总价和商品数量
        int itemNum = 0;
        BigDecimal totalPrice = new BigDecimal(0);
        for (EbCart cart : carts) {
            totalPrice = totalPrice.add(cart.getSku().getSkuPrice().multiply(new BigDecimal(cart.getQuantity())));
            itemNum++;
        }

        model.addAttribute("carts", carts);
        model.addAttribute("totalPrice",totalPrice);
        model.addAttribute("itemNum",itemNum);
        model.addAttribute("userAddress", userAddress);
        return "shop/confirmProductCase";

    }

查看订单的数据库表

DROP TABLE EB_ORDER CASCADE CONSTRAINTS;

CREATE TABLE EB_ORDER  (
   ORDER_ID             NUMBER(11)                      NOT NULL,
   PTL_USER_ID          NUMBER(11),
   "USERNAME"           VARCHAR2(100),
   ORDER_NUM            VARCHAR2(80),
   PAYMENT              NUMBER(1),
   PAY_PLATFORM         NUMBER(2),
   DELIVERY             NUMBER(1),
   IS_CONFIRM           NUMBER(1),
   ORDER_SUM            NUMBER(20,2),
   SHIP_FEE             NUMBER(20,2),
   IS_PAID              NUMBER(1),
   ORDER_STATE          NUMBER(2),
   PAYMENT_CASH         NUMBER(1),
   DISTRI_ID            NUMBER(11),
   DELIVERY_METHOD      NUMBER(1),
   PAYMENT_NO           VARCHAR2(30),
   ORDER_TIME           TIMESTAMP,
   PAY_TIME             TIMESTAMP,
   DEPOSIT_TIME         TIMESTAMP,
   SUCCESS_TIME         TIMESTAMP,
   UPDATE_TIME          TIMESTAMP,
   SRV_TYPE             NUMBER(2),
   SELF_COLLECT_SITE    VARCHAR2(200),
   IS_DELETED           NUMBER(1)                      DEFAULT 0,
   IS_DISPLAY           NUMBER(1),
   NOTES                VARCHAR2(400),
   SHIP_NAME            VARCHAR2(80)                    NOT NULL,
   PROVINCE             VARCHAR2(40)                    NOT NULL,
   CITY                 VARCHAR2(40)                    NOT NULL,
   DISTRICT             VARCHAR2(40)                    NOT NULL,
   ZIP_CODE             VARCHAR2(40),
   ADDR                 VARCHAR2(400)                   NOT NULL,
   PHONE                VARCHAR2(60)                    NOT NULL,
   PAYABLE              NUMBER(1),
   COMPANY              VARCHAR2(240),
   CONTENTS             NUMBER(2),
   IS_CALL              NUMBER(1)                      DEFAULT 0,
   DELIVERY_NO          VARCHAR2(300),
   AREA_CODE            VARCHAR2(50),
   AREA_NAME            VARCHAR2(50),
   IS_PRINT             NUMBER(1)                      DEFAULT 0,
   CRM_CALLS_TIME       TIMESTAMP,
   IS_OFFER_RELEASE     NUMBER(1)                      DEFAULT 0,
   JOB_NUM              VARCHAR2(300),
   CONSTRAINT PK_EB_ORDER PRIMARY KEY (ORDER_ID)
);

COMMENT ON TABLE EB_ORDER IS
'订单。包括实体商品和虚拟商品的订单';

COMMENT ON COLUMN EB_ORDER.ORDER_ID IS
'订单主键';

COMMENT ON COLUMN EB_ORDER.PTL_USER_ID IS
'前台用户主键';

COMMENT ON COLUMN EB_ORDER."USERNAME" IS
'冗余前台用户名称,只允许以Email地址注册,不可重复,注册后不可修改。大于0小于等于50个字符,必须包含一个并且只有一个符号@ ;第一个字符不得是@或者. ;不允许出现@.或者.@;结尾不得用户账号:只允许以Email地址注册,不可重复,注册后不可修改';

COMMENT ON COLUMN EB_ORDER.ORDER_NUM IS
'订单号:订单号规则:订单生成日期+6位数,递增. 订单起始编号:111024000001. 后六位数一旦满,则后六位数递增为七位数';

COMMENT ON COLUMN EB_ORDER.PAYMENT IS
'支付方式:1: 在线支付;2: 货到付款;3: 营业厅自取;4:邮局汇款';

COMMENT ON COLUMN EB_ORDER.PAY_PLATFORM IS
'支付平台:1: 工商银行; 2: 建设银行; 3: 招商银行';

COMMENT ON COLUMN EB_ORDER.DELIVERY IS
'送货时间:1: 只工作日送货(双休日,假日不用送); 2: 工作日、双休日、假日均可送货; 3: 只双休日、假日送货(工作日送货)';

COMMENT ON COLUMN EB_ORDER.IS_CONFIRM IS
'送货前电话确认:0: 否; 1: 是';

COMMENT ON COLUMN EB_ORDER.ORDER_SUM IS
'订单总金额:各订单项的售价之和,包括运费等。';

COMMENT ON COLUMN EB_ORDER.SHIP_FEE IS
'运费';

COMMENT ON COLUMN EB_ORDER.IS_PAID IS
'是否已支付:0: 未付款; 1: 已付款';

COMMENT ON COLUMN EB_ORDER.ORDER_STATE IS
'订单状态。参见类EbOrderStateConstants.java';

COMMENT ON COLUMN EB_ORDER.PAYMENT_CASH IS
'货到付款方式:1: 现金;2: POS刷卡; 3: 支票';

COMMENT ON COLUMN EB_ORDER.DISTRI_ID IS
'配送商ID';

COMMENT ON COLUMN EB_ORDER.DELIVERY_METHOD IS
'送货方式:1:快递;2:EMS;3:平邮';

COMMENT ON COLUMN EB_ORDER.PAYMENT_NO IS
'支付号';

COMMENT ON COLUMN EB_ORDER.ORDER_TIME IS
'下单时间';

COMMENT ON COLUMN EB_ORDER.PAY_TIME IS
'付款时间';

COMMENT ON COLUMN EB_ORDER.DEPOSIT_TIME IS
'到帐时间';

COMMENT ON COLUMN EB_ORDER.SUCCESS_TIME IS
'成功时间';

COMMENT ON COLUMN EB_ORDER.SRV_TYPE IS
'业务类型,0:无业务;1:需要写卡;2:需要办理CRM;3:需要写卡和办理CRM';

COMMENT ON COLUMN EB_ORDER.SELF_COLLECT_SITE IS
'商品自提点名称';

COMMENT ON COLUMN EB_ORDER.IS_DELETED IS
'前台用户删除订单标记。1:是;0:否';

COMMENT ON COLUMN EB_ORDER.IS_DISPLAY IS
'前台用户删除订单标记。1:是;0:否';

COMMENT ON COLUMN EB_ORDER.NOTES IS
'备注';

COMMENT ON COLUMN EB_ORDER.SHIP_NAME IS
'收货人姓名';

COMMENT ON COLUMN EB_ORDER.PROVINCE IS
'省份';

COMMENT ON COLUMN EB_ORDER.CITY IS
'城市';

COMMENT ON COLUMN EB_ORDER.DISTRICT IS
'地区';

COMMENT ON COLUMN EB_ORDER.ZIP_CODE IS
'邮编';

COMMENT ON COLUMN EB_ORDER.ADDR IS
'街道地址';

COMMENT ON COLUMN EB_ORDER.PHONE IS
'联系电话';

COMMENT ON COLUMN EB_ORDER.PAYABLE IS
'发票抬头:1: 个人;2: 单位';

COMMENT ON COLUMN EB_ORDER.COMPANY IS
'单位名称';

COMMENT ON COLUMN EB_ORDER.CONTENTS IS
'发票内容:1: 明细;2: 办公用品';

COMMENT ON COLUMN EB_ORDER.IS_CALL IS
'是否外呼过。0:未外呼;1:已外呼';

COMMENT ON COLUMN EB_ORDER.DELIVERY_NO IS
'物流编号。';

COMMENT ON COLUMN EB_ORDER.AREA_CODE IS
'市区代码(冗余自EB_CITY_AREA表)';

COMMENT ON COLUMN EB_ORDER.AREA_NAME IS
'市区名称(冗余自EB_CITY_AREA表)';

COMMENT ON COLUMN EB_ORDER.IS_PRINT IS
'是否需要打印发票。0-不需要打印;1-需要打印;';

COMMENT ON COLUMN EB_ORDER.CRM_CALLS_TIME IS
'crm办理成功或失败的时间';

COMMENT ON COLUMN EB_ORDER.IS_OFFER_RELEASE IS
'是否营销案解约。0-否;1-是;';

COMMENT ON COLUMN EB_ORDER.JOB_NUM IS
'工单号,由crm接口返回';

订单明细表:


DROP TABLE EB_ORDER_DETAIL CASCADE CONSTRAINTS;

CREATE TABLE EB_ORDER_DETAIL  (
   ORDER_DETAIL_ID      NUMBER(11)                      NOT NULL,
   ORDER_ID             NUMBER(11),
   ITEM_ID              NUMBER(11),
   ITEM_NAME            VARCHAR2(400),
   ITEM_NO              VARCHAR2(120),
   SKU_ID               NUMBER(11),
   SKU                  VARCHAR2(80),
   SKU_IMG              VARCHAR2(80),
   SKU_NAME             VARCHAR2(500),
   SKU_CAT_TYPE         NUMBER(5),
   SKU_SPEC             VARCHAR2(1000),
   MARKET_PRICE         NUMBER(20,2),
   SKU_DISCOUNT         NUMBER(20,2),
   SKU_PRICE            NUMBER(20,2),
   OFFER_GROUP_ID       NUMBER(11),
   OFFER_GROUP_NAME     VARCHAR2(200),
   OFFER_TYPE           NUMBER(2),
   SHORT_NAME           VARCHAR2(80),
   OFFER_ID             NUMBER(11),
   OFFER_NAME           VARCHAR2(800),
   OFFER_NO             VARCHAR2(120),
   SHORT_NAME2          VARCHAR2(50),
   OFFER_TERM           NUMBER(2),
   COMMIT_MONTHLY       NUMBER(20,2),
   PREPAID              NUMBER(20,2),
   PERIOD               NUMBER(2),
   REFUND_MONTHLY       NUMBER(20,2),
   REFUND_1ST_MONTH     NUMBER(20,2),
   REFUND_LAST_MONTH    NUMBER(20,2),
   ORDER_DETAIL_TYPE    NUMBER(2),
   MERCHANT_ID          NUMBER(11),
   QUANTITY             NUMBER(5),
   PRICE                NUMBER(20,2),
   SERIESCODE           VARCHAR2(3000),
   OFFER_GROUP_NO       VARCHAR2(300),
   PROMO_TYPE           NUMBER(4),
   COND_ID              NUMBER(8),
   CONSTRAINT PK_EB_ORDER_DETAIL PRIMARY KEY (ORDER_DETAIL_ID)
);

COMMENT ON TABLE EB_ORDER_DETAIL IS
'3-10是商品的冗余数据
11-26是营销案的冗余数据

营销案的时候,根据SKU存多条,每条SKU的营销案信息一致

备注:
当订单中包含offer,offer里包含sku的时候,
在保存offer内的sku信息时,会冗余offer信息到该条记录中,便于订单的查询。';

COMMENT ON COLUMN EB_ORDER_DETAIL.ORDER_ID IS
'订单主键';

COMMENT ON COLUMN EB_ORDER_DETAIL.ITEM_ID IS
'商品主键';

COMMENT ON COLUMN EB_ORDER_DETAIL.ITEM_NAME IS
'商品名称';

COMMENT ON COLUMN EB_ORDER_DETAIL.ITEM_NO IS
'商品编号:自动生成,不可重复,添加完成后不可修改。编号规则:一级目录数字“1”+7位数递增,裸机起始编号   10000001。7位数递增满时自动升为8位数。
编号根据EB_ITEM.CAT_TYPE获得,如实体商品为1(EB_ITEM.CAT_TYPE=1)

(删除
--实体起始编号:   10000001 虚拟起始编号:   20000001。
--注意:商品是否为手机或号卡,用其所属的类目来区分,CAT_ID为1的为手机,CAT_ID为2的为号卡。)
';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_ID IS
'最小销售单元主键';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU IS
'货号';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_IMG IS
'图片存储位置,1~5';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_NAME IS
'SKU名称';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_CAT_TYPE IS
'冗余EB_CAT里边的CAT_TYPE字段值,用以标示:0、不分类;1、实体;2、号卡;3、虚拟商品';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_SPEC IS
'规格1:规格2:规格3。。。规格N,格式参见订单需求文档';

COMMENT ON COLUMN EB_ORDER_DETAIL.MARKET_PRICE IS
'(以分币为单位)市场价:所有价格相关的字段限定为9位有效数字以内(即1234567.89,相当于千万元以内),给前端用户显示时,小数永远保持2位,即整数价格后面显示为.00;若小数价格多于2位小数,则直接截掉2位小数右边的小数(不用四舍五入);若小数为1位,如1元9角,则显示1.90';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_DISCOUNT IS
'优惠额';

COMMENT ON COLUMN EB_ORDER_DETAIL.SKU_PRICE IS
'sku销售价';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_GROUP_NAME IS
'活动名称';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_TYPE IS
'活动类型:1:购物送礼;2:优惠购机;3:购机返话费';

COMMENT ON COLUMN EB_ORDER_DETAIL.SHORT_NAME IS
'活动简称。用字母代表,如A1中的A';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_ID IS
'促销活动主键';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_NAME IS
'活动档次简介';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_NO IS
'活动档次编号:起始编号: 50000001';

COMMENT ON COLUMN EB_ORDER_DETAIL.SHORT_NAME2 IS
'活动档次数字编号,如A1中的1';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_TERM IS
'业务协议期:1:6个月;2:12个月;3:18个月;4:24个月';

COMMENT ON COLUMN EB_ORDER_DETAIL.COMMIT_MONTHLY IS
'(以分币为单位)月承诺消费金额。大于0小于10000的整数';

COMMENT ON COLUMN EB_ORDER_DETAIL.PREPAID IS
'(以分币为单位)预存话费金额。大于0小于10000的整数';

COMMENT ON COLUMN EB_ORDER_DETAIL.PERIOD IS
'返还期。1:立即到帐;2:6个月;3:12个月;4:18个月;5:24个月';

COMMENT ON COLUMN EB_ORDER_DETAIL.REFUND_MONTHLY IS
'(以分币为单位)月返还金额。大于0小于10000的整数';

COMMENT ON COLUMN EB_ORDER_DETAIL.REFUND_1ST_MONTH IS
'(以分币为单位)首月返还金额。大于0小于10000的整数';

COMMENT ON COLUMN EB_ORDER_DETAIL.REFUND_LAST_MONTH IS
'(以分币为单位)末月返还金额。大于0小于10000的整数';

COMMENT ON COLUMN EB_ORDER_DETAIL.ORDER_DETAIL_TYPE IS
'1、SKU;2、OFFER;3、营销案的SKU';

COMMENT ON COLUMN EB_ORDER_DETAIL.QUANTITY IS
'数量';

COMMENT ON COLUMN EB_ORDER_DETAIL.PRICE IS
'(以分币为单位)成交价格(即实际购买价格)。若是营销活动,则按公式sku_price+prepaid-sku_discount计算';

COMMENT ON COLUMN EB_ORDER_DETAIL.SERIESCODE IS
'串号,逗号分割';

COMMENT ON COLUMN EB_ORDER_DETAIL.OFFER_GROUP_NO IS
'活动编号,用来存储从crm过来的编号';

COMMENT ON COLUMN EB_ORDER_DETAIL.PROMO_TYPE IS
'活动类型,用来存储从crm返回的活动类型,在营销案开通接口中使用';

COMMENT ON COLUMN EB_ORDER_DETAIL.COND_ID IS
'活动档次编码,从crm传过来的';

我们对这两张表逆向工程!

order表

插入mapper的SQL语句、主键需要返回、一些属性可默认插入


<insert id="insert" parameterType="com.rl.ecps.model.EbOrder">
    <selectKey keyProperty="orderId" order="BEFORE" resultType="long">
      select seqorderid.nextval from dual
    </selectKey>
    insert into EB_ORDER (ORDER_ID, PTL_USER_ID, USERNAME,
    ORDER_NUM, PAYMENT, PAY_PLATFORM,
    DELIVERY, IS_CONFIRM, ORDER_SUM,
    SHIP_FEE, IS_PAID, ORDER_STATE,
    PAYMENT_CASH, DISTRI_ID, DELIVERY_METHOD,
    PAYMENT_NO, ORDER_TIME, PAY_TIME,
    DEPOSIT_TIME, SUCCESS_TIME, UPDATE_TIME,
    SRV_TYPE, SELF_COLLECT_SITE, SELF_COLLECT_SITE_ID,
    IS_DELETED, IS_DISPLAY, NOTES,
    SHIP_NAME, PROVINCE, CITY,
    DISTRICT, ZIP_CODE, ADDR,
    PHONE, PAYABLE, COMPANY,
    CONTENTS, IS_CALL, DELIVERY_NO,
    AREA_CODE, AREA_NAME, IS_PRINT,
    CRM_CALLS_TIME, IS_OFFER_RELEASE, JOB_NUM,
    ORDER_TYPE, FIXED_PHONE, ATTACH_FILE,
    RETURN_TYPE, SECKILL_ID, ORDER_SOURCE,
    ORDER_EXT1, ORDER_EXT2, ORDER_EXT3,
    ORDER_EXT4, ORDER_EXT5)
    values (#{orderId,jdbcType=DECIMAL}, #{ptlUserId,jdbcType=DECIMAL}, #{username,jdbcType=VARCHAR},
    #{orderNum,jdbcType=VARCHAR}, #{payment,jdbcType=DECIMAL}, #{payPlatform,jdbcType=DECIMAL},
    #{delivery,jdbcType=DECIMAL}, #{isConfirm,jdbcType=DECIMAL}, #{orderSum,jdbcType=DECIMAL},
    #{shipFee,jdbcType=DECIMAL}, 0, #{orderState,jdbcType=DECIMAL},
    #{paymentCash,jdbcType=DECIMAL}, #{distriId,jdbcType=DECIMAL}, #{deliveryMethod,jdbcType=DECIMAL},
    #{paymentNo,jdbcType=VARCHAR}, sysdate, #{payTime,jdbcType=TIMESTAMP},
    #{depositTime,jdbcType=TIMESTAMP}, #{successTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP},
    #{srvType,jdbcType=DECIMAL}, #{selfCollectSite,jdbcType=VARCHAR}, #{selfCollectSiteId,jdbcType=DECIMAL},
    #{isDeleted,jdbcType=DECIMAL}, #{isDisplay,jdbcType=DECIMAL}, #{notes,jdbcType=VARCHAR},
    #{shipName,jdbcType=VARCHAR}, #{province,jdbcType=VARCHAR}, #{city,jdbcType=VARCHAR},
    #{district,jdbcType=VARCHAR}, #{zipCode,jdbcType=VARCHAR}, #{addr,jdbcType=VARCHAR},
    #{phone,jdbcType=VARCHAR}, #{payable,jdbcType=DECIMAL}, #{company,jdbcType=VARCHAR},
    #{contents,jdbcType=DECIMAL}, 0, #{deliveryNo,jdbcType=VARCHAR},
    #{areaCode,jdbcType=VARCHAR}, #{areaName,jdbcType=VARCHAR}, #{isPrint,jdbcType=DECIMAL},
    #{crmCallsTime,jdbcType=TIMESTAMP}, #{isOfferRelease,jdbcType=DECIMAL}, #{jobNum,jdbcType=VARCHAR},
    #{orderType,jdbcType=DECIMAL}, #{fixedPhone,jdbcType=VARCHAR}, #{attachFile,jdbcType=VARCHAR},
    #{returnType,jdbcType=DECIMAL}, #{seckillId,jdbcType=DECIMAL}, #{orderSource,jdbcType=DECIMAL},
    #{orderExt1,jdbcType=VARCHAR}, #{orderExt2,jdbcType=VARCHAR}, #{orderExt3,jdbcType=VARCHAR},
    #{orderExt4,jdbcType=VARCHAR}, #{orderExt5,jdbcType=VARCHAR})
  </insert>

编写对应的Dao


/**
 * 继承SqlSessionDaoSupport能够得到sessionFactory的引用,非常方便!
        */
@Repository
public class EbOrderDaoImpl extends SqlSessionDaoSupport implements EbOrderDao {

    String nameSpace = "com.rl.ecps.sqlMap.EbOrderMapper.";

    public void saveOrder(EbOrder order) {
        this.getSqlSession().insert(nameSpace + "insert", order);
    }
}
orderDetail

orderDetail的主键是序列自动生成的;


seqorderdetailid.nextval,

编写Dao


public interface EbOrderDetailDao {

    /**
     * 这里保存的并不是List集合,因为考虑到了并发的问题,这里最好使用单个实体
     * 即时一个订单中有多个订单项,这里使用单个实体会方便一点!
     * 并且订单明细中还需要获取得出库存的数据、还要对库存进行操作。
     * @param orderDetail
     */
    void saveOrderDetail(EbOrderDetail orderDetail);

}

/**
 * 继承SqlSessionDaoSupport能够得到sessionFactory的引用,非常方便
 */
@Repository
public class EbOrderDetailDaoImpl extends SqlSessionDaoSupport implements EbOrderDetailDao {
    String nameSpace = "com.rl.ecps.sqlMap.EbOrderDetailMapper.";
    public void saveOrderDetail(EbOrderDetail orderDetail) {
        this.getSqlSession().insert(nameSpace + "insert", orderDetail);
    }
}
编写Service

public interface EbOrderService {

    /**
     * 因为在订单提交完毕后,我们是需要把购物车的数据清空的。
     * 将购物车的数据清空实际上就是清空cookie的数据!
     * 因此需要request和response对象!
     * @param ebOrder
     * @param details
     * @param request
     * @param response
     */
    void addOrder(EbOrder ebOrder, List<EbOrderDetail> details, HttpServletRequest request , HttpServletResponse response);

}

接下来就是获取页面的参数、将数据保存到Order和OrderDetail中了

Controller

    /**
     * 获取页面的参数、保存订单!
     * @param session
     * @param model
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("/SubmitOrder.do")
    public String SubmitOrder(HttpSession session, Model model, HttpServletRequest request,HttpServletResponse response,EbOrder order,String address) throws InvocationTargetException, IllegalAccessException {

        //设置订单的信息
        TsPtlUser user = (TsPtlUser) session.getAttribute("user");
        if(user != null){
            order.setPtlUserId(user.getPtlUserId());
            order.setUsername(user.getUsername());
        }
        order.setOrderNum(new SimpleDateFormat().format(new Date()));

        //收集地址分为两种:一种是新增,一种是使用原有的。
        //如果使用原有的,那么带过来的是id,我们可以直接获取对应的数据,封装到order对象中。
        if(!StringUtils.equals("add", address)){
            EbShipAddr addr = addrService.selectByPrimaryKey(new Long(address));
            BeanUtils.copyProperties(order, addr);
        }

        //订单明细和购物车清单的数据是一样的。
        List<EbOrderDetail> detailList = new ArrayList<EbOrderDetail>();

        List<EbCart> cartList = cartService.listCart(request, response);

        //遍历购物车的清单,将数据加入到订单明细中
        for(EbCart cart:cartList){
            EbOrderDetail detail = new EbOrderDetail();
            detail.setItemId(cart.getSku().getItem().getItemId());
            detail.setItemName(cart.getSku().getItem().getItemName());
            detail.setItemNo(cart.getSku().getItem().getItemNo());
            detail.setSkuId(cart.getSkuId());
            String specVal = "";
            List<EbSpecValue> specList = cart.getSku().getSpecList();
            for(EbSpecValue spec : specList){
                specVal = specVal + spec.getSpecValue()+",";
            }
            specVal = specVal.substring(0, specVal.length() - 1);
            detail.setSkuSpec(specVal);
            detail.setQuantity(cart.getQuantity());
            detail.setSkuPrice(cart.getSku().getSkuPrice());
            detail.setMarketPrice(cart.getSku().getMarketPrice());
            detailList.add(detail);
        }
        orderService.addOrder(order, detailList, request, response);
        model.addAttribute("order", order);

        return "shop/confirmProductCase2";
    }
Service流程步骤

用户提交了订单的话,那么我们要做什么事呢???

  • 将库存的数量减少
  • 清空购物车数据

库存的数据减少这里涉及到了并发的问题:

  • 如果两个用户同时提交订单的话,当前的库存是1,一个提交了订单,另一个应该是没货的。
  • 但如果我们不做任何操作的话,那么库存会减成是0
  • 这样明显是不合理的

于是我们就需要对这个操作“上锁”

锁有两种:

  • 乐观锁
    • 在修改数据的时候把事务锁起来,通过version的方式来进行锁定
  • 悲观锁
    • 在查询完数据的时候就把事务锁起来,直到提交事务

悲观锁:

乐观锁:

但其实我们现在这个操作并不需要用到悲观锁和乐观锁。因为我们查询出来的数据不用做另一番操作

  • 我们写一条SQL语句就能够搞掂了

  <update id="updateStock" parameterType="map">
    update eb_sku t  set t.stock_inventory = t.stock_inventory - #{quantity} where sku_id = #{skuId} 
    and t.stock_inventory > =#{quantity}
  </update>

service编写:

   public void addOrder(EbOrder ebOrder, List<EbOrderDetail> details, HttpServletRequest request, HttpServletResponse response) {

        orderDao.saveOrder(ebOrder);

        Map<String,Object> map = new HashMap<String,Object>();
        for(EbOrderDetail detail : details){
            detail.setOrderId(ebOrder.getOrderId());
            detailDao.saveOrderDetail(detail);

            /*EbSku sku = skuDao.getSkuById(detail.getSkuId());

            sku.setStockInventory(sku.getStockInventory() - detail.getQuantity());
            skuDao.update(sku);*/
            map.put("skuId", detail.getSkuId());
            map.put("quantity", detail.getQuantity());
            int flag = skuDao.updateStock(map);
            if(flag == 0){
                throw new EbStockException("库存不足");
            }

        }
        cartService.clearCar(request, response);
    }

走一遍流程:

总结
  • 我们将购物车的数据存储在Cookie中,那么即使用户不登陆也能查看得到购物车的数据
  • Cookie存储的是JSON值,可以通过JsonArray来对JSON和Java对象之间的转换。很好用!
  • 在添加商品到购物车中的时候分为三种情况
    • 购物车存在,加入的商品在购物车中也存在
    • 购物车存在,加入的商品在购物车中不存在
    • 购物车不存在。
  • 我们可以拿到Cookie来解决前两种情况,第三种情况可以根据Cookie转成Java集合时来对其判断。如果Cookie不存在(购物车不存在)、那么转成的Java集合一定为null。
  • 在Cookie保存的数据是skuId和quantity,由于在购物车中需要显示有关商品和价钱之类的数据。我们主要显示的价钱的数据,那么我们在设计购物车实体的时候就使用了Sku来关联Item的数据!
  • 当Java对象解析成Cookie的时候,是不用把我们的Sku属性算进去的。
  • 无论是增改查,都是将Cookie获取出来,转成是Java对象、对Java对象进行修改[添加],最后重写Cookie
  • 如果我们的数据是在后台传过去或者转换而成的,在前台上并没有做什么改变的话。那么我们可以直接使用后台的数据来对其进行操作。
    • 不要老想着全部操作都由前台传递参数来进行处理!
  • 查询出订单表所需要的数据、在页面上展示出来
  • OrderDetail在Dao保存的时候不再使用List来作为参数,而是直接使用OrderDetail实体来作为参数。因为考虑到了并发的问题,还需要OrderDetail的数据去操作库存。因此直接使用实体作为参数。
  • 在确认订单需要确认库存是否充足,在完成订单之后需要把购物车清空!
  • 悲观锁:在查询出数据之后就把事务锁起来
  • 客观锁:在修改数据的时候通过version将事务锁起来
  • 如果查询出来的数据没有被多次操作的话,那我们可以考虑是不是可以使用一条SQL语句就能够完成我们的任务了。
3人推荐
随时随地看视频
慕课网APP