linlinjava/litemall · error · RuntimeException

下单的商品货品数量大于库存量

Error message

下单的商品货品数量大于库存量

What it means

During order submission in WxOrderService.submit, after the checked cart goods are collected the service iterates each item and computes remainNumber = product.getNumber() - checkGoods.getNumber(). If the requested quantity exceeds the current stock of that SKU (litemall_goods_product.number), remainNumber goes negative and the whole submission is aborted with RuntimeException '下单的商品货品数量大于库存量' (order quantity exceeds stock). This is a business-rule guard, not a system fault — the DB is never touched for stock before this check.

Source

Thrown at litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java:432

            orderGoodsService.add(orderGoods);
        }

        // 删除购物车里面的商品信息
        if(cartId.equals(0)){
            cartService.clearGoods(userId);
        }else{
            cartService.deleteById(cartId);
        }

        // 商品货品数量减少
        for (LitemallCart checkGoods : checkedGoodsList) {
            Integer productId = checkGoods.getProductId();
            LitemallGoodsProduct product = productService.findById(productId);

            int remainNumber = product.getNumber() - checkGoods.getNumber();
            if (remainNumber < 0) {
                throw new RuntimeException("下单的商品货品数量大于库存量");
            }
            if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {
                throw new RuntimeException("商品货品库存减少失败");
            }
        }

        // 如果使用了优惠券,设置优惠券使用状态
        if (couponId != 0 && couponId != -1) {
            LitemallCouponUser couponUser = couponUserService.findById(userCouponId);
            couponUser.setStatus(CouponUserConstant.STATUS_USED);
            couponUser.setUsedTime(LocalDateTime.now());
            couponUser.setOrderId(orderId);
            couponUserService.update(couponUser);
        }

        //如果是团购项目,添加团购信息
        if (grouponRulesId != null && grouponRulesId > 0) {
            LitemallGroupon groupon = new LitemallGroupon();

View on GitHub (pinned to a1ef964a71)

Solutions

  1. Surface the error to the user as an out-of-stock message: catch it in WxOrderController and return ResponseUtil.fail with a goods-unavailable code so the mini-program prompts the user to adjust the cart.
  2. Before submit, re-query stock for each cart item and disable/flag entries where quantity > stock (frontend refresh of cart).
  3. For concurrent races, make the stock check and decrement atomic: rely on a conditional UPDATE (reduceStock with a 'number >= ?' WHERE clause) and treat 0 updated rows as the out-of-stock signal instead of a pre-read.

Example fix

// before
int remainNumber = product.getNumber() - checkGoods.getNumber();
if (remainNumber < 0) {
    throw new RuntimeException("下单的商品货品数量大于库存量");
}
if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {
    throw new RuntimeException("商品货品库存减少失败");
}

// after - atomic conditional decrement, single source of truth
if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {
    throw new RuntimeException("下单的商品货品数量大于库存量");
}
// mapper: UPDATE litemall_goods_product SET number = number - #{num} WHERE id = #{id} AND number >= #{num} AND deleted = false
Defensive patterns

Strategy: validation

Validate before calling

// before submit, verify every checked cart item against live stock
for (LitemallCart cartItem : cartService.queryByUid(userId)) {
    if (cartItem.getChecked() == null || cartItem.getChecked() != 1) continue;
    LitemallGoodsProduct p = productService.findById(cartItem.getProductId());
    if (p == null || p.getNumber() < cartItem.getNumber()) {
        return ResponseUtil.fail(GOODS_OUT_OF_STOCK, "商品" + cartItem.getGoodsName() + "库存不足");
    }
}

Try / catch

// in WxOrderController.submit
catch (RuntimeException e) {
    if (e.getMessage().contains("库存")) {
        return ResponseUtil.fail(GOODS_OUT_OF_STOCK, "商品库存不足,请调整购物车");
    }
    throw e;
}

Prevention

When it happens

Trigger: User checks out a cart containing more units of a product SKU than litemall_goods_product.number currently holds: concurrent buyers racing for the last stock, stock reduced by an admin after the item sat in the cart, or a stale mini-program page showing an old 'x件有货' count. Note the check runs inside the submit loop before reduceStock(), so any single offending SKU aborts the entire order.

Common situations: Flash-sale/groupon scenarios where two users submit near-simultaneously; cart holds an item whose stock was lowered or sold out later; product re-indexed/off-shelf between add-to-cart and checkout; testing with seeded stock values smaller than cart quantities.

Related errors


AI-assisted analysis of linlinjava/litemall@a1ef964a71 (2026-08-14). Data as JSON: /api/errors/5613e2f67359a7b5. Report an issue: GitHub.