商品库存流水

This commit is contained in:
Tankaikai 2025-03-13 16:50:51 +08:00
parent b3a725556d
commit 8cec1a7a4c
5 changed files with 201 additions and 26 deletions

View File

@ -9,10 +9,8 @@ import com.czg.log.annotation.OperationLog;
import com.czg.product.dto.ProdConsBindDTO;
import com.czg.product.dto.ProdSkuDTO;
import com.czg.product.dto.ProductDTO;
import com.czg.product.param.ProdRefundToStockParam;
import com.czg.product.param.ProductIsSaleParam;
import com.czg.product.param.ProductIsSoldOutParam;
import com.czg.product.param.ProductReportDamageParam;
import com.czg.product.entity.ProductStockFlow;
import com.czg.product.param.*;
import com.czg.product.service.ProdConsRelationService;
import com.czg.product.service.ProductService;
import com.czg.resp.CzgResult;
@ -31,7 +29,7 @@ import java.util.List;
/**
* 商品
* 商品管理 - 商品列表
*
* @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-16
@ -44,6 +42,9 @@ public class ProductController {
private final ProdConsRelationService prodConsRelationService;
private final RabbitPublisher rabbitPublisher;
/**
* 商品-分页
*/
@GetMapping("page")
@OperationLog("商品-分页")
//@SaAdminCheckPermission("product:page")
@ -52,6 +53,9 @@ public class ProductController {
return CzgResult.success(data);
}
/**
* 商品-列表
*/
@GetMapping("list")
@OperationLog("商品-列表")
//@SaAdminCheckPermission("product:list")
@ -60,6 +64,9 @@ public class ProductController {
return CzgResult.success(data);
}
/**
* 商品-详情
*/
@GetMapping("{id}")
@OperationLog("商品-详情")
//@SaAdminCheckPermission("product:info")
@ -69,6 +76,9 @@ public class ProductController {
return CzgResult.success(data);
}
/**
* 商品-新增
*/
@PostMapping
@OperationLog("商品-新增")
//@SaAdminCheckPermission("product:add")
@ -86,6 +96,9 @@ public class ProductController {
return CzgResult.success();
}
/**
* 商品-修改
*/
@PutMapping
@OperationLog("商品-修改")
@SaStaffCheckPermission("yun_xu_xiu_gai_shang_pin")
@ -111,6 +124,10 @@ public class ProductController {
return CzgResult.success();
}
/**
* 商品-删除
* @param id 商品ID
*/
@DeleteMapping("{id}")
@OperationLog("商品-删除")
@SaStaffCheckPermission("yun_xu_xiu_gai_shang_pin")
@ -209,4 +226,28 @@ public class ProductController {
return CzgResult.success();
}
/**
* 商品-统计
*/
@GetMapping("statistics")
@OperationLog("商品-统计")
//@SaAdminCheckPermission("product:statistics")
public CzgResult<Void> statistics(@RequestBody ProductInfoParam param) {
Long shopId = StpKit.USER.getShopId(0L);
param.setShopId(shopId);
productService.getProductStatistics(param);
return CzgResult.success();
}
/**
* 商品-库存变动记录
*/
@GetMapping("stockFlow")
@OperationLog("商品-库存变动记录")
//@SaAdminCheckPermission("consStockFlow:flow")
public CzgResult<Page<ProductStockFlow>> stockFlow(ProductStockFlowParam param) {
Page<ProductStockFlow> data = productService.findProductStockFlowPage(param);
return CzgResult.success(data);
}
}

View File

@ -127,7 +127,7 @@ public class Product implements Serializable {
*/
private String groupCategoryId;
/**
* 商品库存数量
* 商品库存数量
*/
private Integer stockNumber;
/**

View File

@ -2,10 +2,9 @@ package com.czg.product.service;
import com.czg.product.dto.ProductDTO;
import com.czg.product.entity.Product;
import com.czg.product.param.ProdRefundToStockParam;
import com.czg.product.param.ProductIsSaleParam;
import com.czg.product.param.ProductIsSoldOutParam;
import com.czg.product.param.ProductReportDamageParam;
import com.czg.product.entity.ProductStockFlow;
import com.czg.product.param.*;
import com.czg.product.vo.ProductStatisticsVo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService;
@ -99,4 +98,18 @@ public interface ProductService extends IService<Product> {
*/
void reportDamage(ProductReportDamageParam param);
/**
* 商品统计
*
* @param param 商品统计入参
*/
ProductStatisticsVo getProductStatistics(ProductInfoParam param);
/**
* 商品出入库流水查询
* @param param 查询参数
* @return 分页数据
*/
Page<ProductStockFlow> findProductStockFlowPage(ProductStockFlowParam param);
}

View File

@ -9,17 +9,11 @@ import com.czg.config.RabbitPublisher;
import com.czg.constant.CacheConstant;
import com.czg.enums.YesNoEnum;
import com.czg.product.dto.ProductStockSubtractDTO;
import com.czg.product.entity.ConsInfo;
import com.czg.product.entity.ConsStockFlow;
import com.czg.product.entity.ProdConsRelation;
import com.czg.product.entity.Product;
import com.czg.product.entity.*;
import com.czg.product.enums.InOutItemEnum;
import com.czg.product.enums.InOutTypeEnum;
import com.czg.product.service.ProductRpcService;
import com.czg.service.product.mapper.ConsInfoMapper;
import com.czg.service.product.mapper.ConsStockFlowMapper;
import com.czg.service.product.mapper.ProdConsRelationMapper;
import com.czg.service.product.mapper.ProductMapper;
import com.czg.service.product.mapper.*;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@ -54,6 +48,8 @@ public class ProductRpcServiceImpl implements ProductRpcService {
@Resource
private ConsStockFlowMapper consStockFlowMapper;
@Resource
private ProductStockFlowMapper productStockFlowMapper;
@Resource
private RabbitPublisher rabbitPublisher;
@Override
@ -65,7 +61,21 @@ public class ProductRpcServiceImpl implements ProductRpcService {
return;
}
for (ProductStockSubtractDTO dto : list) {
Product product = productMapper.selectOneById(dto.getProductId());
productMapper.updateProductStockNum(dto.getProductId(), dto.getShopId(), "sub", dto.getNum());
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
flow.setCreateUserId(1L);
flow.setCreateUserName("银收客");
flow.setShopId(shopId);
flow.setProductId(product.getId());
flow.setProductName(product.getName());
flow.setBeforeNumber(NumberUtil.toBigDecimal(product.getStockNumber()));
flow.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, NumberUtil.toBigDecimal(dto.getNum())));
flow.setAfterNumber(NumberUtil.sub(NumberUtil.toBigDecimal(product.getStockNumber()), NumberUtil.toBigDecimal(dto.getNum())));
flow.setInOutType(InOutTypeEnum.OUT.value());
flow.setInOutItem(InOutItemEnum.ORDER_OUT.value());
productStockFlowMapper.insert(flow);
// 查询商品绑定耗材信息
List<ProdConsRelation> relationList = prodConsRelationMapper.selectListByQuery(QueryWrapper.create().eq(ProdConsRelation::getProductId, dto.getProductId()));
if (CollUtil.isEmpty(relationList)) {
@ -124,7 +134,21 @@ public class ProductRpcServiceImpl implements ProductRpcService {
return;
}
for (ProductStockSubtractDTO dto : list) {
Product product = productMapper.selectOneById(dto.getProductId());
productMapper.updateProductStockNum(dto.getProductId(), dto.getShopId(), "add", dto.getNum());
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
flow.setCreateUserId(1L);
flow.setCreateUserName("银收客");
flow.setShopId(shopId);
flow.setProductId(product.getId());
flow.setProductName(product.getName());
flow.setBeforeNumber(NumberUtil.toBigDecimal(product.getStockNumber()));
flow.setInOutNumber(NumberUtil.toBigDecimal(dto.getNum()));
flow.setAfterNumber(NumberUtil.add(NumberUtil.toBigDecimal(product.getStockNumber()), NumberUtil.toBigDecimal(dto.getNum())));
flow.setInOutType(InOutTypeEnum.IN.value());
flow.setInOutItem(InOutItemEnum.ORDER_IN.value());
productStockFlowMapper.insert(flow);
// 查询商品绑定耗材信息
List<ProdConsRelation> relationList = prodConsRelationMapper.selectListByQuery(QueryWrapper.create().eq(ProdConsRelation::getProductId, dto.getProductId()));
if (CollUtil.isEmpty(relationList)) {
@ -186,6 +210,19 @@ public class ProductRpcServiceImpl implements ProductRpcService {
continue;
}
productMapper.updateProductStockNum(dto.getProductId(), dto.getShopId(), "add", dto.getNum());
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
flow.setCreateUserId(1L);
flow.setCreateUserName("银收客");
flow.setShopId(shopId);
flow.setProductId(product.getId());
flow.setProductName(product.getName());
flow.setBeforeNumber(NumberUtil.toBigDecimal(product.getStockNumber()));
flow.setInOutNumber(NumberUtil.toBigDecimal(dto.getNum()));
flow.setAfterNumber(NumberUtil.add(NumberUtil.toBigDecimal(product.getStockNumber()), NumberUtil.toBigDecimal(dto.getNum())));
flow.setInOutType(InOutTypeEnum.IN.value());
flow.setInOutItem(InOutItemEnum.ORDER_IN.value());
productStockFlowMapper.insert(flow);
// 查询商品绑定耗材信息
List<ProdConsRelation> relationList = prodConsRelationMapper.selectListByQuery(QueryWrapper.create().eq(ProdConsRelation::getProductId, dto.getProductId()));
if (CollUtil.isEmpty(relationList)) {

View File

@ -14,16 +14,11 @@ import com.czg.exception.CzgException;
import com.czg.product.dto.ProdConsRelationDTO;
import com.czg.product.dto.ProdSkuDTO;
import com.czg.product.dto.ProductDTO;
import com.czg.product.entity.ConsInfo;
import com.czg.product.entity.ConsStockFlow;
import com.czg.product.entity.ProdSku;
import com.czg.product.entity.Product;
import com.czg.product.entity.*;
import com.czg.product.enums.*;
import com.czg.product.param.ProdRefundToStockParam;
import com.czg.product.param.ProductIsSaleParam;
import com.czg.product.param.ProductIsSoldOutParam;
import com.czg.product.param.ProductReportDamageParam;
import com.czg.product.param.*;
import com.czg.product.service.ProductService;
import com.czg.product.vo.ProductStatisticsVo;
import com.czg.sa.StpKit;
import com.czg.service.product.mapper.*;
import com.czg.utils.PageUtil;
@ -63,6 +58,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
private final ProdConsRelationMapper prodConsRelationMapper;
private final ConsInfoMapper consInfoMapper;
private final ConsStockFlowMapper consStockFlowMapper;
private final ProductStockFlowMapper productStockFlowMapper;
private QueryWrapper buildQueryWrapper(ProductDTO param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
@ -209,6 +205,24 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
}
prodSkuMapper.insertBatch(prodSkuList);
}
if (entity.getIsStock() == YesNoEnum.NO.value()) {
return;
}
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
flow.setCreateUserId(createUserId);
flow.setCreateUserName(createUserName);
flow.setShopId(shopId);
flow.setProductId(entity.getId());
flow.setProductName(entity.getName());
flow.setBeforeNumber(BigDecimal.ZERO);
flow.setInOutNumber(NumberUtil.toBigDecimal(entity.getStockNumber()));
flow.setAfterNumber(NumberUtil.toBigDecimal(entity.getStockNumber()));
flow.setInOutType(InOutTypeEnum.IN.value());
flow.setInOutItem(InOutItemEnum.WIN_IN.value());
productStockFlowMapper.insert(flow);
}
@Override
@ -220,6 +234,7 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
if (exists) {
throw new CzgException("商品已存在");
}
Product old = super.getById(dto.getId());
Product entity = BeanUtil.copyProperties(dto, Product.class);
entity.setImages(JSON.toJSONString(dto.getImages(), JSONWriter.Feature.WriteMapNullValue));
entity.setGroupSnap("[]");
@ -264,6 +279,34 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
.eq(ProdSku::getShopId, shopId)
.update();
}
if (entity.getIsStock() == YesNoEnum.NO.value()) {
return;
}
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
flow.setCreateUserId(createUserId);
flow.setCreateUserName(createUserName);
flow.setShopId(shopId);
flow.setProductId(old.getId());
flow.setProductName(old.getName());
flow.setBeforeNumber(NumberUtil.toBigDecimal(old.getStockNumber()));
BigDecimal inOutNumber = NumberUtil.sub(NumberUtil.toBigDecimal(entity.getStockNumber()), flow.getBeforeNumber());
// 盘亏
if (NumberUtil.isLess(inOutNumber, BigDecimal.ZERO)) {
flow.setInOutNumber(inOutNumber);
flow.setInOutType(InOutTypeEnum.OUT.value());
flow.setInOutItem(InOutItemEnum.LOSS_OUT.value());
}
// 盘盈
if (NumberUtil.isGreater(inOutNumber, BigDecimal.ZERO)) {
flow.setInOutNumber(inOutNumber);
flow.setInOutType(InOutTypeEnum.IN.value());
flow.setInOutItem(InOutItemEnum.WIN_IN.value());
}
flow.setAfterNumber(NumberUtil.toBigDecimal(entity.getStockNumber()));
productStockFlowMapper.insert(flow);
}
@Override
@ -378,6 +421,20 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
throw new CzgException("商品库存不足,无法报损");
}
super.updateById(product);
// 记录商品库存流水
ProductStockFlow flow = new ProductStockFlow();
flow.setCreateUserId(createUserId);
flow.setCreateUserName(createUserName);
flow.setShopId(shopId);
flow.setProductId(product.getId());
flow.setProductName(product.getName());
flow.setBeforeNumber(NumberUtil.toBigDecimal(stockNumber));
flow.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, NumberUtil.toBigDecimal(param.getNumber())));
flow.setAfterNumber(NumberUtil.toBigDecimal(product.getStockNumber()));
flow.setInOutType(InOutTypeEnum.OUT.value());
flow.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
flow.setImgUrls(JSON.toJSONString(param.getImgUrls()));
productStockFlowMapper.insert(flow);
// 如果绑定了耗材则同步更新耗材库存
List<ProdConsRelationDTO> consList = prodConsRelationMapper.selectListByProdId(param.getProductId());
if (CollUtil.isEmpty(consList)) {
@ -411,4 +468,31 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
.update();
}
}
@Override
public ProductStatisticsVo getProductStatistics(ProductInfoParam param) {
ProductStatisticsVo data = productStockFlowMapper.getProductStatistics(param);
if (data == null) {
data = new ProductStatisticsVo();
}
return data;
}
@Override
public Page<ProductStockFlow> findProductStockFlowPage(ProductStockFlowParam param) {
Long shopId = StpKit.USER.getShopId(0L);
QueryWrapper queryWrapper = QueryWrapper.create();
queryWrapper.eq(ProductStockFlow::getShopId, shopId);
if (StrUtil.isNotBlank(param.getInOutType())) {
queryWrapper.eq(ProductStockFlow::getInOutType, param.getInOutType());
}
if (StrUtil.isNotBlank(param.getInOutItem())) {
queryWrapper.eq(ProductStockFlow::getInOutItem, param.getInOutItem());
}
if (ObjUtil.isNotNull(param.getProductId())) {
queryWrapper.eq(ProductStockFlow::getProductId, param.getProductId());
}
queryWrapper.orderBy(ProductStockFlow::getId, false);
return productStockFlowMapper.paginate(PageUtil.buildPage(), queryWrapper);
}
}