add GaoProductStockBatchRequest

This commit is contained in:
Timi
2026-09-16 15:15:19 +08:00
parent cd8bcae5be
commit 7d53d31c3f
9 changed files with 117 additions and 4 deletions
@@ -101,8 +101,8 @@ public class GaoPackageConfigController {
if (skuIdList.isEmpty()) {
return;
}
Map<String, GaoProductSku> skuMap = skuIdList.stream()
.collect(java.util.stream.Collectors.toMap(item -> item, skuService::detail));
Map<String, GaoProductSku> skuMap = skuService.listByIdList(skuIdList).stream()
.collect(java.util.stream.Collectors.toMap(GaoProductSku::getId, item -> item));
for (GaoPackageConfig config : configList) {
for (GaoPackageConfigItem item : config.getItemList()) {
item.setSku(skuMap.get(item.getSkuId()));
@@ -7,6 +7,7 @@ import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
import com.imyeyu.api.modules.gao.vo.GaoProductStockBatchRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
@@ -23,6 +24,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/// GAO 产品库存接口
///
/// @author Codex
@@ -64,6 +67,16 @@ public class GaoProductStockController {
return stockService.detail(storeScopeService.resolveStoreId(storeId), skuId);
}
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.PRODUCT_STOCK_READ)
@PostMapping("/batch-detail")
public List<GaoProductStock> batchDetail(@RequestBody GaoProductStockBatchRequest request) {
TimiException.required(request, "not found stock batch request");
request.setStoreId(storeScopeService.resolveStoreId(request.getStoreId()));
return stockService.batchDetail(request.getStoreId(), request.getSkuIdList());
}
@AOPLog
@RequestRateLimit
@RequireGaoPermission(GaoPermissionCode.PRODUCT_STOCK_CREATE)
@@ -32,6 +32,9 @@ public interface GaoProductStockMapper extends BaseMapper<GaoProductStock, Strin
/// 按门店和产品查询库存余额
GaoProductStock selectByStoreIdAndSkuId(@Param("storeId") String storeId, @Param("skuId") String skuId);
/// 按门店和 SKU 批量查询库存余额
List<GaoProductStock> selectByStoreIdAndSkuIdList(@Param("storeId") String storeId, @Param("skuIdList") List<String> skuIdList);
/// 条件更新库存余额
int updateQuantity(@Param("id") String id, @Param("onHandDelta") java.math.BigDecimal onHandDelta, @Param("reservedDelta") java.math.BigDecimal reservedDelta, @Param("updatedBy") String updatedBy);
@@ -26,6 +26,9 @@ public interface GaoProductStockService {
/// 查询库存余额详情
GaoProductStock detail(String storeId, String skuId);
/// 批量查询指定 SKU 库存余额
List<GaoProductStock> batchDetail(String storeId, List<String> skuIdList);
/// 期初入库
GaoProductStock initialIn(GaoProductStockChangeRequest request);
@@ -8,7 +8,9 @@ import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
import com.imyeyu.api.modules.gao.mapper.GaoProductMapper;
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
import com.imyeyu.api.modules.gao.service.GaoProductService;
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
@@ -48,6 +50,7 @@ import java.util.stream.Collectors;
public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct, String> implements GaoProductService {
private final GaoProductMapper mapper;
private final GaoProductStockMapper stockMapper;
private final AttachmentService attachmentService;
private final ObjectProvider<GaoProductCategoryService> categoryServiceProvider;
private final ObjectProvider<GaoProductSkuService> skuServiceProvider;
@@ -120,6 +123,9 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
TimiException.required(request.getId(), "not found product.id");
GaoProduct product = get(request.getId());
TimiException.required(product, "not found product");
if (request.getProductType() == GaoProduct.ProductType.SERVICE && product.getProductType() != GaoProduct.ProductType.SERVICE) {
verifyNoReservedStock(product);
}
product.setCategoryId(request.getCategoryId());
product.setCode(TimiJava.defaultIfNull(request.getCode(), product.getCode()));
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
@@ -139,6 +145,19 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
}
}
private void verifyNoReservedStock(GaoProduct product) {
List<String> skuIdList = skuServiceProvider.getObject().listByProductId(product.getId()).stream()
.map(GaoProductSku::getId)
.filter(id -> id != null && !id.isBlank())
.toList();
if (skuIdList.isEmpty()) {
return;
}
boolean hasReservedStock = stockMapper.selectByStoreIdAndSkuIdList(product.getStoreId(), skuIdList).stream()
.anyMatch(stock -> stock.getReservedQuantity() != null && 0 < stock.getReservedQuantity().signum());
TimiException.requiredTrue(!hasReservedStock, "当前产品存在预占库存,不能切换为服务产品,请先完成或取消相关订单");
}
@OperationAudit(AuditOperation.GAO_PRODUCT_DISABLE)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
@@ -68,7 +68,8 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
List<GaoProductStock> list = stockMapper.selectByQuery(page);
Map<String, Attachment> photoMap = attachmentService.mapFirstImageByIdList(
Attachment.BizType.GAO_PRODUCT_SKU,
list.stream().map(GaoProductStock::getSkuId).filter(id -> id != null && !id.isBlank()).toList()
list.stream().map(GaoProductStock::getSkuId).filter(id -> id != null && !id.isBlank()).toList(),
"PHOTO"
);
for (GaoProductStock stock : list) {
fillBalance(stock);
@@ -86,7 +87,17 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
TimiException.required(page, "not found stock product page");
PageResult<GaoProductStockProductView> result = new PageResult<>();
result.setTotal(stockMapper.countProductByQuery(page));
result.setList(stockMapper.selectProductByQuery(page));
List<GaoProductStockProductView> list = stockMapper.selectProductByQuery(page);
Map<String, Attachment> photoMap = attachmentService.mapFirstImageByIdList(
Attachment.BizType.GAO_PRODUCT,
list.stream().map(GaoProductStockProductView::getProductId).filter(id -> id != null && !id.isBlank()).toList(),
"PHOTO"
);
for (GaoProductStockProductView product : list) {
Attachment photo = photoMap.get(product.getProductId());
product.setAttachmentList(photo == null ? List.of() : List.of(photo));
}
result.setList(list);
return result;
}
@@ -111,6 +122,27 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
return stock;
}
@Override
public List<GaoProductStock> batchDetail(String storeId, List<String> skuIdList) {
TimiException.required(storeId, "not found storeId");
storeScopeService.checkStoreId(storeId);
if (skuIdList == null || skuIdList.isEmpty()) {
return List.of();
}
List<String> normalizedSkuIdList = skuIdList.stream()
.filter(value -> value != null && !value.isBlank())
.distinct()
.toList();
if (normalizedSkuIdList.isEmpty()) {
return List.of();
}
List<GaoProductStock> result = stockMapper.selectByStoreIdAndSkuIdList(storeId, normalizedSkuIdList);
for (GaoProductStock stock : result) {
fillBalance(stock);
}
return result;
}
@OperationAudit(AuditOperation.GAO_PRODUCT_STOCK_INITIAL_IN)
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
@@ -0,0 +1,19 @@
package com.imyeyu.api.modules.gao.vo;
import lombok.Data;
import java.util.List;
/// GAO 产品库存批量查询请求
///
/// @author Codex
/// @since 2026-09-16
@Data
public class GaoProductStockBatchRequest {
/// 门店 ID
private String storeId;
/// SKU ID 列表
private List<String> skuIdList;
}
@@ -1,9 +1,11 @@
package com.imyeyu.api.modules.gao.vo;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.gao.entity.GaoProduct;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/// 产品库存第一层汇总视图
///
@@ -21,6 +23,9 @@ public class GaoProductStockProductView {
/// 产品名称
private String productName;
/// 产品图片
private List<Attachment> attachmentList;
/// 产品类型
private GaoProduct.ProductType productType;
@@ -10,6 +10,25 @@
SELECT * FROM `gao_product_stock`
WHERE `store_id` = #{storeId} AND `sku_id` = #{skuId}
</select>
<select id="selectByStoreIdAndSkuIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
SELECT s.`id`,
k.`store_id`,
k.`id` AS `sku_id`,
COALESCE(s.`on_hand_quantity`, 0) AS `on_hand_quantity`,
COALESCE(s.`reserved_quantity`, 0) AS `reserved_quantity`,
s.`updated_by`,
s.`created_at`,
s.`updated_at`
FROM `gao_product_sku` k
JOIN `gao_product` p ON p.`id` = k.`product_id`
LEFT JOIN `gao_product_stock` s ON s.`sku_id` = k.`id` AND s.`store_id` = #{storeId}
WHERE k.`store_id` = #{storeId}
AND p.`deleted_at` IS NULL AND k.`deleted_at` IS NULL
AND k.`id` IN
<foreach collection="skuIdList" item="skuId" open="(" separator="," close=")">
#{skuId}
</foreach>
</select>
<select id="countByQuery" resultType="long">
SELECT COUNT(1)
FROM `gao_product_sku` k