{"record":{"id":"5613e2f67359a7b5","repo":"linlinjava/litemall","slug":"error","errorCode":null,"errorMessage":"下单的商品货品数量大于库存量","messagePattern":"下单的商品货品数量大于库存量","errorType":"exception","errorClass":"RuntimeException","httpStatus":500,"severity":"error","filePath":"litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java","lineNumber":432,"sourceCode":"\n            orderGoodsService.add(orderGoods);\n        }\n\n        // 删除购物车里面的商品信息\n        if(cartId.equals(0)){\n            cartService.clearGoods(userId);\n        }else{\n            cartService.deleteById(cartId);\n        }\n\n        // 商品货品数量减少\n        for (LitemallCart checkGoods : checkedGoodsList) {\n            Integer productId = checkGoods.getProductId();\n            LitemallGoodsProduct product = productService.findById(productId);\n\n            int remainNumber = product.getNumber() - checkGoods.getNumber();\n            if (remainNumber < 0) {\n                throw new RuntimeException(\"下单的商品货品数量大于库存量\");\n            }\n            if (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {\n                throw new RuntimeException(\"商品货品库存减少失败\");\n            }\n        }\n\n        // 如果使用了优惠券，设置优惠券使用状态\n        if (couponId != 0 && couponId != -1) {\n            LitemallCouponUser couponUser = couponUserService.findById(userCouponId);\n            couponUser.setStatus(CouponUserConstant.STATUS_USED);\n            couponUser.setUsedTime(LocalDateTime.now());\n            couponUser.setOrderId(orderId);\n            couponUserService.update(couponUser);\n        }\n\n        //如果是团购项目，添加团购信息\n        if (grouponRulesId != null && grouponRulesId > 0) {\n            LitemallGroupon groupon = new LitemallGroupon();","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/linlinjava/litemall/blob/a1ef964a718b7277925b19ea26afe78ea3a1d325/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java#L414-L450","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nint remainNumber = product.getNumber() - checkGoods.getNumber();\nif (remainNumber < 0) {\n    throw new RuntimeException(\"下单的商品货品数量大于库存量\");\n}\nif (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {\n    throw new RuntimeException(\"商品货品库存减少失败\");\n}\n\n// after - atomic conditional decrement, single source of truth\nif (productService.reduceStock(productId, checkGoods.getNumber()) == 0) {\n    throw new RuntimeException(\"下单的商品货品数量大于库存量\");\n}\n// mapper: UPDATE litemall_goods_product SET number = number - #{num} WHERE id = #{id} AND number >= #{num} AND deleted = false","handlingStrategy":"validation","validationCode":"// before submit, verify every checked cart item against live stock\nfor (LitemallCart cartItem : cartService.queryByUid(userId)) {\n    if (cartItem.getChecked() == null || cartItem.getChecked() != 1) continue;\n    LitemallGoodsProduct p = productService.findById(cartItem.getProductId());\n    if (p == null || p.getNumber() < cartItem.getNumber()) {\n        return ResponseUtil.fail(GOODS_OUT_OF_STOCK, \"商品\" + cartItem.getGoodsName() + \"库存不足\");\n    }\n}","typeGuard":null,"tryCatchPattern":"// in WxOrderController.submit\ncatch (RuntimeException e) {\n    if (e.getMessage().contains(\"库存\")) {\n        return ResponseUtil.fail(GOODS_OUT_OF_STOCK, \"商品库存不足，请调整购物车\");\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["business-rule","stock","order","concurrency","litemall"],"backgroundTag":null,"analyzedSha":"a1ef964a718b7277925b19ea26afe78ea3a1d325","analyzedAt":"2026-08-14T12:39:46.078Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}