diff --git a/core/api/src/main/java/com/wansenai/api/stock/BasStockAccountController.java b/core/api/src/main/java/com/wansenai/api/stock/BasStockAccountController.java new file mode 100644 index 0000000..efd0f80 --- /dev/null +++ b/core/api/src/main/java/com/wansenai/api/stock/BasStockAccountController.java @@ -0,0 +1,98 @@ +package com.wansenai.api.stock; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.wansenai.entities.stock.BasStockAccount; +import com.wansenai.service.stock.BasStockAccountService; +import com.wansenai.utils.response.Response; +import com.wansenai.vo.stock.StockAccountVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +/** + *

库存账接口

+ * @author clunt + */ +@Tag(name = "库存账接口") +@RestController +@RequestMapping("basStockAccount") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BasStockAccountController { + + private final BasStockAccountService stockAccountService; + + @Operation(summary = "获取库存列表") + @GetMapping("/list") + public Response> getStockList( + @Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page, + @Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize, + @Parameter(description = "仓库ID") @RequestParam(required = false) Long warehouseId, + @Parameter(description = "商品ID") @RequestParam(required = false) Long goodsId) { + + Page pageRequest = new Page<>(page, pageSize); + return Response.responseData(stockAccountService.getStockVOPage(pageRequest, warehouseId, goodsId)); + } + + @Operation(summary = "获取库存详情") + @GetMapping("/{id}") + public Response getStockById(@Parameter(description = "库存ID") @PathVariable Long id) { + return Response.responseData(stockAccountService.getStockDetailById(id)); + } + + @Operation(summary = "查询库存信息") + @GetMapping("/info") + public Response getStockInfo( + @Parameter(description = "仓库ID") @RequestParam Long warehouseId, + @Parameter(description = "商品ID") @RequestParam Long goodsId) { + return Response.responseData(stockAccountService.getStockInfo(warehouseId, goodsId)); + } + + @Operation(summary = "添加库存信息") + @PostMapping("/add") + public Response addStock(@RequestBody BasStockAccount stock) { + return Response.responseData(stockAccountService.addStock(stock)); + } + + @Operation(summary = "批量添加库存") + @PostMapping("/batch") + public Response batchAddStock(@RequestBody List stockList) { + return Response.responseData(stockAccountService.batchAddStock(stockList)); + } + + @Operation(summary = "更新库存信息") + @PutMapping("/update") + public Response updateStock(@RequestBody BasStockAccount stock) { + return Response.responseData(stockAccountService.updateStock(stock)); + } + + @Operation(summary = "更新库存数量") + @PutMapping("/qty/{id}") + public Response updateStockQty( + @Parameter(description = "库存ID") @PathVariable Long id, + @Parameter(description = "数量") @RequestParam BigDecimal qty) { + return Response.responseData(stockAccountService.updateStockQty(id, qty)); + } + + @Operation(summary = "更新锁定数量") + @PutMapping("/lock/{id}") + public Response updateLockQty( + @Parameter(description = "库存ID") @PathVariable Long id, + @Parameter(description = "锁定数量") @RequestParam BigDecimal qtyLock) { + return Response.responseData(stockAccountService.updateLockQty(id, qtyLock)); + } + + @Operation(summary = "更新在途数量") + @PutMapping("/tran/{id}") + public Response updateTranQty( + @Parameter(description = "库存ID") @PathVariable Long id, + @Parameter(description = "在途数量") @RequestParam BigDecimal qtyTran) { + return Response.responseData(stockAccountService.updateTranQty(id, qtyTran)); + } +} diff --git a/core/api/src/main/resources/application.yml b/core/api/src/main/resources/application.yml index f636075..3bfed4b 100644 --- a/core/api/src/main/resources/application.yml +++ b/core/api/src/main/resources/application.yml @@ -66,4 +66,7 @@ springdoc: - group: '商店模块' paths-to-match: '/**' packages-to-scan: com.wansenai.api.shop + - group: '库存账模块' + paths-to-match: '/**' + packages-to-scan: com.wansenai.api.stock diff --git a/core/dao/src/main/java/com/wansenai/mappers/stock/BasStockAccountMapper.java b/core/dao/src/main/java/com/wansenai/mappers/stock/BasStockAccountMapper.java new file mode 100644 index 0000000..411e4a9 --- /dev/null +++ b/core/dao/src/main/java/com/wansenai/mappers/stock/BasStockAccountMapper.java @@ -0,0 +1,42 @@ +package com.wansenai.mappers.stock; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.wansenai.entities.stock.BasStockAccount; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.List; + +@Mapper +public interface BasStockAccountMapper extends BaseMapper { + + /** + * 根据仓库ID和商品ID查询库存信息 + */ + BasStockAccount getStockByWarehouseAndGoods(@Param("warehouseId") Long warehouseId, + @Param("goodsId") Long goodsId); + + /** + * 更新库存数量 + */ + int updateStockQty(@Param("id") Long id, + @Param("qty") BigDecimal qty); + + /** + * 更新锁定数量 + */ + int updateLockQty(@Param("id") Long id, + @Param("qtyLock") BigDecimal qtyLock); + + /** + * 更新在途数量 + */ + int updateTranQty(@Param("id") Long id, + @Param("qtyTran") BigDecimal qtyTran); + + /** + * 批量插入库存信息 + */ + int batchInsert(@Param("stockList") List stockList); +} diff --git a/core/dao/src/main/resources/mapper_xml/stock/BasStockAccountMapper.xml b/core/dao/src/main/resources/mapper_xml/stock/BasStockAccountMapper.xml new file mode 100644 index 0000000..a4141a7 --- /dev/null +++ b/core/dao/src/main/resources/mapper_xml/stock/BasStockAccountMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + id, warehouse_id, warehouse_type_id, stock_type, goods_id, sku_id, + qty_lock, qty_tran, qty, qty_tran_out, qty_tran_in, + create_time, create_by, update_time, update_by, remark + + + + + + + + UPDATE bas_stock_account + SET qty = #{qty}, + update_time = NOW() + WHERE id = #{id} + + + + + UPDATE bas_stock_account + SET qty_lock = #{qtyLock}, + update_time = NOW() + WHERE id = #{id} + + + + + UPDATE bas_stock_account + SET qty_tran = #{qtyTran}, + update_time = NOW() + WHERE id = #{id} + + + + + INSERT INTO bas_stock_account ( + id, warehouse_id, warehouse_type_id, stock_type, goods_id, sku_id, + qty_lock, qty_tran, qty, qty_tran_out, qty_tran_in, + create_time, create_by, remark + ) VALUES + + ( + #{stock.id}, #{stock.warehouseId}, #{stock.warehouseTypeId}, + #{stock.stockType}, #{stock.goodsId}, #{stock.skuId}, + #{stock.qtyLock}, #{stock.qtyTran}, #{stock.qty}, + #{stock.qtyTranOut}, #{stock.qtyTranIn}, + #{stock.createTime}, #{stock.createBy}, #{stock.remark} + ) + + + + diff --git a/core/domain/src/main/java/com/wansenai/entities/stock/BasStockAccount.java b/core/domain/src/main/java/com/wansenai/entities/stock/BasStockAccount.java new file mode 100644 index 0000000..ce06bf9 --- /dev/null +++ b/core/domain/src/main/java/com/wansenai/entities/stock/BasStockAccount.java @@ -0,0 +1,89 @@ +package com.wansenai.entities.stock; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + *

库存账表

+ * @author clunt + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("bas_stock_account") +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "库存账表") +public class BasStockAccount implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long id; + + @Schema(description = "仓库ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long warehouseId; + + @Schema(description = "库区ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long warehouseTypeId; + + @Schema(description = "库存类型") + private String stockType; + + @Schema(description = "商品ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long goodsId; + + @Schema(description = "物料ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long skuId; + + @Schema(description = "锁定数") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal qtyLock; + + @Schema(description = "在途数") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal qtyTran; + + @Schema(description = "在库数") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal qty; + + @Schema(description = "发货在途数") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal qtyTranOut; + + @Schema(description = "收货在途数") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private BigDecimal qtyTranIn; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建人") + private String createBy; + + @Schema(description = "更新时间") + private Date updateTime; + + @Schema(description = "更新人") + private String updateBy; + + @Schema(description = "备注") + private String remark; + +} diff --git a/core/domain/src/main/java/com/wansenai/vo/product/ProductDetailVO.java b/core/domain/src/main/java/com/wansenai/vo/product/ProductDetailVO.java index b44106e..f52e813 100644 --- a/core/domain/src/main/java/com/wansenai/vo/product/ProductDetailVO.java +++ b/core/domain/src/main/java/com/wansenai/vo/product/ProductDetailVO.java @@ -15,12 +15,15 @@ package com.wansenai.vo.product; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.wansenai.bo.BigDecimalSerializerBO; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.math.BigDecimal; +import java.util.Date; import java.util.List; @Data +@Schema(description = "商品详情VO") public class ProductDetailVO { @JsonFormat(shape = JsonFormat.Shape.STRING) @@ -50,17 +53,7 @@ public class ProductDetailVO { private String productCategoryName; private String remark; - // - // TODO - // The @JsonFormat has been added here because the front-end is a select component, - // and the value values they bind correspond to string types of 0 and 1 - // - // Vue strictly compares the values bound to the v-model with the value attribute of the option, - // which means that their types and values must match exactly. - // - // So we did Json conversion here, but in fact, - // they still maintain the Integer type in the database and backend logic. - // + @JsonFormat(shape = JsonFormat.Shape.STRING) private Integer enableSerialNumber; @@ -82,4 +75,67 @@ public class ProductDetailVO { // Image list information private List imageList; + + // 新增字段 + @Schema(description = "商品ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long id; + + @Schema(description = "商品编号") + private String productNumber; + + @Schema(description = "商品规格") + private String standard; + + @Schema(description = "商品型号") + private String model; + + @Schema(description = "商品单位") + private String unit; + + @Schema(description = "商品分类ID") + @JsonFormat(shape = JsonFormat.Shape.STRING) + private Long categoryId; + + @Schema(description = "商品分类名称") + private String categoryName; + + @Schema(description = "零售价") + private BigDecimal retailPrice; + + @Schema(description = "最低售价") + private BigDecimal lowPrice; + + @Schema(description = "预设售价一") + private BigDecimal salePrice1; + + @Schema(description = "预设售价二") + private BigDecimal salePrice2; + + @Schema(description = "预设售价三") + private BigDecimal salePrice3; + + @Schema(description = "批发价") + private BigDecimal wholesalePrice; + + @Schema(description = "最低采购价") + private BigDecimal lowPurchasePrice; + + @Schema(description = "默认采购价") + private BigDecimal defaultPurchasePrice; + + @Schema(description = "状态") + private Integer status; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建人") + private String createBy; + + @Schema(description = "更新时间") + private Date updateTime; + + @Schema(description = "更新人") + private String updateBy; } diff --git a/core/service/src/main/java/com/wansenai/service/stock/BasStockAccountService.java b/core/service/src/main/java/com/wansenai/service/stock/BasStockAccountService.java new file mode 100644 index 0000000..e1118bd --- /dev/null +++ b/core/service/src/main/java/com/wansenai/service/stock/BasStockAccountService.java @@ -0,0 +1,58 @@ +package com.wansenai.service.stock; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.wansenai.entities.stock.BasStockAccount; +import com.wansenai.vo.stock.StockAccountVO; + +import java.math.BigDecimal; +import java.util.List; + +public interface BasStockAccountService extends IService { + + /** + * 查询库存信息 + */ + BasStockAccount getStockInfo(Long warehouseId, Long goodsId); + + /** + * 更新库存数量 + */ + boolean updateStockQty(Long id, BigDecimal qty); + + /** + * 更新锁定数量 + */ + boolean updateLockQty(Long id, BigDecimal qtyLock); + + /** + * 更新在途数量 + */ + boolean updateTranQty(Long id, BigDecimal qtyTran); + + /** + * 批量添加库存信息 + */ + boolean batchAddStock(List stockList); + + /** + * 添加库存信息 + */ + boolean addStock(BasStockAccount stock); + + /** + * 更新库存信息 + */ + boolean updateStock(BasStockAccount stock); + + /** + * 获取库存详情(包含关联信息) + */ + StockAccountVO getStockDetailById(Long id); + + /** + * 分页查询库存列表(包含关联信息) + */ + IPage getStockVOPage(Page page, Long warehouseId, Long goodsId); +} diff --git a/core/service/src/main/java/com/wansenai/service/stock/impl/BasStockAccountServiceImpl.java b/core/service/src/main/java/com/wansenai/service/stock/impl/BasStockAccountServiceImpl.java new file mode 100644 index 0000000..98e3414 --- /dev/null +++ b/core/service/src/main/java/com/wansenai/service/stock/impl/BasStockAccountServiceImpl.java @@ -0,0 +1,142 @@ +package com.wansenai.service.stock.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.wansenai.entities.stock.BasStockAccount; +import com.wansenai.entities.warehouse.Warehouse; +import com.wansenai.mappers.stock.BasStockAccountMapper; +import com.wansenai.mappers.warehouse.WarehouseMapper; +import com.wansenai.service.product.ProductService; +import com.wansenai.service.stock.BasStockAccountService; +import com.wansenai.service.user.ISysUserService; +import com.wansenai.utils.SnowflakeIdUtil; +import com.wansenai.vo.product.ProductDetailVO; +import com.wansenai.vo.stock.StockAccountVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BasStockAccountServiceImpl extends ServiceImpl implements BasStockAccountService { + + private final ISysUserService sysUserService; + private final WarehouseMapper warehouseMapper; + private final ProductService productService; + + @Override + public BasStockAccount getStockInfo(Long warehouseId, Long goodsId) { + return baseMapper.getStockByWarehouseAndGoods(warehouseId, goodsId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStockQty(Long id, BigDecimal qty) { + return baseMapper.updateStockQty(id, qty) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateLockQty(Long id, BigDecimal qtyLock) { + return baseMapper.updateLockQty(id, qtyLock) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateTranQty(Long id, BigDecimal qtyTran) { + return baseMapper.updateTranQty(id, qtyTran) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean batchAddStock(List stockList) { + if (CollectionUtils.isEmpty(stockList)) { + return false; + } + + Date now = new Date(); + String currentUser = sysUserService.getCurrentUserName(); + stockList.forEach(stock -> { + stock.setId(SnowflakeIdUtil.nextId()); + stock.setCreateTime(now); + stock.setCreateBy(currentUser); + }); + + return baseMapper.batchInsert(stockList) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addStock(BasStockAccount stock) { + stock.setId(SnowflakeIdUtil.nextId()); + stock.setCreateTime(new Date()); + stock.setCreateBy(sysUserService.getCurrentUserName()); + return save(stock); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateStock(BasStockAccount stock) { + stock.setUpdateTime(new Date()); + stock.setUpdateBy(sysUserService.getCurrentUserName()); + return updateById(stock); + } + + @Override + public StockAccountVO getStockDetailById(Long id) { + BasStockAccount stock = getById(id); + if (stock == null) { + return null; + } + + return convertToVO(stock); + } + + @Override + public IPage getStockVOPage(Page page, Long warehouseId, Long goodsId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(warehouseId != null, BasStockAccount::getWarehouseId, warehouseId) + .eq(goodsId != null, BasStockAccount::getGoodsId, goodsId); + + Page stockPage = page(page, queryWrapper); + + return stockPage.convert(this::convertToVO); + } + + private StockAccountVO convertToVO(BasStockAccount stock) { + StockAccountVO vo = new StockAccountVO(); + BeanUtils.copyProperties(stock, vo); + + // 获取仓库信息 + if (stock.getWarehouseId() != null) { + Warehouse warehouse = warehouseMapper.selectById(stock.getWarehouseId()); + if (warehouse != null) { + vo.setWarehouseName(warehouse.getWarehouseName()); + } + } + + // 获取商品信息 + if (stock.getGoodsId() != null) { + ProductDetailVO product = productService.getProductInfoDetail(stock.getGoodsId()).getData(); + if (product != null) { + vo.setGoodsName(product.getProductName()); + vo.setGoodsStandard(product.getStandard()); + vo.setGoodsModel(product.getModel()); + vo.setGoodsUnit(product.getUnit()); + } + } + + return vo; + } +}