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
- 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.
- Before submit, re-query stock for each cart item and disable/flag entries where quantity > stock (frontend refresh of cart).
- 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
- Re-check stock when rendering the checkout page, not only at add-to-cart time.
- Treat a conditional reduceStock rowcount of 0 as the authoritative out-of-stock signal so the pre-read check is advisory only.
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.