You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring Boot的商品库存扣减更新接口实现咨询

实现库存扣减的REST接口

一、PUT方法:通过请求体扣减指定商品库存

1. 修改Service接口(ProductoService.java)

新增库存扣减方法,拆分职责更清晰:

package com.coder.billing.services.Producto;

import com.coder.billing.models.Producto;

import java.util.List;

public interface ProductoService {
    // 原有方法保留
    List<Producto> getAllProductos();
    Producto getOneProduct(Long id);
    Producto createProduct(Producto producto);
    Producto updateProduct(Producto producto, Long id);
    void deleteProduct(Long id);

    // 新增库存扣减方法
    Producto deductStock(Long id, Integer cantidadProducto);
}

2. 实现Service方法(ProductoServiceImpl.java)

实现扣减逻辑,同时校验库存充足性:

package com.coder.billing.services.Producto;

import com.coder.billing.models.Producto;
import com.coder.billing.repositories.Producto.ProductoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class ProductoServiceImp implements ProductoService {

    @Autowired
    ProductoRepository productoRepository;

    // 原有方法保留
    public List<Producto> getAllProductos() {
        return productoRepository.findAll();
    }

    public Producto getOneProduct(Long id) {
        return productoRepository.findById(id).orElseThrow(() -> new RuntimeException("Producto no encontrado"));
    }

    public Producto createProduct(Producto producto) {
        return productoRepository.save(producto);
    }

    public Producto updateProduct(Producto producto, Long id) {
        Producto productoActual = productoRepository.findById(id).orElseThrow(() -> new RuntimeException("Producto no encontrado"));
        productoActual.setIdProducto(id);
        productoActual.setNombreProducto(producto.getNombreProducto());
        productoActual.setPrecioProducto(producto.getPrecioProducto());
        productoActual.setDescripcion(producto.getDescripcion());
        productoActual.setStock(producto.getStock());
        return productoRepository.save(productoActual);
    }

    public void deleteProduct(Long id) {
        productoRepository.deleteById(id);
    }

    // 实现库存扣减,添加事务保证原子性
    @Transactional
    @Override
    public Producto deductStock(Long id, Integer cantidadProducto) {
        Producto producto = productoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Producto no encontrado"));
        
        if (producto.getStock() < cantidadProducto) {
            throw new RuntimeException("库存不足,当前库存: " + producto.getStock());
        }
        
        producto.setStock(producto.getStock() - cantidadProducto);
        return productoRepository.save(producto);
    }
}

3. 修改Controller的PUT接口(ProductoController.java)

让PUT接口支持两种场景:全量更新商品信息,或仅扣减库存:

@PutMapping("/{id}")
public ResponseEntity<ProductoDTO> updateProduct(@PathVariable Long id, @RequestBody ProductoDTO productoDTO) {
    Producto productoActual;
    // 若请求体包含有效扣减数量,执行库存扣减逻辑
    if (productoDTO.getCantidadProducto() != null && productoDTO.getCantidadProducto() > 0) {
        productoActual = productoService.deductStock(id, productoDTO.getCantidadProducto());
    } else {
        // 原有全量更新逻辑
        Producto productoPeticion = modelMapper.map(productoDTO, Producto.class);
        productoActual = productoService.updateProduct(productoPeticion, id);
    }
    
    ProductoDTO productoDTORespuesta = modelMapper.map(productoActual, ProductoDTO.class);
    return ResponseEntity.ok(productoDTORespuesta);
}

二、POST方法:传入商品数量实现库存扣减

如果需要独立的库存扣减接口(比如用于出库操作),可以新增POST接口:

1. (可选)创建专用DTO(StockDeductDTO.java)

用更精简的DTO传递参数:

package com.coder.billing.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StockDeductDTO {
    private Integer cantidadProducto;
}

2. 在Controller中新增POST接口

@PostMapping("/{id}/deduct-stock")
public ResponseEntity<ProductoDTO> deductStock(@PathVariable Long id, @RequestBody StockDeductDTO stockDeductDTO) {
    if (stockDeductDTO.getCantidadProducto() == null || stockDeductDTO.getCantidadProducto() <= 0) {
        throw new RuntimeException("请传入有效的扣减数量");
    }
    Producto producto = productoService.deductStock(id, stockDeductDTO.getCantidadProducto());
    ProductoDTO productoDTO = modelMapper.map(producto, ProductoDTO.class);
    return ResponseEntity.ok(productoDTO);
}

若不想新增DTO,也可直接复用ProductoDTO:

@PostMapping("/{id}/deduct-stock")
public ResponseEntity<ProductoDTO> deductStock(@PathVariable Long id, @RequestBody ProductoDTO productoDTO) {
    if (productoDTO.getCantidadProducto() == null || productoDTO.getCantidadProducto() <= 0) {
        throw new RuntimeException("请传入有效的扣减数量");
    }
    Producto producto = productoService.deductStock(id, productoDTO.getCantidadProducto());
    ProductoDTO productoDTORespuesta = modelMapper.map(producto, ProductoDTO.class);
    return ResponseEntity.ok(productoDTORespuesta);
}

三、请求示例

PUT请求(更新商品+扣减库存)

PUT /api/productos/1
Content-Type: application/json

{
    "nombreProducto": "更新后的商品名称",
    "cantidadProducto": 5
}

POST请求(仅扣减库存)

POST /api/productos/1/deduct-stock
Content-Type: application/json

{
    "cantidadProducto": 3
}

四、优化建议

  • 异常处理:自定义异常类替代RuntimeException,搭配@ExceptionHandler统一返回标准化错误响应(比如库存不足返回400,商品不存在返回404)。
  • 并发控制:高并发场景下,可在库存扣减逻辑中添加乐观锁(比如在Producto实体添加@Version字段),避免超卖。

内容的提问来源于stack exchange,提问作者Nacho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:48:23