{"record":{"id":"a7d6b165a3fd2274","repo":"linlinjava/litemall","slug":"error-a7d6b1","errorCode":null,"errorMessage":"商品货品库存增加失败","messagePattern":"商品货品库存增加失败","errorType":"http","errorClass":"RuntimeException","httpStatus":500,"severity":"error","filePath":"litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java","lineNumber":587,"sourceCode":"        OrderHandleOption handleOption = OrderUtil.build(order);\n        if (!handleOption.isCancel()) {\n            return ResponseUtil.fail(ORDER_INVALID_OPERATION, \"订单不能取消\");\n        }\n\n        // 设置订单已取消状态\n        order.setOrderStatus(OrderUtil.STATUS_CANCEL);\n        order.setEndTime(LocalDateTime.now());\n        if (orderService.updateWithOptimisticLocker(order) == 0) {\n            throw new RuntimeException(\"更新数据已失效\");\n        }\n\n        // 商品货品数量增加\n        List<LitemallOrderGoods> orderGoodsList = orderGoodsService.queryByOid(orderId);\n        for (LitemallOrderGoods orderGoods : orderGoodsList) {\n            Integer productId = orderGoods.getProductId();\n            Short number = orderGoods.getNumber();\n            if (productService.addStock(productId, number) == 0) {\n                throw new RuntimeException(\"商品货品库存增加失败\");\n            }\n        }\n\n        // 返还优惠券\n        releaseCoupon(orderId);\n\n        return ResponseUtil.ok();\n    }\n\n    /**\n     * 付款订单的预支付会话标识\n     * <p>\n     * 1. 检测当前订单是否能够付款\n     * 2. 微信商户平台返回支付订单ID\n     * 3. 设置订单付款状态\n     *\n     * @param userId 用户ID\n     * @param body   订单信息，{ orderId：xxx }","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/linlinjava/litemall/blob/a1ef964a718b7277925b19ea26afe78ea3a1d325/litemall-wx-api/src/main/java/org/linlinjava/litemall/wx/service/WxOrderService.java#L569-L605","documentation":"In WxOrderService.cancel, after the order is successfully flipped to STATUS_CANCEL, the service restores inventory by calling productService.addStock(productId, number) for each order-goods row. addStock maps to a conditional SQL UPDATE (litemall_goods_product SET number = number + ?); 0 affected rows means the product row was not matched — typically the SKU was logically deleted or its id no longer resolves — and the service throws '商品货品库存增加失败' (stock restore failed). Because this runs after the status update within the same flow, the surrounding transaction must roll everything back, leaving the order un-cancelled.","triggerScenarios":"An order contains a product SKU that has since been logically deleted (deleted=1) or hard-removed by an admin; the order_goods row references a productId that no longer exists in litemall_goods_product (data drift from manual edits/imports). The cancel itself succeeds but the very first addStock UPDATE matches 0 rows.","commonSituations":"Admin deletes obsolete SKUs while old unshipped orders for them still exist; store data migrated/pruned without preserving product ids; test databases with order_goods rows referencing seeded products that were wiped.","solutions":["Check whether the 0-row result is due to logical delete of the SKU: if the row is gone for good, decide business-wise whether to skip restoring stock for that line (log it) instead of failing the whole cancel.","If the cancel must succeed, wrap addStock per-item, log failures with productId, and continue — stock drift on a dead SKU is usually acceptable; order state consistency is not.","Investigate the data: SELECT deleted, number FROM litemall_goods_product WHERE id=<productId> for each order_goods row of the failing order; fix orphaned references or re-create the SKU row."],"exampleFix":"// before\nif (productService.addStock(productId, number) == 0) {\n    throw new RuntimeException(\"商品货品库存增加失败\");\n}\n\n// after - log and continue so cancellation is not blocked by a dead SKU\nif (productService.addStock(productId, number) == 0) {\n    logger.error(\"库存返还失败, 货品不存在或已删除, productId={}, orderId={}\", productId, orderId);\n}","handlingStrategy":"fallback","validationCode":"// optional pre-check: identify dead SKUs before cancelling\nList<LitemallOrderGoods> goods = orderGoodsService.queryByOid(orderId);\nfor (LitemallOrderGoods og : goods) {\n    if (productService.findById(og.getProductId()) == null) {\n        logger.warn(\"订单{}包含已删除货品{}，取消时将跳过库存返还\", orderId, og.getProductId());\n    }\n}","typeGuard":null,"tryCatchPattern":"for (LitemallOrderGoods orderGoods : orderGoodsList) {\n    if (productService.addStock(orderGoods.getProductId(), orderGoods.getNumber()) == 0) {\n        // dead SKU: log and continue — do not block the cancellation\n        logger.error(\"库存返还失败 productId={} orderId={}\", orderGoods.getProductId(), orderId);\n    }\n}","preventionTips":["Decide explicitly whether a missing SKU should block cancellation; in most domains order-state consistency outranks stock restoration for deleted products.","Never hard-delete product rows that historical orders reference — use the logical deleted flag the conditional UPDATE already respects."],"tags":["stock","order","cancel","data-integrity","database","litemall"],"backgroundTag":null,"analyzedSha":"a1ef964a718b7277925b19ea26afe78ea3a1d325","analyzedAt":"2026-08-14T12:39:46.078Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}