Compare commits

...

2 Commits

Author SHA1 Message Date
Yifei Kuang 7e1b941f39 feat: 1.库存账接口 2025-01-06 15:46:16 +08:00
Yifei Kuang dfc3b833a8 feat: 1.库存账接口 2025-01-06 15:46:11 +08:00
9 changed files with 662 additions and 11 deletions

View File

@ -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;
/**
* <p>库存账接口</p>
* @author clunt
*/
@Tag(name = "库存账接口")
@RestController
@RequestMapping("basStockAccount")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BasStockAccountController {
private final BasStockAccountService stockAccountService;
@Operation(summary = "获取库存列表")
@GetMapping("/list")
public Response<IPage<StockAccountVO>> 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<BasStockAccount> pageRequest = new Page<>(page, pageSize);
return Response.responseData(stockAccountService.getStockVOPage(pageRequest, warehouseId, goodsId));
}
@Operation(summary = "获取库存详情")
@GetMapping("/{id}")
public Response<StockAccountVO> getStockById(@Parameter(description = "库存ID") @PathVariable Long id) {
return Response.responseData(stockAccountService.getStockDetailById(id));
}
@Operation(summary = "查询库存信息")
@GetMapping("/info")
public Response<BasStockAccount> 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<Boolean> addStock(@RequestBody BasStockAccount stock) {
return Response.responseData(stockAccountService.addStock(stock));
}
@Operation(summary = "批量添加库存")
@PostMapping("/batch")
public Response<Boolean> batchAddStock(@RequestBody List<BasStockAccount> stockList) {
return Response.responseData(stockAccountService.batchAddStock(stockList));
}
@Operation(summary = "更新库存信息")
@PutMapping("/update")
public Response<Boolean> updateStock(@RequestBody BasStockAccount stock) {
return Response.responseData(stockAccountService.updateStock(stock));
}
@Operation(summary = "更新库存数量")
@PutMapping("/qty/{id}")
public Response<Boolean> 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<Boolean> 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<Boolean> updateTranQty(
@Parameter(description = "库存ID") @PathVariable Long id,
@Parameter(description = "在途数量") @RequestParam BigDecimal qtyTran) {
return Response.responseData(stockAccountService.updateTranQty(id, qtyTran));
}
}

View File

@ -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

View File

@ -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<BasStockAccount> {
/**
* 根据仓库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<BasStockAccount> stockList);
}

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wansenai.mappers.stock.BasStockAccountMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.wansenai.entities.stock.BasStockAccount">
<id column="id" property="id"/>
<result column="warehouse_id" property="warehouseId"/>
<result column="warehouse_type_id" property="warehouseTypeId"/>
<result column="stock_type" property="stockType"/>
<result column="goods_id" property="goodsId"/>
<result column="sku_id" property="skuId"/>
<result column="qty_lock" property="qtyLock"/>
<result column="qty_tran" property="qtyTran"/>
<result column="qty" property="qty"/>
<result column="qty_tran_out" property="qtyTranOut"/>
<result column="qty_tran_in" property="qtyTranIn"/>
<result column="create_time" property="createTime"/>
<result column="create_by" property="createBy"/>
<result column="update_time" property="updateTime"/>
<result column="update_by" property="updateBy"/>
<result column="remark" property="remark"/>
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
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
</sql>
<!-- 根据仓库ID和商品ID查询库存信息 -->
<select id="getStockByWarehouseAndGoods" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM bas_stock_account
WHERE warehouse_id = #{warehouseId} AND goods_id = #{goodsId}
</select>
<!-- 更新库存数量 -->
<update id="updateStockQty">
UPDATE bas_stock_account
SET qty = #{qty},
update_time = NOW()
WHERE id = #{id}
</update>
<!-- 更新锁定数量 -->
<update id="updateLockQty">
UPDATE bas_stock_account
SET qty_lock = #{qtyLock},
update_time = NOW()
WHERE id = #{id}
</update>
<!-- 更新在途数量 -->
<update id="updateTranQty">
UPDATE bas_stock_account
SET qty_tran = #{qtyTran},
update_time = NOW()
WHERE id = #{id}
</update>
<!-- 批量插入库存信息 -->
<insert id="batchInsert">
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
<foreach collection="stockList" item="stock" separator=",">
(
#{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}
)
</foreach>
</insert>
</mapper>

View File

@ -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;
/**
* <p>库存账表</p>
* @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;
}

View File

@ -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<ProductImageVO> 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;
}

View File

@ -0,0 +1,81 @@
package com.wansenai.vo.stock;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@Schema(description = "库存账VO")
public class StockAccountVO {
@Schema(description = "主键ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(description = "仓库ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long warehouseId;
@Schema(description = "仓库名称")
private String warehouseName;
@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 = "商品名称")
private String goodsName;
@Schema(description = "商品规格")
private String goodsStandard;
@Schema(description = "商品型号")
private String goodsModel;
@Schema(description = "商品单位")
private String goodsUnit;
@Schema(description = "物料ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long skuId;
@Schema(description = "锁定数")
private BigDecimal qtyLock;
@Schema(description = "在途数")
private BigDecimal qtyTran;
@Schema(description = "在库数")
private BigDecimal qty;
@Schema(description = "发货在途数")
private BigDecimal qtyTranOut;
@Schema(description = "收货在途数")
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;
}

View File

@ -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> {
/**
* 查询库存信息
*/
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<BasStockAccount> stockList);
/**
* 添加库存信息
*/
boolean addStock(BasStockAccount stock);
/**
* 更新库存信息
*/
boolean updateStock(BasStockAccount stock);
/**
* 获取库存详情(包含关联信息)
*/
StockAccountVO getStockDetailById(Long id);
/**
* 分页查询库存列表(包含关联信息)
*/
IPage<StockAccountVO> getStockVOPage(Page<BasStockAccount> page, Long warehouseId, Long goodsId);
}

View File

@ -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<BasStockAccountMapper, BasStockAccount> 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<BasStockAccount> 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<StockAccountVO> getStockVOPage(Page<BasStockAccount> page, Long warehouseId, Long goodsId) {
LambdaQueryWrapper<BasStockAccount> queryWrapper = new LambdaQueryWrapper<BasStockAccount>()
.eq(warehouseId != null, BasStockAccount::getWarehouseId, warehouseId)
.eq(goodsId != null, BasStockAccount::getGoodsId, goodsId);
Page<BasStockAccount> 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;
}
}