v1.0.49 #61
@@ -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);
|
||||
|
||||
|
||||
+19
@@ -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
|
||||
|
||||
+34
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user