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;
+ }
+}