v1.0.31 #43
@@ -40,6 +40,8 @@ public class Attachment extends UUIDEntity {
|
|||||||
|
|
||||||
GAO_PRODUCT,
|
GAO_PRODUCT,
|
||||||
|
|
||||||
|
GAO_PRODUCT_SKU,
|
||||||
|
|
||||||
GAO_EVENT_RECORD,
|
GAO_EVENT_RECORD,
|
||||||
|
|
||||||
GAO_FUND_RECORD,
|
GAO_FUND_RECORD,
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
|||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
import com.imyeyu.api.modules.gao.service.GaoEventService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||||
import com.imyeyu.api.modules.user.entity.User;
|
import com.imyeyu.api.modules.user.entity.User;
|
||||||
@@ -37,7 +37,7 @@ public class GaoPackageConfigController {
|
|||||||
|
|
||||||
private final GaoPackageConfigService service;
|
private final GaoPackageConfigService service;
|
||||||
private final GaoEventService eventService;
|
private final GaoEventService eventService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
|
||||||
@@ -92,20 +92,20 @@ public class GaoPackageConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fillProductDetails(List<GaoPackageConfig> configList) {
|
private void fillProductDetails(List<GaoPackageConfig> configList) {
|
||||||
List<String> productIdList = configList.stream()
|
List<String> skuIdList = configList.stream()
|
||||||
.flatMap(config -> config.getItemList().stream())
|
.flatMap(config -> config.getItemList().stream())
|
||||||
.map(GaoPackageConfigItem::getProductId)
|
.map(GaoPackageConfigItem::getSkuId)
|
||||||
.filter(value -> value != null && !value.isBlank())
|
.filter(value -> value != null && !value.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
if (productIdList.isEmpty()) {
|
if (skuIdList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, GaoProduct> productMap = productService.listByIdList(productIdList).stream()
|
Map<String, GaoProductSku> skuMap = skuIdList.stream()
|
||||||
.collect(java.util.stream.Collectors.toMap(GaoProduct::getId, item -> item));
|
.collect(java.util.stream.Collectors.toMap(item -> item, skuService::detail));
|
||||||
for (GaoPackageConfig config : configList) {
|
for (GaoPackageConfig config : configList) {
|
||||||
for (GaoPackageConfigItem item : config.getItemList()) {
|
for (GaoPackageConfigItem item : config.getItemList()) {
|
||||||
item.setProduct(productMap.get(item.getProductId()));
|
item.setSku(skuMap.get(item.getSkuId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,13 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
|||||||
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
|
import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductCategoryPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductCategoryPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
||||||
@@ -33,6 +38,8 @@ public class GaoProductController {
|
|||||||
|
|
||||||
private final GaoProductCategoryService categoryService;
|
private final GaoProductCategoryService categoryService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductService productService;
|
||||||
|
private final GaoProductSkuService skuService;
|
||||||
|
private final GaoProductSpecService specService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@@ -142,6 +149,93 @@ public class GaoProductController {
|
|||||||
productService.disable(id);
|
productService.disable(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_READ)
|
||||||
|
@PostMapping("/sku/detail")
|
||||||
|
public GaoProductSku skuDetail(@RequestParam String id) {
|
||||||
|
GaoProductSku result = skuService.detail(id);
|
||||||
|
storeScopeService.checkStoreId(result.getStoreId());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_CREATE)
|
||||||
|
@PostMapping("/sku/create")
|
||||||
|
public String skuCreate(@RequestBody GaoProductSku sku) {
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
GaoProduct product = productService.get(sku.getProductId());
|
||||||
|
TimiException.required(product, "not found product");
|
||||||
|
storeScopeService.checkStoreId(product.getStoreId());
|
||||||
|
skuService.create(sku);
|
||||||
|
return sku.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE)
|
||||||
|
@PostMapping("/sku/update")
|
||||||
|
public void skuUpdate(@RequestBody GaoProductSku request) {
|
||||||
|
TimiException.required(request, "not found product sku");
|
||||||
|
TimiException.required(request.getId(), "not found product sku.id");
|
||||||
|
GaoProductSku sku = skuService.get(request.getId());
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
storeScopeService.checkStoreId(sku.getStoreId());
|
||||||
|
request.setProductId(sku.getProductId());
|
||||||
|
skuService.update(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE)
|
||||||
|
@PostMapping("/sku/disable")
|
||||||
|
public void skuDisable(@RequestParam String id) {
|
||||||
|
GaoProductSku sku = skuService.get(id);
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
storeScopeService.checkStoreId(sku.getStoreId());
|
||||||
|
skuService.disable(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_READ)
|
||||||
|
@PostMapping("/spec/list")
|
||||||
|
public List<GaoProductSpec> specList(@RequestParam String productId) {
|
||||||
|
GaoProduct product = productService.get(productId);
|
||||||
|
TimiException.required(product, "not found product");
|
||||||
|
storeScopeService.checkStoreId(product.getStoreId());
|
||||||
|
return specService.listByProductId(productId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE)
|
||||||
|
@PostMapping("/spec/create")
|
||||||
|
public String specCreate(@RequestBody GaoProductSpec spec) {
|
||||||
|
TimiException.required(spec, "not found product spec");
|
||||||
|
GaoProduct product = productService.get(spec.getProductId());
|
||||||
|
TimiException.required(product, "not found product");
|
||||||
|
storeScopeService.checkStoreId(product.getStoreId());
|
||||||
|
specService.createSpec(spec);
|
||||||
|
return spec.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE)
|
||||||
|
@PostMapping("/spec/value/create")
|
||||||
|
public String specValueCreate(@RequestBody GaoProductSpecValue value) {
|
||||||
|
TimiException.required(value, "not found product spec value");
|
||||||
|
String productId = specService.getProductIdBySpecId(value.getSpecId());
|
||||||
|
TimiException.required(productId, "规格项不存在");
|
||||||
|
GaoProduct product = productService.get(productId);
|
||||||
|
TimiException.required(product, "not found product");
|
||||||
|
storeScopeService.checkStoreId(product.getStoreId());
|
||||||
|
specService.createValue(value);
|
||||||
|
return value.getId();
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.PRODUCT_READ)
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_READ)
|
||||||
|
|||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission;
|
||||||
|
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockHealthView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockMovementDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSalesRankView;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO SKU 图表接口
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/gao/product/sku/chart")
|
||||||
|
public class GaoProductSkuChartController {
|
||||||
|
|
||||||
|
private final GaoProductSkuChartService service;
|
||||||
|
private final GaoStoreScopeService storeScopeService;
|
||||||
|
|
||||||
|
/// 查询 SKU 每日销售统计
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/sales/daily")
|
||||||
|
public List<GaoProductSkuSalesDailyStatView> salesDaily(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.salesDaily(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 销售排行
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/sales/rank")
|
||||||
|
public List<GaoProductSkuSalesRankView> salesRank(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.salesRank(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 单位销售排行
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/sales/unit/rank")
|
||||||
|
public List<GaoProductSkuUnitSalesRankView> unitSalesRank(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.unitSalesRank(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 每日履约统计
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/fulfillment/daily")
|
||||||
|
public List<GaoProductSkuFulfillmentDailyStatView> fulfillmentDaily(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.fulfillmentDaily(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 履约进度排行
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/fulfillment/rank")
|
||||||
|
public List<GaoProductSkuFulfillmentRankView> fulfillmentRank(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.fulfillmentRank(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 每日库存变动统计
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/stock/movement/daily")
|
||||||
|
public List<GaoProductSkuStockMovementDailyStatView> stockMovementDaily(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.stockMovementDaily(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 查询 SKU 库存健康度
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/stock/health")
|
||||||
|
public List<GaoProductSkuStockHealthView> stockHealth(@RequestBody GaoProductChartReq req) {
|
||||||
|
return service.stockHealth(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GaoProductChartReq normalize(GaoProductChartReq req) {
|
||||||
|
TimiException.required(req, "SKU 图表请求不能为空");
|
||||||
|
req.setStoreId(storeScopeService.resolveStoreId(req.getStoreId()));
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockRecordPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockRecordPage;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
@@ -45,12 +46,22 @@ public class GaoProductStockController {
|
|||||||
return stockService.pageByQuery(page);
|
return stockService.pageByQuery(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_STOCK_READ)
|
||||||
|
@PostMapping("/product-list")
|
||||||
|
public PageResult<GaoProductStockProductView> productList(@RequestBody GaoProductStockPage page) {
|
||||||
|
TimiException.required(page, "not found stock product page");
|
||||||
|
page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId()));
|
||||||
|
return stockService.pageProductByQuery(page);
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequireGaoPermission(GaoPermissionCode.PRODUCT_STOCK_READ)
|
@RequireGaoPermission(GaoPermissionCode.PRODUCT_STOCK_READ)
|
||||||
@PostMapping("/detail")
|
@PostMapping("/detail")
|
||||||
public GaoProductStock detail(@RequestParam(required = false) String storeId, @RequestParam String productId) {
|
public GaoProductStock detail(@RequestParam(required = false) String storeId, @RequestParam String skuId) {
|
||||||
return stockService.detail(storeScopeService.resolveStoreId(storeId), productId);
|
return stockService.detail(storeScopeService.resolveStoreId(storeId), skuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.imyeyu.api.modules.gao.service.GaoSalesOrderChartService;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderSkuRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
||||||
@@ -84,6 +85,15 @@ public class GaoSalesOrderChartController {
|
|||||||
return service.customerProductRank(normalize(req));
|
return service.customerProductRank(normalize(req));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 查询指定客户订单 SKU 排行
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequireGaoPermission(GaoPermissionCode.STAT_READ)
|
||||||
|
@PostMapping("/customer/sku/rank")
|
||||||
|
public List<GaoCustomerOrderSkuRankView> customerSkuRank(@RequestBody GaoSalesOrderChartReq req) {
|
||||||
|
return service.customerSkuRank(normalize(req));
|
||||||
|
}
|
||||||
|
|
||||||
/// 查询销售订单状态统计
|
/// 查询销售订单状态统计
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
|
|||||||
/// 履约单 ID
|
/// 履约单 ID
|
||||||
private String fulfillmentId;
|
private String fulfillmentId;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品图片附件
|
/// 产品图片附件
|
||||||
@Transient
|
@Transient
|
||||||
@@ -59,6 +59,18 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
|
|||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// 产品 ID 快照
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位快照
|
/// 单位快照
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,27 @@ public class GaoPackageConfigItem extends UUIDEntity {
|
|||||||
/// 套餐配置 ID
|
/// 套餐配置 ID
|
||||||
private String packageConfigId;
|
private String packageConfigId;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// 产品 ID 快照
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位快照
|
/// 单位快照
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
@@ -42,7 +54,7 @@ public class GaoPackageConfigItem extends UUIDEntity {
|
|||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
/// 产品详情
|
/// SKU 详情
|
||||||
@Transient
|
@Transient
|
||||||
private GaoProduct product;
|
private GaoProductSku sku;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.imyeyu.spring.entity.UUIDEntity;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/// GAO 产品
|
/// GAO 产品
|
||||||
@@ -68,15 +67,9 @@ public class GaoProduct extends UUIDEntity {
|
|||||||
/// true 为启用库存管理
|
/// true 为启用库存管理
|
||||||
private Boolean stockEnabled;
|
private Boolean stockEnabled;
|
||||||
|
|
||||||
/// true 为允许业务数量保留小数
|
|
||||||
private Boolean allowDecimalQuantity;
|
|
||||||
|
|
||||||
/// 状态
|
/// 状态
|
||||||
private Status status;
|
private Status status;
|
||||||
|
|
||||||
/// 最低库存数量,基础单位
|
|
||||||
private BigDecimal minStockQuantity;
|
|
||||||
|
|
||||||
/// 排序值
|
/// 排序值
|
||||||
private Integer sort;
|
private Integer sort;
|
||||||
|
|
||||||
@@ -90,9 +83,13 @@ public class GaoProduct extends UUIDEntity {
|
|||||||
@Transient
|
@Transient
|
||||||
private GaoProductCategory category;
|
private GaoProductCategory category;
|
||||||
|
|
||||||
/// 产品单位列表
|
/// 产品规格列表
|
||||||
@Transient
|
@Transient
|
||||||
private List<GaoProductUnit> unitList;
|
private List<GaoProductSpec> specList;
|
||||||
|
|
||||||
|
/// 产品 SKU 列表
|
||||||
|
@Transient
|
||||||
|
private List<GaoProductSku> skuList;
|
||||||
|
|
||||||
/// 产品附件列表
|
/// 产品附件列表
|
||||||
@Transient
|
@Transient
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品 SKU
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoProductSku extends UUIDEntity {
|
||||||
|
|
||||||
|
/// SKU 状态
|
||||||
|
public enum Status {
|
||||||
|
|
||||||
|
/// 启用
|
||||||
|
ACTIVE,
|
||||||
|
|
||||||
|
/// 停用
|
||||||
|
INACTIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 门店 ID
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 门店内 SKU 编码
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/// 条码
|
||||||
|
private String barcode;
|
||||||
|
|
||||||
|
/// SKU 展示名称
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/// 规格摘要
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 规格值组合键
|
||||||
|
private String specValueKey;
|
||||||
|
|
||||||
|
/// true 为启用库存管理
|
||||||
|
private Boolean stockEnabled;
|
||||||
|
|
||||||
|
/// true 为允许业务数量保留小数
|
||||||
|
private Boolean allowDecimalQuantity;
|
||||||
|
|
||||||
|
/// 最低库存数量,基础单位
|
||||||
|
private BigDecimal minStockQuantity;
|
||||||
|
|
||||||
|
/// 状态
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
/// 排序值
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/// 备注
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/// 最后修改用户 ID
|
||||||
|
private String updatedBy;
|
||||||
|
|
||||||
|
/// 规格值 ID 列表
|
||||||
|
@Transient
|
||||||
|
private List<String> specValueIdList;
|
||||||
|
|
||||||
|
/// SKU 单位列表
|
||||||
|
@Transient
|
||||||
|
private List<GaoProductSkuUnit> unitList;
|
||||||
|
|
||||||
|
/// SKU 附件列表
|
||||||
|
@Transient
|
||||||
|
private List<Attachment> attachmentList;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.annotation.table.AutoUUID;
|
||||||
|
import com.imyeyu.spring.annotation.table.Id;
|
||||||
|
import com.imyeyu.spring.entity.Creatable;
|
||||||
|
import com.imyeyu.spring.entity.IDEntity;
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO SKU 规格值关联
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuSpecValue implements IDEntity<String>, Creatable {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@AutoUUID
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// 规格值 ID
|
||||||
|
private String specValueId;
|
||||||
|
|
||||||
|
private Long createdAt;
|
||||||
|
}
|
||||||
+5
-5
@@ -6,16 +6,16 @@ import lombok.EqualsAndHashCode;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/// GAO 产品业务单位
|
/// GAO 产品 SKU 业务单位
|
||||||
///
|
///
|
||||||
/// @author Codex
|
/// @author Codex
|
||||||
/// @since 2026-09-02
|
/// @since 2026-09-06
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class GaoProductUnit extends UUIDEntity {
|
public class GaoProductSkuUnit extends UUIDEntity {
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 单位名称
|
/// 单位名称
|
||||||
private String name;
|
private String name;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品规格项
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoProductSpec extends UUIDEntity {
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 规格名称
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/// 排序值
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/// 规格值列表
|
||||||
|
@Transient
|
||||||
|
private List<GaoProductSpecValue> valueList;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.entity.UUIDEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/// GAO 产品规格值
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GaoProductSpecValue extends UUIDEntity {
|
||||||
|
|
||||||
|
/// 规格项 ID
|
||||||
|
private String specId;
|
||||||
|
|
||||||
|
/// 规格值名称
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/// true 为启用
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/// 排序值
|
||||||
|
private Integer sort;
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@ public class GaoProductStock extends UUIDEntity {
|
|||||||
/// 门店 ID
|
/// 门店 ID
|
||||||
private String storeId;
|
private String storeId;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 实际库存,基础单位
|
/// 实际库存,基础单位
|
||||||
private BigDecimal onHandQuantity;
|
private BigDecimal onHandQuantity;
|
||||||
@@ -30,9 +30,9 @@ public class GaoProductStock extends UUIDEntity {
|
|||||||
/// 最后操作人
|
/// 最后操作人
|
||||||
private String updatedBy;
|
private String updatedBy;
|
||||||
|
|
||||||
/// 产品
|
/// SKU
|
||||||
@Transient
|
@Transient
|
||||||
private GaoProduct product;
|
private GaoProductSku sku;
|
||||||
|
|
||||||
/// 可用库存,基础单位
|
/// 可用库存,基础单位
|
||||||
@Transient
|
@Transient
|
||||||
|
|||||||
@@ -70,15 +70,27 @@ public class GaoProductStockRecord implements IDEntity<String>, Creatable {
|
|||||||
/// 门店 ID
|
/// 门店 ID
|
||||||
private String storeId;
|
private String storeId;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// 产品 ID 快照
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位名称快照
|
/// 单位名称快照
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
|||||||
/// 订单项类型
|
/// 订单项类型
|
||||||
private ItemType itemType;
|
private ItemType itemType;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品图片附件
|
/// 产品图片附件
|
||||||
@Transient
|
@Transient
|
||||||
@@ -46,6 +46,18 @@ public class GaoSalesOrderItem extends UUIDEntity {
|
|||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// 产品 ID 快照
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位快照
|
/// 单位快照
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockHealthView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockMovementDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSalesRankView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO SKU 图表 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
public interface GaoProductSkuChartMapper {
|
||||||
|
|
||||||
|
/// 查询 SKU 每日销售统计
|
||||||
|
List<GaoProductSkuSalesDailyStatView> selectSalesDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 销售排行
|
||||||
|
List<GaoProductSkuSalesRankView> selectSalesRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 单位销售排行
|
||||||
|
List<GaoProductSkuUnitSalesRankView> selectUnitSalesRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 每日履约统计
|
||||||
|
List<GaoProductSkuFulfillmentDailyStatView> selectFulfillmentDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 履约进度排行
|
||||||
|
List<GaoProductSkuFulfillmentRankView> selectFulfillmentRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 每日库存变动统计
|
||||||
|
List<GaoProductSkuStockMovementDailyStatView> selectStockMovementDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 库存健康度
|
||||||
|
List<GaoProductSkuStockHealthView> selectStockHealth(GaoProductChartReq req);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品 SKU Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSkuMapper extends BaseMapper<GaoProductSku, String> {
|
||||||
|
|
||||||
|
/// 按产品查询 SKU
|
||||||
|
List<GaoProductSku> selectByProductId(@Param("productId") String productId);
|
||||||
|
|
||||||
|
/// 批量按产品查询 SKU
|
||||||
|
List<GaoProductSku> selectByProductIdList(@Param("productIdList") Collection<String> productIdList);
|
||||||
|
|
||||||
|
/// 查询 SKU 编码
|
||||||
|
GaoProductSku selectByStoreIdAndCode(@Param("storeId") String storeId, @Param("code") String code, @Param("excludeId") String excludeId);
|
||||||
|
|
||||||
|
/// 查询 SKU 条码
|
||||||
|
GaoProductSku selectByStoreIdAndBarcode(@Param("storeId") String storeId, @Param("barcode") String barcode, @Param("excludeId") String excludeId);
|
||||||
|
|
||||||
|
/// 查询产品下相同规格值组合的 SKU
|
||||||
|
GaoProductSku selectByProductIdAndSpecValueKey(@Param("productId") String productId, @Param("specValueKey") String specValueKey, @Param("excludeId") String excludeId);
|
||||||
|
|
||||||
|
/// 锁定 SKU
|
||||||
|
GaoProductSku selectForUpdate(@Param("id") String id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuSpecValue;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO SKU 规格值关联 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSkuSpecValueMapper extends BaseMapper<GaoProductSkuSpecValue, String> {
|
||||||
|
|
||||||
|
/// 批量查询 SKU 规格值关联
|
||||||
|
List<GaoProductSkuSpecValue> selectBySkuIdList(@Param("skuIdList") Collection<String> skuIdList);
|
||||||
|
|
||||||
|
/// 删除 SKU 的规格值关联
|
||||||
|
void deleteBySkuId(@Param("skuId") String skuId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品 SKU 单位 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSkuUnitMapper extends BaseMapper<GaoProductSkuUnit, String> {
|
||||||
|
|
||||||
|
/// 按 SKU 查询有效单位
|
||||||
|
List<GaoProductSkuUnit> selectBySkuId(@Param("skuId") String skuId);
|
||||||
|
|
||||||
|
/// 批量查询 SKU 有效单位
|
||||||
|
List<GaoProductSkuUnit> selectBySkuIdList(@Param("skuIdList") Collection<String> skuIdList);
|
||||||
|
|
||||||
|
/// 锁定单位
|
||||||
|
GaoProductSkuUnit selectForUpdate(@Param("id") String id);
|
||||||
|
|
||||||
|
/// 更新有效单位
|
||||||
|
int updateUnit(GaoProductSkuUnit unit);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品规格项 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSpecMapper extends BaseMapper<GaoProductSpec, String> {
|
||||||
|
|
||||||
|
/// 按产品查询规格项
|
||||||
|
List<GaoProductSpec> selectByProductId(@Param("productId") String productId);
|
||||||
|
|
||||||
|
/// 批量按产品查询规格项
|
||||||
|
List<GaoProductSpec> selectByProductIdList(@Param("productIdList") Collection<String> productIdList);
|
||||||
|
|
||||||
|
/// 查询规格值所属产品
|
||||||
|
String selectProductIdBySpecValueId(@Param("specValueId") String specValueId);
|
||||||
|
|
||||||
|
/// 查询规格项所属产品
|
||||||
|
String selectProductIdBySpecId(@Param("specId") String specId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品规格值 Mapper
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSpecValueMapper extends BaseMapper<GaoProductSpecValue, String> {
|
||||||
|
|
||||||
|
/// 按 ID 查询有效规格值
|
||||||
|
GaoProductSpecValue selectById(@Param("id") String id);
|
||||||
|
|
||||||
|
/// 按规格项查询规格值
|
||||||
|
List<GaoProductSpecValue> selectBySpecIdList(@Param("specIdList") Collection<String> specIdList);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ import java.util.List;
|
|||||||
public interface GaoProductStockMapper extends BaseMapper<GaoProductStock, String> {
|
public interface GaoProductStockMapper extends BaseMapper<GaoProductStock, String> {
|
||||||
|
|
||||||
/// 锁定库存余额
|
/// 锁定库存余额
|
||||||
GaoProductStock selectForUpdate(@Param("storeId") String storeId, @Param("productId") String productId);
|
GaoProductStock selectForUpdate(@Param("storeId") String storeId, @Param("skuId") String skuId);
|
||||||
|
|
||||||
/// 按条件统计库存余额
|
/// 按条件统计库存余额
|
||||||
long countByQuery(GaoProductStockPage page);
|
long countByQuery(GaoProductStockPage page);
|
||||||
@@ -22,8 +23,14 @@ public interface GaoProductStockMapper extends BaseMapper<GaoProductStock, Strin
|
|||||||
/// 按条件查询库存余额
|
/// 按条件查询库存余额
|
||||||
List<GaoProductStock> selectByQuery(GaoProductStockPage page);
|
List<GaoProductStock> selectByQuery(GaoProductStockPage page);
|
||||||
|
|
||||||
|
/// 按产品汇总库存数量
|
||||||
|
long countProductByQuery(GaoProductStockPage page);
|
||||||
|
|
||||||
|
/// 按产品分页查询库存汇总
|
||||||
|
List<GaoProductStockProductView> selectProductByQuery(GaoProductStockPage page);
|
||||||
|
|
||||||
/// 按门店和产品查询库存余额
|
/// 按门店和产品查询库存余额
|
||||||
GaoProductStock selectByStoreIdAndProductId(@Param("storeId") String storeId, @Param("productId") String productId);
|
GaoProductStock selectByStoreIdAndSkuId(@Param("storeId") String storeId, @Param("skuId") String skuId);
|
||||||
|
|
||||||
/// 条件更新库存余额
|
/// 条件更新库存余额
|
||||||
int updateQuantity(@Param("id") String id, @Param("onHandDelta") java.math.BigDecimal onHandDelta, @Param("reservedDelta") java.math.BigDecimal reservedDelta, @Param("updatedBy") String updatedBy);
|
int updateQuantity(@Param("id") String id, @Param("onHandDelta") java.math.BigDecimal onHandDelta, @Param("reservedDelta") java.math.BigDecimal reservedDelta, @Param("updatedBy") String updatedBy);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public interface GaoProductStockRecordMapper extends BaseMapper<GaoProductStockR
|
|||||||
List<GaoProductStockRecord> selectByBiz(@Param("bizType") GaoProductStockRecord.BizType bizType, @Param("bizId") String bizId);
|
List<GaoProductStockRecord> selectByBiz(@Param("bizType") GaoProductStockRecord.BizType bizType, @Param("bizId") String bizId);
|
||||||
|
|
||||||
/// 查询指定业务和产品的库存流水
|
/// 查询指定业务和产品的库存流水
|
||||||
GaoProductStockRecord selectByBizProduct(@Param("bizType") GaoProductStockRecord.BizType bizType, @Param("bizId") String bizId, @Param("productId") String productId, @Param("operationType") GaoProductStockRecord.OperationType operationType);
|
GaoProductStockRecord selectByBizSku(@Param("bizType") GaoProductStockRecord.BizType bizType, @Param("bizId") String bizId, @Param("skuId") String skuId, @Param("operationType") GaoProductStockRecord.OperationType operationType);
|
||||||
|
|
||||||
/// 按条件统计库存流水
|
/// 按条件统计库存流水
|
||||||
long countByQuery(GaoProductStockRecordPage page);
|
long countByQuery(GaoProductStockRecordPage page);
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.gao.mapper;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
|
||||||
import org.apache.ibatis.annotations.Param;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/// GAO 产品单位 Mapper
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-09-02
|
|
||||||
public interface GaoProductUnitMapper extends BaseMapper<GaoProductUnit, String> {
|
|
||||||
|
|
||||||
/// 按产品查询有效单位
|
|
||||||
List<GaoProductUnit> selectByProductId(@Param("productId") String productId);
|
|
||||||
|
|
||||||
/// 批量查询产品有效单位
|
|
||||||
List<GaoProductUnit> selectByProductIdList(@Param("productIdList") Collection<String> productIdList);
|
|
||||||
|
|
||||||
/// 锁定单位
|
|
||||||
GaoProductUnit selectForUpdate(@Param("id") String id);
|
|
||||||
|
|
||||||
/// 更新有效单位
|
|
||||||
int updateUnit(GaoProductUnit unit);
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderSkuRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
||||||
@@ -36,6 +37,9 @@ public interface GaoSalesOrderChartMapper {
|
|||||||
/// 查询客户订单产品排行
|
/// 查询客户订单产品排行
|
||||||
List<GaoCustomerOrderProductRankView> selectCustomerProductRank(GaoSalesOrderChartReq req);
|
List<GaoCustomerOrderProductRankView> selectCustomerProductRank(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
|
/// 查询客户订单 SKU 排行
|
||||||
|
List<GaoCustomerOrderSkuRankView> selectCustomerSkuRank(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
/// 查询销售订单状态统计
|
/// 查询销售订单状态统计
|
||||||
List<GaoSalesOrderStatusStatView> selectStatus(GaoSalesOrderChartReq req);
|
List<GaoSalesOrderStatusStatView> selectStatus(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.gao.service;
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection;
|
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
import com.imyeyu.spring.service.BaseService;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
@@ -28,21 +26,6 @@ public interface GaoProductService extends BaseService<GaoProduct, String> {
|
|||||||
/// 查询启用且允许销售的产品单位
|
/// 查询启用且允许销售的产品单位
|
||||||
List<GaoProduct> listSellable(GaoProductPage page);
|
List<GaoProduct> listSellable(GaoProductPage page);
|
||||||
|
|
||||||
/// 查询并校验可销售产品单位
|
|
||||||
GaoProductUnitSelection getSellableProductUnit(String storeId, String productId, String productUnitId);
|
|
||||||
|
|
||||||
/// 查询当前门店的有效产品单位
|
|
||||||
///
|
|
||||||
/// @param storeId 门店 ID
|
|
||||||
/// @param productId 产品 ID
|
|
||||||
/// @param productUnitId 产品单位 ID
|
|
||||||
/// @param purchase true 为按采购入库用途校验
|
|
||||||
/// @return 已校验的产品单位
|
|
||||||
GaoProductUnitSelection getProductUnit(String storeId, String productId, String productUnitId, boolean purchase);
|
|
||||||
|
|
||||||
/// 查询产品单位
|
|
||||||
List<GaoProductUnit> listUnitByProductId(String productId);
|
|
||||||
|
|
||||||
/// 停用产品
|
/// 停用产品
|
||||||
void disable(String id);
|
void disable(String id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockHealthView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockMovementDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSalesRankView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO SKU 图表服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
public interface GaoProductSkuChartService {
|
||||||
|
|
||||||
|
/// 查询 SKU 每日销售统计
|
||||||
|
List<GaoProductSkuSalesDailyStatView> salesDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 销售排行
|
||||||
|
List<GaoProductSkuSalesRankView> salesRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 单位销售排行
|
||||||
|
List<GaoProductSkuUnitSalesRankView> unitSalesRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 每日履约统计
|
||||||
|
List<GaoProductSkuFulfillmentDailyStatView> fulfillmentDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 履约进度排行
|
||||||
|
List<GaoProductSkuFulfillmentRankView> fulfillmentRank(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 每日库存变动统计
|
||||||
|
List<GaoProductSkuStockMovementDailyStatView> stockMovementDaily(GaoProductChartReq req);
|
||||||
|
|
||||||
|
/// 查询 SKU 库存健康度
|
||||||
|
List<GaoProductSkuStockHealthView> stockHealth(GaoProductChartReq req);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品 SKU 服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSkuService extends BaseService<GaoProductSku, String> {
|
||||||
|
|
||||||
|
/// 查询 SKU 详情
|
||||||
|
GaoProductSku detail(String id);
|
||||||
|
|
||||||
|
/// 按产品查询 SKU
|
||||||
|
List<GaoProductSku> listByProductId(String productId);
|
||||||
|
|
||||||
|
/// 批量按产品查询 SKU
|
||||||
|
List<GaoProductSku> listByProductIdList(Collection<String> productIdList);
|
||||||
|
|
||||||
|
/// 查询并校验可销售 SKU 单位
|
||||||
|
GaoProductSkuUnitSelection getSellableSkuUnit(String storeId, String skuId, String skuUnitId);
|
||||||
|
|
||||||
|
/// 查询并校验 SKU 单位
|
||||||
|
GaoProductSkuUnitSelection getSkuUnit(String storeId, String skuId, String skuUnitId, boolean purchase);
|
||||||
|
|
||||||
|
/// 查询 SKU 单位
|
||||||
|
List<GaoProductSkuUnit> listUnitBySkuId(String skuId);
|
||||||
|
|
||||||
|
/// 停用 SKU
|
||||||
|
void disable(String id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO 产品规格服务
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
public interface GaoProductSpecService {
|
||||||
|
|
||||||
|
/// 查询产品规格
|
||||||
|
List<GaoProductSpec> listByProductId(String productId);
|
||||||
|
|
||||||
|
/// 查询规格项所属产品 ID
|
||||||
|
String getProductIdBySpecId(String specId);
|
||||||
|
|
||||||
|
/// 创建规格项
|
||||||
|
void createSpec(GaoProductSpec spec);
|
||||||
|
|
||||||
|
/// 创建规格值
|
||||||
|
void createValue(GaoProductSpecValue value);
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ public interface GaoProductStockRecordService extends BaseService<GaoProductStoc
|
|||||||
GaoProductStockRecord getRevokeBySourceRecordId(String sourceRecordId);
|
GaoProductStockRecord getRevokeBySourceRecordId(String sourceRecordId);
|
||||||
|
|
||||||
/// 查询指定业务和产品的库存流水
|
/// 查询指定业务和产品的库存流水
|
||||||
GaoProductStockRecord getByBizProduct(GaoProductStockRecord.BizType bizType, String bizId, String productId,
|
GaoProductStockRecord getByBizSku(GaoProductStockRecord.BizType bizType, String bizId, String skuId,
|
||||||
GaoProductStockRecord.OperationType operationType);
|
GaoProductStockRecord.OperationType operationType);
|
||||||
|
|
||||||
/// 保存库存操作流水及附件
|
/// 保存库存操作流水及附件
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
|||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
|
||||||
@@ -18,8 +19,11 @@ public interface GaoProductStockService {
|
|||||||
/// 查询库存余额
|
/// 查询库存余额
|
||||||
PageResult<GaoProductStock> pageByQuery(GaoProductStockPage page);
|
PageResult<GaoProductStock> pageByQuery(GaoProductStockPage page);
|
||||||
|
|
||||||
|
/// 按产品汇总查询库存
|
||||||
|
PageResult<GaoProductStockProductView> pageProductByQuery(GaoProductStockPage page);
|
||||||
|
|
||||||
/// 查询库存余额详情
|
/// 查询库存余额详情
|
||||||
GaoProductStock detail(String storeId, String productId);
|
GaoProductStock detail(String storeId, String skuId);
|
||||||
|
|
||||||
/// 期初入库
|
/// 期初入库
|
||||||
GaoProductStock initialIn(GaoProductStockChangeRequest request);
|
GaoProductStock initialIn(GaoProductStockChangeRequest request);
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.gao.service;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/// GAO 产品单位服务
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-09-02
|
|
||||||
public interface GaoProductUnitService {
|
|
||||||
|
|
||||||
/// 校验并规范化产品单位
|
|
||||||
///
|
|
||||||
/// @param product 产品
|
|
||||||
/// @param unitList 单位列表
|
|
||||||
/// @return 规范化后的单位列表
|
|
||||||
List<GaoProductUnit> validateAndNormalize(GaoProduct product, List<GaoProductUnit> unitList);
|
|
||||||
|
|
||||||
/// 按基础单位数量系数换算业务数量
|
|
||||||
///
|
|
||||||
/// @param unit 单位
|
|
||||||
/// @param quantity 业务数量
|
|
||||||
/// @return 基础单位数量
|
|
||||||
BigDecimal convertQuantityToBase(GaoProductUnit unit, BigDecimal quantity);
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.gao.service;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderSkuRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
||||||
@@ -36,6 +37,9 @@ public interface GaoSalesOrderChartService {
|
|||||||
/// 查询客户订单产品排行
|
/// 查询客户订单产品排行
|
||||||
List<GaoCustomerOrderProductRankView> customerProductRank(GaoSalesOrderChartReq req);
|
List<GaoCustomerOrderProductRankView> customerProductRank(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
|
/// 查询客户订单 SKU 排行
|
||||||
|
List<GaoCustomerOrderSkuRankView> customerSkuRank(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
/// 查询销售订单状态统计
|
/// 查询销售订单状态统计
|
||||||
List<GaoSalesOrderStatusStatView> status(GaoSalesOrderChartReq req);
|
List<GaoSalesOrderStatusStatView> status(GaoSalesOrderChartReq req);
|
||||||
|
|
||||||
|
|||||||
+32
-20
@@ -15,7 +15,7 @@ import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackageCancelRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerPackagePage;
|
||||||
@@ -55,7 +55,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
|
|
||||||
private final GaoCustomerPackageMapper mapper;
|
private final GaoCustomerPackageMapper mapper;
|
||||||
private final GaoPackageConfigService packageConfigService;
|
private final GaoPackageConfigService packageConfigService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoCustomerService customerService;
|
private final GaoCustomerService customerService;
|
||||||
private final GaoSalesOrderService salesOrderService;
|
private final GaoSalesOrderService salesOrderService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
@@ -237,9 +237,13 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
private List<GaoCustomerPackagePurchaseItem> defaultPurchaseItems(List<GaoPackageConfigItem> configItemList) {
|
private List<GaoCustomerPackagePurchaseItem> defaultPurchaseItems(List<GaoPackageConfigItem> configItemList) {
|
||||||
return configItemList.stream().map(configItem -> {
|
return configItemList.stream().map(configItem -> {
|
||||||
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem();
|
||||||
item.setProductId(configItem.getProductId());
|
item.setSkuId(configItem.getSkuId());
|
||||||
item.setProductUnitId(configItem.getProductUnitId());
|
item.setSkuUnitId(configItem.getSkuUnitId());
|
||||||
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
||||||
|
item.setProductIdSnapshot(configItem.getProductIdSnapshot());
|
||||||
|
item.setSkuCodeSnapshot(configItem.getSkuCodeSnapshot());
|
||||||
|
item.setSkuNameSnapshot(configItem.getSkuNameSnapshot());
|
||||||
|
item.setSpecSummarySnapshot(configItem.getSpecSummarySnapshot());
|
||||||
item.setUnitSnapshot(configItem.getUnitSnapshot());
|
item.setUnitSnapshot(configItem.getUnitSnapshot());
|
||||||
item.setBaseQuantityPerUnitSnapshot(configItem.getBaseQuantityPerUnitSnapshot());
|
item.setBaseQuantityPerUnitSnapshot(configItem.getBaseQuantityPerUnitSnapshot());
|
||||||
item.setTotalQuantity(configItem.getDefaultQuantity());
|
item.setTotalQuantity(configItem.getDefaultQuantity());
|
||||||
@@ -252,9 +256,13 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
private GaoSalesOrderItem toOrderItem(GaoCustomerPackagePurchaseItem source) {
|
private GaoSalesOrderItem toOrderItem(GaoCustomerPackagePurchaseItem source) {
|
||||||
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
||||||
item.setItemType(GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT);
|
item.setItemType(GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT);
|
||||||
item.setProductId(source.getProductId());
|
item.setSkuId(source.getSkuId());
|
||||||
item.setProductUnitId(source.getProductUnitId());
|
item.setSkuUnitId(source.getSkuUnitId());
|
||||||
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||||
|
item.setProductIdSnapshot(source.getProductIdSnapshot());
|
||||||
|
item.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
||||||
|
item.setSkuNameSnapshot(source.getSkuNameSnapshot());
|
||||||
|
item.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
|
||||||
item.setUnitSnapshot(source.getUnitSnapshot());
|
item.setUnitSnapshot(source.getUnitSnapshot());
|
||||||
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||||
item.setQuantity(source.getTotalQuantity());
|
item.setQuantity(source.getTotalQuantity());
|
||||||
@@ -273,33 +281,37 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
for (int i = 0; i < itemList.size(); i++) {
|
for (int i = 0; i < itemList.size(); i++) {
|
||||||
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
GaoCustomerPackagePurchaseItem item = itemList.get(i);
|
||||||
TimiException.required(item, "购买产品明细不能为空");
|
TimiException.required(item, "购买产品明细不能为空");
|
||||||
TimiException.required(item.getProductId(), "购买产品不能为空");
|
TimiException.required(item.getSkuId(), "购买 SKU 不能为空");
|
||||||
TimiException.required(item.getProductUnitId(), "购买产品单位不能为空");
|
TimiException.required(item.getSkuUnitId(), "购买 SKU 单位不能为空");
|
||||||
TimiException.required(item.getTotalQuantity(), "购买产品数量不能为空");
|
TimiException.required(item.getTotalQuantity(), "购买产品数量不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getTotalQuantity().signum(), "购买产品数量必须大于 0");
|
TimiException.requiredTrue(0 < item.getTotalQuantity().signum(), "购买产品数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= item.getTotalQuantity().stripTrailingZeros().scale(), "购买产品数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= item.getTotalQuantity().stripTrailingZeros().scale(), "购买产品数量最多保留三位小数");
|
||||||
String key = item.getProductId() + "|" + item.getProductUnitId();
|
String key = item.getSkuId() + "|" + item.getSkuUnitId();
|
||||||
TimiException.requiredTrue(keySet.add(key), "购买套餐内产品和单位不能重复");
|
TimiException.requiredTrue(keySet.add(key), "购买套餐内 SKU 和单位不能重复");
|
||||||
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyConfigSellableItems(String storeId, List<GaoPackageConfigItem> itemList) {
|
private void verifyConfigSellableItems(String storeId, List<GaoPackageConfigItem> itemList) {
|
||||||
for (GaoPackageConfigItem item : itemList) {
|
for (GaoPackageConfigItem item : itemList) {
|
||||||
TimiException.required(item.getProductId(), "套餐配置缺少产品");
|
TimiException.required(item.getSkuId(), "套餐配置缺少 SKU");
|
||||||
TimiException.required(item.getProductUnitId(), "套餐配置缺少产品单位");
|
TimiException.required(item.getSkuUnitId(), "套餐配置缺少 SKU 单位");
|
||||||
// 通过产品库唯一选品入口重新校验产品和单位仍处于可销售状态
|
// 通过 SKU 服务唯一选品入口重新校验 SKU 和单位仍处于可销售状态
|
||||||
productService.getSellableProductUnit(storeId, item.getProductId(), item.getProductUnitId());
|
skuService.getSellableSkuUnit(storeId, item.getSkuId(), item.getSkuUnitId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifySameStructureAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
private void verifySameStructureAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
||||||
TimiException.requiredTrue(itemList.size() == configItemList.size(), "套餐权益项目必须与套餐配置一致");
|
TimiException.requiredTrue(itemList.size() == configItemList.size(), "套餐权益项目必须与套餐配置一致");
|
||||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), Function.identity()));
|
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
||||||
for (GaoPackageConfigItem configItem : configItemList) {
|
for (GaoPackageConfigItem configItem : configItemList) {
|
||||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductId() + "|" + configItem.getProductUnitId());
|
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getSkuId() + "|" + configItem.getSkuUnitId());
|
||||||
TimiException.required(item, "套餐权益项目必须与套餐配置一致");
|
TimiException.required(item, "套餐权益项目必须与套餐配置一致");
|
||||||
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
item.setProductNameSnapshot(configItem.getProductNameSnapshot());
|
||||||
|
item.setProductIdSnapshot(configItem.getProductIdSnapshot());
|
||||||
|
item.setSkuCodeSnapshot(configItem.getSkuCodeSnapshot());
|
||||||
|
item.setSkuNameSnapshot(configItem.getSkuNameSnapshot());
|
||||||
|
item.setSpecSummarySnapshot(configItem.getSpecSummarySnapshot());
|
||||||
item.setUnitSnapshot(configItem.getUnitSnapshot());
|
item.setUnitSnapshot(configItem.getUnitSnapshot());
|
||||||
item.setBaseQuantityPerUnitSnapshot(configItem.getBaseQuantityPerUnitSnapshot());
|
item.setBaseQuantityPerUnitSnapshot(configItem.getBaseQuantityPerUnitSnapshot());
|
||||||
TimiException.requiredTrue(item.getSort().equals(configItem.getSort())
|
TimiException.requiredTrue(item.getSort().equals(configItem.getSort())
|
||||||
@@ -308,9 +320,9 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void verifySameQuantityAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
private void verifySameQuantityAsConfig(List<GaoCustomerPackagePurchaseItem> itemList, List<GaoPackageConfigItem> configItemList) {
|
||||||
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), Function.identity()));
|
Map<String, GaoCustomerPackagePurchaseItem> itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
||||||
for (GaoPackageConfigItem configItem : configItemList) {
|
for (GaoPackageConfigItem configItem : configItemList) {
|
||||||
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductId() + "|" + configItem.getProductUnitId());
|
GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getSkuId() + "|" + configItem.getSkuUnitId());
|
||||||
TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0, "普通员工不能修改套餐权益配额");
|
TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0, "普通员工不能修改套餐权益配额");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,9 +339,9 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
List<GaoCustomerPackagePurchaseItem> requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of());
|
||||||
verifyPurchaseItems(requestItemList);
|
verifyPurchaseItems(requestItemList);
|
||||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致");
|
||||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), Function.identity()));
|
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity()));
|
||||||
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
for (GaoCustomerPackagePurchaseItem item : requestItemList) {
|
||||||
GaoSalesOrderItem existingItem = existingItemMap.get(item.getProductId() + "|" + item.getProductUnitId());
|
GaoSalesOrderItem existingItem = existingItemMap.get(item.getSkuId() + "|" + item.getSkuUnitId());
|
||||||
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
TimiException.required(existingItem, "requestNo 对应购买请求内容不一致");
|
||||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||||
&& existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0
|
&& existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0
|
||||||
|
|||||||
+50
-38
@@ -6,7 +6,8 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
|||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
||||||
@@ -17,7 +18,7 @@ import com.imyeyu.api.modules.gao.mapper.GaoOrderFulfillmentMapper;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
@@ -68,7 +69,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
private final GaoOrderFulfillmentItemMapper itemMapper;
|
private final GaoOrderFulfillmentItemMapper itemMapper;
|
||||||
private final GaoSalesOrderService salesOrderService;
|
private final GaoSalesOrderService salesOrderService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductService productService;
|
||||||
private final GaoProductUnitService unitService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoProductStockService productStockService;
|
private final GaoProductStockService productStockService;
|
||||||
private final GaoProductStockRecordService stockRecordService;
|
private final GaoProductStockRecordService stockRecordService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
@@ -359,8 +360,8 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
}
|
}
|
||||||
if (!stockItems.isEmpty()) {
|
if (!stockItems.isEmpty()) {
|
||||||
for (GaoStockChangeItem item : stockItems) {
|
for (GaoStockChangeItem item : stockItems) {
|
||||||
GaoProductStockRecord stockRecord = stockRecordService.getByBizProduct(GaoProductStockRecord.BizType.ORDER_FULFILLMENT,
|
GaoProductStockRecord stockRecord = stockRecordService.getByBizSku(GaoProductStockRecord.BizType.ORDER_FULFILLMENT,
|
||||||
source.getId(), item.getProductId(), GaoProductStockRecord.OperationType.ORDER_OUT);
|
source.getId(), item.getSkuId(), GaoProductStockRecord.OperationType.ORDER_OUT);
|
||||||
if (stockRecord != null) {
|
if (stockRecord != null) {
|
||||||
item.setSourceRecordId(stockRecord.getId());
|
item.setSourceRecordId(stockRecord.getId());
|
||||||
}
|
}
|
||||||
@@ -444,21 +445,24 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, BigDecimal quantity, boolean checkAvailable) {
|
private SourceEntry toSource(GaoSalesOrder order, GaoSalesOrderItem source, BigDecimal quantity, boolean checkAvailable) {
|
||||||
GaoProduct product = productService.get(source.getProductId());
|
GaoProductSku sku = skuService.get(source.getSkuId());
|
||||||
|
TimiException.required(sku, "履约来源 SKU 不存在");
|
||||||
|
GaoProduct product = productService.get(sku.getProductId());
|
||||||
TimiException.required(product, "履约来源产品不存在");
|
TimiException.required(product, "履约来源产品不存在");
|
||||||
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
|
||||||
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
|
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
|
||||||
GaoProductUnit unit = snapshotUnit(source.getProductUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
|
GaoProductSkuUnit unit = snapshotUnit(source.getSkuUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
|
||||||
return buildSource(order, source.getId(), source.getProductId(), source.getProductUnitId(), source.getProductNameSnapshot(),
|
return buildSource(order, source.getId(), source.getSkuId(), source.getSkuUnitId(), source.getProductIdSnapshot(), source.getProductNameSnapshot(), source.getSkuCodeSnapshot(), source.getSkuNameSnapshot(), source.getSpecSummarySnapshot(),
|
||||||
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getQuantity(), source.getReservedQuantity(), source.getFulfilledQuantity(),
|
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getQuantity(), source.getReservedQuantity(), source.getFulfilledQuantity(),
|
||||||
product, unit, checkAvailable);
|
product, unit, checkAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String productId, String unitId,
|
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String skuId, String unitId, String productId,
|
||||||
String productName, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
|
String productName, String skuCode, String skuName, String specSummary, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
|
||||||
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductUnit unit,
|
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductSkuUnit unit,
|
||||||
boolean checkAvailable) {
|
boolean checkAvailable) {
|
||||||
if (!Boolean.TRUE.equals(product.getAllowDecimalQuantity())) {
|
GaoProductSku sku = skuService.get(skuId);
|
||||||
|
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
||||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
||||||
}
|
}
|
||||||
BigDecimal reservedBefore = decimal(reservedQuantity);
|
BigDecimal reservedBefore = decimal(reservedQuantity);
|
||||||
@@ -467,30 +471,30 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
BigDecimal available = decimal(totalQuantity).subtract(reservedBefore).subtract(fulfilledBefore);
|
BigDecimal available = decimal(totalQuantity).subtract(reservedBefore).subtract(fulfilledBefore);
|
||||||
TimiException.requiredTrue(0 <= available.compareTo(quantity), "来源产品可履约数量不足");
|
TimiException.requiredTrue(0 <= available.compareTo(quantity), "来源产品可履约数量不足");
|
||||||
}
|
}
|
||||||
return new SourceEntry(orderItemId, productId, unitId, productName, unitName, baseQuantityPerUnit,
|
return new SourceEntry(orderItemId, skuId, unitId, productId, productName, skuCode, skuName, specSummary, unitName, baseQuantityPerUnit,
|
||||||
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, unit);
|
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, sku, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<GaoStockChangeItem> buildStockItems(String storeId, List<SourceEntry> sourceList) {
|
private List<GaoStockChangeItem> buildStockItems(String storeId, List<SourceEntry> sourceList) {
|
||||||
Map<String, GaoStockChangeItem> itemMap = new LinkedHashMap<>();
|
Map<String, GaoStockChangeItem> itemMap = new LinkedHashMap<>();
|
||||||
for (SourceEntry source : sourceList) {
|
for (SourceEntry source : sourceList) {
|
||||||
if (!Boolean.TRUE.equals(source.product().getStockEnabled())) {
|
if (!Boolean.TRUE.equals(source.sku().getStockEnabled())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GaoProductUnit baseUnit = productService.listUnitByProductId(source.product().getId()).stream()
|
GaoProductSkuUnit baseUnit = skuService.listUnitBySkuId(source.sku().getId()).stream()
|
||||||
.filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElse(null);
|
.filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElse(null);
|
||||||
TimiException.required(baseUnit, "实物产品缺少基础库存单位");
|
TimiException.required(baseUnit, "实物产品缺少基础库存单位");
|
||||||
BigDecimal baseQuantity = unitService.convertQuantityToBase(source.unit(), source.quantity());
|
BigDecimal baseQuantity = source.quantity().multiply(source.unit().getBaseQuantityPerUnit());
|
||||||
GaoStockChangeItem item = itemMap.get(source.product().getId());
|
GaoStockChangeItem item = itemMap.get(source.sku().getId());
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = GaoStockChangeItem.of(source.product(), baseUnit, baseQuantity, baseQuantity);
|
item = GaoStockChangeItem.of(source.product(), source.sku(), baseUnit, baseQuantity, baseQuantity);
|
||||||
itemMap.put(source.product().getId(), item);
|
itemMap.put(source.sku().getId(), item);
|
||||||
} else {
|
} else {
|
||||||
item.setQuantity(item.getQuantity().add(baseQuantity));
|
item.setQuantity(item.getQuantity().add(baseQuantity));
|
||||||
item.setBaseQuantity(item.getBaseQuantity().add(baseQuantity));
|
item.setBaseQuantity(item.getBaseQuantity().add(baseQuantity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return itemMap.values().stream().sorted(Comparator.comparing(GaoStockChangeItem::getProductId)).toList();
|
return itemMap.values().stream().sorted(Comparator.comparing(GaoStockChangeItem::getSkuId)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoOrderFulfillment lockFulfillment(String fulfillmentId, GaoSalesOrder order) {
|
private GaoOrderFulfillment lockFulfillment(String fulfillmentId, GaoSalesOrder order) {
|
||||||
@@ -517,19 +521,23 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
|
|
||||||
private GaoOrderFulfillmentItem copyFulfillmentItem(GaoOrderFulfillmentItem source) {
|
private GaoOrderFulfillmentItem copyFulfillmentItem(GaoOrderFulfillmentItem source) {
|
||||||
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
||||||
result.setProductId(source.getProductId());
|
result.setSkuId(source.getSkuId());
|
||||||
result.setProductUnitId(source.getProductUnitId());
|
result.setSkuUnitId(source.getSkuUnitId());
|
||||||
|
result.setProductIdSnapshot(source.getProductIdSnapshot());
|
||||||
result.setSourceOrderItemId(source.getSourceOrderItemId());
|
result.setSourceOrderItemId(source.getSourceOrderItemId());
|
||||||
result.setQuantity(source.getQuantity());
|
result.setQuantity(source.getQuantity());
|
||||||
result.setBaseQuantity(source.getBaseQuantity());
|
result.setBaseQuantity(source.getBaseQuantity());
|
||||||
result.setProductNameSnapshot(source.getProductNameSnapshot());
|
result.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||||
|
result.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
||||||
|
result.setSkuNameSnapshot(source.getSkuNameSnapshot());
|
||||||
|
result.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
|
||||||
result.setUnitSnapshot(source.getUnitSnapshot());
|
result.setUnitSnapshot(source.getUnitSnapshot());
|
||||||
result.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
result.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoProductUnit snapshotUnit(String id, String name, BigDecimal baseQuantityPerUnit) {
|
private GaoProductSkuUnit snapshotUnit(String id, String name, BigDecimal baseQuantityPerUnit) {
|
||||||
GaoProductUnit unit = new GaoProductUnit();
|
GaoProductSkuUnit unit = new GaoProductSkuUnit();
|
||||||
unit.setId(id);
|
unit.setId(id);
|
||||||
unit.setName(name);
|
unit.setName(name);
|
||||||
unit.setBaseQuantityPerUnit(baseQuantityPerUnit);
|
unit.setBaseQuantityPerUnit(baseQuantityPerUnit);
|
||||||
@@ -619,27 +627,27 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
if (itemList == null || itemList.isEmpty()) {
|
if (itemList == null || itemList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> productIdList = itemList.stream()
|
List<String> skuIdList = itemList.stream()
|
||||||
.map(GaoOrderFulfillmentItem::getProductId)
|
.map(GaoOrderFulfillmentItem::getSkuId)
|
||||||
.filter(value -> value != null && !value.isBlank())
|
.filter(value -> value != null && !value.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
if (productIdList.isEmpty()) {
|
if (skuIdList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
||||||
Attachment.BizType.GAO_PRODUCT,
|
Attachment.BizType.GAO_PRODUCT_SKU,
|
||||||
productIdList,
|
skuIdList);
|
||||||
GaoProduct.AttachType.PHOTO.name());
|
|
||||||
for (GaoOrderFulfillmentItem item : itemList) {
|
for (GaoOrderFulfillmentItem item : itemList) {
|
||||||
item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of()));
|
item.setAttachmentList(attachmentMap.getOrDefault(item.getSkuId(), List.of()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record SourceEntry(String sourceOrderItemId, String productId, String productUnitId,
|
private record SourceEntry(String sourceOrderItemId, String skuId, String skuUnitId, String productIdSnapshot,
|
||||||
String productNameSnapshot, String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
|
String productNameSnapshot, String skuCodeSnapshot, String skuNameSnapshot, String specSummarySnapshot,
|
||||||
BigDecimal totalQuantity, BigDecimal reservedBefore, BigDecimal fulfilledBefore, GaoProduct product,
|
String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
|
||||||
GaoProductUnit unit) {
|
BigDecimal totalQuantity, BigDecimal reservedBefore, BigDecimal fulfilledBefore, GaoProduct product, GaoProductSku sku,
|
||||||
|
GaoProductSkuUnit unit) {
|
||||||
|
|
||||||
private String sourceId() {
|
private String sourceId() {
|
||||||
return sourceOrderItemId;
|
return sourceOrderItemId;
|
||||||
@@ -648,8 +656,9 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
private GaoOrderFulfillmentItem toFulfillmentItem(String fulfillmentId) {
|
private GaoOrderFulfillmentItem toFulfillmentItem(String fulfillmentId) {
|
||||||
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
GaoOrderFulfillmentItem result = new GaoOrderFulfillmentItem();
|
||||||
result.setFulfillmentId(fulfillmentId);
|
result.setFulfillmentId(fulfillmentId);
|
||||||
result.setProductId(productId);
|
result.setSkuId(skuId);
|
||||||
result.setProductUnitId(productUnitId);
|
result.setSkuUnitId(skuUnitId);
|
||||||
|
result.setProductIdSnapshot(productIdSnapshot);
|
||||||
result.setSourceOrderItemId(sourceOrderItemId);
|
result.setSourceOrderItemId(sourceOrderItemId);
|
||||||
result.setQuantity(quantity);
|
result.setQuantity(quantity);
|
||||||
result.setBaseQuantity(quantity.multiply(baseQuantityPerUnitSnapshot).setScale(3));
|
result.setBaseQuantity(quantity.multiply(baseQuantityPerUnitSnapshot).setScale(3));
|
||||||
@@ -658,6 +667,9 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
result.setFulfilledBefore(fulfilledBefore);
|
result.setFulfilledBefore(fulfilledBefore);
|
||||||
result.setFulfilledAfter(fulfilledBefore);
|
result.setFulfilledAfter(fulfilledBefore);
|
||||||
result.setProductNameSnapshot(productNameSnapshot);
|
result.setProductNameSnapshot(productNameSnapshot);
|
||||||
|
result.setSkuCodeSnapshot(skuCodeSnapshot);
|
||||||
|
result.setSkuNameSnapshot(skuNameSnapshot);
|
||||||
|
result.setSpecSummarySnapshot(specSummarySnapshot);
|
||||||
result.setUnitSnapshot(unitSnapshot);
|
result.setUnitSnapshot(unitSnapshot);
|
||||||
result.setBaseQuantityPerUnitSnapshot(baseQuantityPerUnitSnapshot);
|
result.setBaseQuantityPerUnitSnapshot(baseQuantityPerUnitSnapshot);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+25
-17
@@ -4,16 +4,16 @@ import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
|||||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
import com.imyeyu.api.modules.gao.entity.GaoPackageConfig;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigItemMapper;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoPackageConfigMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection;
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
@@ -42,8 +42,7 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
|
|
||||||
private final GaoPackageConfigMapper mapper;
|
private final GaoPackageConfigMapper mapper;
|
||||||
private final GaoPackageConfigItemMapper itemMapper;
|
private final GaoPackageConfigItemMapper itemMapper;
|
||||||
private final GaoProductService productService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoProductUnitService unitService;
|
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
private final LoggerService loggerService;
|
||||||
|
|
||||||
@@ -184,22 +183,27 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
for (int i = 0; i < itemList.size(); i++) {
|
for (int i = 0; i < itemList.size(); i++) {
|
||||||
GaoPackageConfigItem item = itemList.get(i);
|
GaoPackageConfigItem item = itemList.get(i);
|
||||||
TimiException.required(item, "套餐产品明细不能为空");
|
TimiException.required(item, "套餐产品明细不能为空");
|
||||||
TimiException.required(item.getProductId(), "产品不能为空");
|
TimiException.required(item.getSkuId(), "SKU 不能为空");
|
||||||
TimiException.required(item.getProductUnitId(), "产品单位不能为空");
|
TimiException.required(item.getSkuUnitId(), "SKU 单位不能为空");
|
||||||
GaoProductUnitSelection selection = productService.getSellableProductUnit(storeId, item.getProductId(), item.getProductUnitId());
|
GaoProductSkuUnitSelection selection = skuService.getSellableSkuUnit(storeId, item.getSkuId(), item.getSkuUnitId());
|
||||||
GaoProduct product = selection.getProduct();
|
GaoProduct product = selection.getProduct();
|
||||||
GaoProductUnit unitEntity = selection.getUnit();
|
GaoProductSku sku = selection.getSku();
|
||||||
|
GaoProductSkuUnit unitEntity = selection.getUnit();
|
||||||
BigDecimal quantity = item.getDefaultQuantity();
|
BigDecimal quantity = item.getDefaultQuantity();
|
||||||
TimiException.required(quantity, "产品默认数量不能为空");
|
TimiException.required(quantity, "产品默认数量不能为空");
|
||||||
TimiException.requiredTrue(0 < quantity.signum(), "产品默认数量必须大于 0");
|
TimiException.requiredTrue(0 < quantity.signum(), "产品默认数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "产品数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "产品数量最多保留三位小数");
|
||||||
if (!Boolean.TRUE.equals(product.getAllowDecimalQuantity())) {
|
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
||||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该 SKU 不允许小数数量");
|
||||||
}
|
}
|
||||||
unitService.convertQuantityToBase(unitEntity, quantity);
|
quantity.multiply(unitEntity.getBaseQuantityPerUnit());
|
||||||
String key = item.getProductId() + "|" + item.getProductUnitId();
|
String key = item.getSkuId() + "|" + item.getSkuUnitId();
|
||||||
TimiException.requiredTrue(keySet.add(key), "同一套餐内产品和单位不能重复");
|
TimiException.requiredTrue(keySet.add(key), "同一套餐内 SKU 和单位不能重复");
|
||||||
item.setProductNameSnapshot(product.getName());
|
item.setProductNameSnapshot(product.getName());
|
||||||
|
item.setProductIdSnapshot(product.getId());
|
||||||
|
item.setSkuCodeSnapshot(sku.getCode());
|
||||||
|
item.setSkuNameSnapshot(sku.getName());
|
||||||
|
item.setSpecSummarySnapshot(sku.getSpecSummary());
|
||||||
item.setUnitSnapshot(unitEntity.getName());
|
item.setUnitSnapshot(unitEntity.getName());
|
||||||
item.setBaseQuantityPerUnitSnapshot(unitEntity.getBaseQuantityPerUnit());
|
item.setBaseQuantityPerUnitSnapshot(unitEntity.getBaseQuantityPerUnit());
|
||||||
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
item.setSort(TimiJava.defaultIfNull(item.getSort(), i));
|
||||||
@@ -210,9 +214,13 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService<GaoP
|
|||||||
for (GaoPackageConfigItem source : TimiJava.safeIterable(itemList)) {
|
for (GaoPackageConfigItem source : TimiJava.safeIterable(itemList)) {
|
||||||
GaoPackageConfigItem item = new GaoPackageConfigItem();
|
GaoPackageConfigItem item = new GaoPackageConfigItem();
|
||||||
item.setPackageConfigId(configId);
|
item.setPackageConfigId(configId);
|
||||||
item.setProductId(source.getProductId());
|
item.setSkuId(source.getSkuId());
|
||||||
item.setProductUnitId(source.getProductUnitId());
|
item.setSkuUnitId(source.getSkuUnitId());
|
||||||
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
item.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||||
|
item.setProductIdSnapshot(source.getProductIdSnapshot());
|
||||||
|
item.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
||||||
|
item.setSkuNameSnapshot(source.getSkuNameSnapshot());
|
||||||
|
item.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
|
||||||
item.setUnitSnapshot(source.getUnitSnapshot());
|
item.setUnitSnapshot(source.getUnitSnapshot());
|
||||||
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||||
item.setDefaultQuantity(source.getDefaultQuantity());
|
item.setDefaultQuantity(source.getDefaultQuantity());
|
||||||
|
|||||||
+26
-86
@@ -5,14 +5,12 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
|||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
|
import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoProductMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoProductMapper;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoProductUnitMapper;
|
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection;
|
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
import com.imyeyu.api.modules.user.service.UserLoginService;
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
@@ -45,10 +43,10 @@ import java.util.stream.Collectors;
|
|||||||
public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct, String> implements GaoProductService {
|
public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct, String> implements GaoProductService {
|
||||||
|
|
||||||
private final GaoProductMapper mapper;
|
private final GaoProductMapper mapper;
|
||||||
private final GaoProductUnitMapper unitMapper;
|
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final ObjectProvider<GaoProductCategoryService> categoryServiceProvider;
|
private final ObjectProvider<GaoProductCategoryService> categoryServiceProvider;
|
||||||
private final GaoProductUnitService unitService;
|
private final ObjectProvider<GaoProductSkuService> skuServiceProvider;
|
||||||
|
private final ObjectProvider<GaoProductSpecService> specServiceProvider;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
private final LoggerService loggerService;
|
||||||
|
|
||||||
@@ -93,37 +91,9 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
List<GaoProduct> list = mapper.selectByQuery(page);
|
List<GaoProduct> list = mapper.selectByQuery(page);
|
||||||
fillTransient(list);
|
fillTransient(list);
|
||||||
for (GaoProduct product : list) {
|
for (GaoProduct product : list) {
|
||||||
product.setUnitList(product.getUnitList().stream().filter(unit -> Boolean.TRUE.equals(unit.getAllowSale())).toList());
|
product.setSkuList(product.getSkuList().stream().filter(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE && sku.getUnitList().stream().anyMatch(unit -> Boolean.TRUE.equals(unit.getAllowSale()))).toList());
|
||||||
}
|
}
|
||||||
return list.stream().filter(product -> !product.getUnitList().isEmpty()).toList();
|
return list.stream().filter(product -> !product.getSkuList().isEmpty()).toList();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GaoProductUnitSelection getSellableProductUnit(String storeId, String productId, String productUnitId) {
|
|
||||||
return getProductUnit(storeId, productId, productUnitId, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GaoProductUnitSelection getProductUnit(String storeId, String productId, String productUnitId, boolean purchase) {
|
|
||||||
TimiException.required(storeId, "not found storeId");
|
|
||||||
TimiException.required(productId, "not found productId");
|
|
||||||
TimiException.required(productUnitId, "not found productUnitId");
|
|
||||||
GaoProduct product = get(productId);
|
|
||||||
TimiException.required(product, "not found product");
|
|
||||||
TimiException.requiredTrue(storeId.equals(product.getStoreId()), "产品不属于当前门店");
|
|
||||||
TimiException.requiredTrue(product.getStatus() == GaoProduct.Status.ACTIVE, "产品已停用");
|
|
||||||
GaoProductUnit unit = unitMapper.selectForUpdate(productUnitId);
|
|
||||||
TimiException.required(unit, "not found product unit");
|
|
||||||
TimiException.requiredTrue(productId.equals(unit.getProductId()), "产品单位不属于当前产品");
|
|
||||||
TimiException.requiredTrue(!purchase || Boolean.TRUE.equals(unit.getAllowPurchase()), "产品单位不允许采购入库");
|
|
||||||
TimiException.requiredTrue(purchase || Boolean.TRUE.equals(unit.getAllowSale()), "产品单位不允许销售");
|
|
||||||
return new GaoProductUnitSelection(product, unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<GaoProductUnit> listUnitByProductId(String productId) {
|
|
||||||
TimiException.required(productId, "not found productId");
|
|
||||||
return unitMapper.selectByProductId(productId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@@ -132,7 +102,7 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CREATE", product, () -> {
|
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CREATE", product, () -> {
|
||||||
prepare(product, null);
|
prepare(product, null);
|
||||||
super.create(product);
|
super.create(product);
|
||||||
saveUnits(product.getId(), product.getUnitList(), List.of());
|
saveSkus(product, product.getSkuList());
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
|
||||||
return product;
|
return product;
|
||||||
});
|
});
|
||||||
@@ -151,17 +121,14 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
|
product.setName(TimiJava.defaultIfNull(request.getName(), product.getName()));
|
||||||
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
|
product.setProductType(TimiJava.defaultIfNull(request.getProductType(), product.getProductType()));
|
||||||
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
|
product.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), product.getStockEnabled()));
|
||||||
product.setAllowDecimalQuantity(TimiJava.defaultIfNull(request.getAllowDecimalQuantity(), product.getAllowDecimalQuantity()));
|
|
||||||
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
|
product.setStatus(TimiJava.defaultIfNull(request.getStatus(), product.getStatus()));
|
||||||
product.setMinStockQuantity(request.getMinStockQuantity());
|
|
||||||
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
|
product.setSort(TimiJava.defaultIfNull(request.getSort(), product.getSort()));
|
||||||
product.setRemark(request.getRemark());
|
product.setRemark(request.getRemark());
|
||||||
List<GaoProductUnit> existingUnitList = unitMapper.selectByProductId(product.getId());
|
product.setSkuList(request.getSkuList() == null ? skuServiceProvider.getObject().listByProductId(product.getId()) : request.getSkuList());
|
||||||
product.setUnitList(request.getUnitList() == null ? existingUnitList : request.getUnitList());
|
|
||||||
prepare(product, product.getId());
|
prepare(product, product.getId());
|
||||||
super.update(product);
|
super.update(product);
|
||||||
if (request.getUnitList() != null) {
|
if (request.getSkuList() != null) {
|
||||||
saveUnits(product.getId(), product.getUnitList(), existingUnitList);
|
saveSkus(product, request.getSkuList());
|
||||||
}
|
}
|
||||||
if (request.getAttachmentList() != null) {
|
if (request.getAttachmentList() != null) {
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), request.getAttachmentList());
|
||||||
@@ -176,6 +143,7 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_DISABLE", id, () -> {
|
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_DISABLE", id, () -> {
|
||||||
GaoProduct product = get(id);
|
GaoProduct product = get(id);
|
||||||
TimiException.required(product, "not found product");
|
TimiException.required(product, "not found product");
|
||||||
|
TimiException.requiredTrue(skuServiceProvider.getObject().listByProductId(id).stream().noneMatch(sku -> sku.getStatus() == com.imyeyu.api.modules.gao.entity.GaoProductSku.Status.ACTIVE), "请先停用产品的全部 SKU");
|
||||||
product.setStatus(GaoProduct.Status.INACTIVE);
|
product.setStatus(GaoProduct.Status.INACTIVE);
|
||||||
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
product.setUpdatedAt(Time.now());
|
product.setUpdatedAt(Time.now());
|
||||||
@@ -202,15 +170,7 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
product.setProductType(TimiJava.defaultIfNull(product.getProductType(), GaoProduct.ProductType.GOODS));
|
product.setProductType(TimiJava.defaultIfNull(product.getProductType(), GaoProduct.ProductType.GOODS));
|
||||||
product.setStockEnabled(TimiJava.defaultIfNull(product.getStockEnabled(), product.getProductType() != GaoProduct.ProductType.SERVICE));
|
product.setStockEnabled(TimiJava.defaultIfNull(product.getStockEnabled(), product.getProductType() != GaoProduct.ProductType.SERVICE));
|
||||||
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !product.getStockEnabled(), "SERVICE 产品不能启用库存");
|
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !product.getStockEnabled(), "SERVICE 产品不能启用库存");
|
||||||
product.setAllowDecimalQuantity(TimiJava.defaultIfNull(product.getAllowDecimalQuantity(), false));
|
|
||||||
product.setStatus(TimiJava.defaultIfNull(product.getStatus(), GaoProduct.Status.ACTIVE));
|
product.setStatus(TimiJava.defaultIfNull(product.getStatus(), GaoProduct.Status.ACTIVE));
|
||||||
if (!Boolean.TRUE.equals(product.getStockEnabled())) {
|
|
||||||
TimiException.requiredNull(product.getMinStockQuantity(), "未启用库存的产品不能填写最低库存");
|
|
||||||
}
|
|
||||||
if (product.getMinStockQuantity() != null) {
|
|
||||||
TimiException.requiredTrue(0 <= product.getMinStockQuantity().signum(), "最低库存不能为负数");
|
|
||||||
TimiException.requiredTrue(3 >= product.getMinStockQuantity().scale(), "最低库存最多保留三位小数");
|
|
||||||
}
|
|
||||||
product.setSort(TimiJava.defaultIfNull(product.getSort(), 0));
|
product.setSort(TimiJava.defaultIfNull(product.getSort(), 0));
|
||||||
TimiException.requiredNull(mapper.selectByStoreIdAndCode(product.getStoreId(), product.getCode(), excludeId), "门店内产品编码不能重复");
|
TimiException.requiredNull(mapper.selectByStoreIdAndCode(product.getStoreId(), product.getCode(), excludeId), "门店内产品编码不能重复");
|
||||||
if (product.getCategoryId() != null) {
|
if (product.getCategoryId() != null) {
|
||||||
@@ -218,42 +178,21 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
TimiException.required(category, "产品分类不存在");
|
TimiException.required(category, "产品分类不存在");
|
||||||
TimiException.requiredTrue(product.getStoreId().equals(category.getStoreId()), "产品分类不属于当前门店");
|
TimiException.requiredTrue(product.getStoreId().equals(category.getStoreId()), "产品分类不属于当前门店");
|
||||||
}
|
}
|
||||||
product.setUnitList(unitService.validateAndNormalize(product, product.getUnitList()));
|
|
||||||
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
product.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveUnits(String productId, List<GaoProductUnit> unitList, List<GaoProductUnit> existingUnitList) {
|
private void saveSkus(GaoProduct product, List<com.imyeyu.api.modules.gao.entity.GaoProductSku> skuList) {
|
||||||
Map<String, GaoProductUnit> existingById = existingUnitList.stream()
|
List<com.imyeyu.api.modules.gao.entity.GaoProductSku> sourceList = skuList;
|
||||||
.collect(Collectors.toMap(GaoProductUnit::getId, item -> item));
|
if (sourceList == null || sourceList.isEmpty()) {
|
||||||
Map<String, GaoProductUnit> existingByName = existingUnitList.stream()
|
com.imyeyu.api.modules.gao.entity.GaoProductSku sku = new com.imyeyu.api.modules.gao.entity.GaoProductSku();
|
||||||
.collect(Collectors.toMap(GaoProductUnit::getName, item -> item));
|
sku.setProductId(product.getId()); sku.setCode(product.getCode()); sku.setName(product.getName());
|
||||||
Set<String> retainedIdSet = new HashSet<>();
|
com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit unit = new com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit();
|
||||||
for (GaoProductUnit source : unitList) {
|
unit.setName("件"); unit.setIsBase(true); unit.setBaseQuantityPerUnit(BigDecimal.ONE);
|
||||||
GaoProductUnit existing = source.getId() == null ? existingByName.get(source.getName()) : existingById.get(source.getId());
|
sku.setUnitList(List.of(unit)); sourceList = List.of(sku);
|
||||||
if (source.getId() != null && existing == null && !existingUnitList.isEmpty()) {
|
|
||||||
throw new TimiException(TimiCode.ERROR, "产品单位不存在或不属于当前产品");
|
|
||||||
}
|
|
||||||
GaoProductUnit unit = new GaoProductUnit();
|
|
||||||
unit.setId(existing == null ? null : existing.getId());
|
|
||||||
unit.setProductId(productId);
|
|
||||||
unit.setName(source.getName());
|
|
||||||
unit.setBaseQuantityPerUnit(source.getBaseQuantityPerUnit());
|
|
||||||
unit.setSalePrice(source.getSalePrice());
|
|
||||||
unit.setIsBase(source.getIsBase());
|
|
||||||
unit.setAllowPurchase(source.getAllowPurchase());
|
|
||||||
unit.setAllowSale(source.getAllowSale());
|
|
||||||
unit.setSort(source.getSort());
|
|
||||||
if (existing == null) {
|
|
||||||
unitMapper.insert(unit);
|
|
||||||
} else {
|
|
||||||
retainedIdSet.add(existing.getId());
|
|
||||||
TimiException.requiredTrue(1 == unitMapper.updateUnit(unit), "产品单位更新失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (GaoProductUnit existing : existingUnitList) {
|
|
||||||
if (!retainedIdSet.contains(existing.getId())) {
|
|
||||||
unitMapper.delete(existing.getId());
|
|
||||||
}
|
}
|
||||||
|
for (com.imyeyu.api.modules.gao.entity.GaoProductSku source : sourceList) {
|
||||||
|
source.setProductId(product.getId());
|
||||||
|
if (source.getId() == null) skuServiceProvider.getObject().create(source); else skuServiceProvider.getObject().update(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,11 +201,12 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> productIdList = list.stream().map(GaoProduct::getId).toList();
|
List<String> productIdList = list.stream().map(GaoProduct::getId).toList();
|
||||||
Map<String, List<GaoProductUnit>> unitMap = unitMapper.selectByProductIdList(productIdList).stream()
|
|
||||||
.collect(Collectors.groupingBy(GaoProductUnit::getProductId));
|
|
||||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_PRODUCT, productIdList);
|
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_PRODUCT, productIdList);
|
||||||
|
Map<String, List<com.imyeyu.api.modules.gao.entity.GaoProductSku>> skuMap = skuServiceProvider.getObject().listByProductIdList(productIdList).stream()
|
||||||
|
.collect(Collectors.groupingBy(com.imyeyu.api.modules.gao.entity.GaoProductSku::getProductId));
|
||||||
for (GaoProduct product : list) {
|
for (GaoProduct product : list) {
|
||||||
product.setUnitList(unitMap.getOrDefault(product.getId(), List.of()));
|
product.setSkuList(skuMap.getOrDefault(product.getId(), List.of()));
|
||||||
|
product.setSpecList(specServiceProvider.getObject().listByProductId(product.getId()));
|
||||||
product.setAttachmentList(attachmentMap.getOrDefault(product.getId(), List.of()));
|
product.setAttachmentList(attachmentMap.getOrDefault(product.getId(), List.of()));
|
||||||
if (product.getCategoryId() != null) {
|
if (product.getCategoryId() != null) {
|
||||||
product.setCategory(categoryServiceProvider.getObject().get(product.getCategoryId()));
|
product.setCategory(categoryServiceProvider.getObject().get(product.getCategoryId()));
|
||||||
|
|||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSkuChartMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuChartService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductChartReq;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockHealthView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuStockMovementDailyStatView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSalesRankView;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/// GAO SKU 图表服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoProductSkuChartServiceImplement implements GaoProductSkuChartService {
|
||||||
|
|
||||||
|
private static final int DEFAULT_TOP_N = 20;
|
||||||
|
private static final int MAX_TOP_N = 100;
|
||||||
|
|
||||||
|
private final GaoProductSkuChartMapper mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuSalesDailyStatView> salesDaily(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return mapper.selectSalesDaily(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuSalesRankView> salesRank(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
normalizeTopN(req);
|
||||||
|
return mapper.selectSalesRank(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuUnitSalesRankView> unitSalesRank(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
normalizeTopN(req);
|
||||||
|
return mapper.selectUnitSalesRank(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuFulfillmentDailyStatView> fulfillmentDaily(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return mapper.selectFulfillmentDaily(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuFulfillmentRankView> fulfillmentRank(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
normalizeTopN(req);
|
||||||
|
return mapper.selectFulfillmentRank(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuStockMovementDailyStatView> stockMovementDaily(GaoProductChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
return mapper.selectStockMovementDaily(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuStockHealthView> stockHealth(GaoProductChartReq req) {
|
||||||
|
checkStoreReq(req);
|
||||||
|
normalizeTopN(req);
|
||||||
|
return mapper.selectStockHealth(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRangeReq(GaoProductChartReq req) {
|
||||||
|
checkStoreReq(req);
|
||||||
|
TimiException.required(req.getBeginAt(), "SKU 图表开始时间不能为空");
|
||||||
|
TimiException.required(req.getEndAt(), "SKU 图表结束时间不能为空");
|
||||||
|
TimiException.requiredTrue(req.getBeginAt() <= req.getEndAt(), "SKU 图表结束时间早于开始时间");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkStoreReq(GaoProductChartReq req) {
|
||||||
|
TimiException.required(req, "SKU 图表请求不能为空");
|
||||||
|
TimiException.required(req.getStoreId(), "SKU 图表门店不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeTopN(GaoProductChartReq req) {
|
||||||
|
int topN = req.getTopN() == null ? DEFAULT_TOP_N : req.getTopN();
|
||||||
|
TimiException.requiredTrue(0 < topN && topN < MAX_TOP_N + 1, "SKU 图表排行数量无效");
|
||||||
|
req.setTopN(topN);
|
||||||
|
}
|
||||||
|
}
|
||||||
+349
@@ -0,0 +1,349 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuSpecValue;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSkuMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSkuSpecValueMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSkuUnitMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSpecMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSpecValueMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
|
import com.imyeyu.api.modules.user.service.UserLoginService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/// GAO 产品 SKU 服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoProductSkuServiceImplement extends AbstractEntityService<GaoProductSku, String> implements GaoProductSkuService {
|
||||||
|
|
||||||
|
private static final BigDecimal ONE = BigDecimal.ONE;
|
||||||
|
|
||||||
|
private final GaoProductSkuMapper mapper;
|
||||||
|
private final GaoProductSkuUnitMapper unitMapper;
|
||||||
|
private final GaoProductSkuSpecValueMapper skuSpecValueMapper;
|
||||||
|
private final GaoProductSpecMapper specMapper;
|
||||||
|
private final GaoProductSpecValueMapper valueMapper;
|
||||||
|
private final AttachmentService attachmentService;
|
||||||
|
private final ObjectProvider<GaoProductService> productServiceProvider;
|
||||||
|
private final UserLoginService userLoginService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<GaoProductSku, String> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoProductSku detail(String id) {
|
||||||
|
GaoProductSku sku = get(id);
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
fillTransient(List.of(sku));
|
||||||
|
return sku;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSku> listByProductId(String productId) {
|
||||||
|
TimiException.required(productId, "not found productId");
|
||||||
|
List<GaoProductSku> result = mapper.selectByProductId(productId);
|
||||||
|
fillTransient(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSku> listByProductIdList(Collection<String> productIdList) {
|
||||||
|
if (productIdList == null || productIdList.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
List<GaoProductSku> result = mapper.selectByProductIdList(productIdList);
|
||||||
|
fillTransient(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoProductSkuUnitSelection getSellableSkuUnit(String storeId, String skuId, String skuUnitId) {
|
||||||
|
return getSkuUnit(storeId, skuId, skuUnitId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoProductSkuUnitSelection getSkuUnit(String storeId, String skuId, String skuUnitId, boolean purchase) {
|
||||||
|
TimiException.required(storeId, "not found storeId");
|
||||||
|
TimiException.required(skuId, "not found skuId");
|
||||||
|
TimiException.required(skuUnitId, "not found skuUnitId");
|
||||||
|
GaoProductSku sku = mapper.selectForUpdate(skuId);
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
TimiException.requiredTrue(storeId.equals(sku.getStoreId()), "SKU 不属于当前门店");
|
||||||
|
TimiException.requiredTrue(sku.getStatus() == GaoProductSku.Status.ACTIVE, "SKU 已停用");
|
||||||
|
GaoProduct product = productServiceProvider.getObject().get(sku.getProductId());
|
||||||
|
TimiException.required(product, "not found product");
|
||||||
|
TimiException.requiredTrue(product.getStatus() == GaoProduct.Status.ACTIVE, "产品已停用");
|
||||||
|
GaoProductSkuUnit unit = unitMapper.selectForUpdate(skuUnitId);
|
||||||
|
TimiException.required(unit, "not found product sku unit");
|
||||||
|
TimiException.requiredTrue(skuId.equals(unit.getSkuId()), "SKU 单位不属于当前 SKU");
|
||||||
|
TimiException.requiredTrue(!purchase || Boolean.TRUE.equals(unit.getAllowPurchase()), "SKU 单位不允许采购入库");
|
||||||
|
TimiException.requiredTrue(purchase || Boolean.TRUE.equals(unit.getAllowSale()), "SKU 单位不允许销售");
|
||||||
|
return new GaoProductSkuUnitSelection(product, sku, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSkuUnit> listUnitBySkuId(String skuId) {
|
||||||
|
TimiException.required(skuId, "not found skuId");
|
||||||
|
return unitMapper.selectBySkuId(skuId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void create(GaoProductSku sku) {
|
||||||
|
prepare(sku, null);
|
||||||
|
super.create(sku);
|
||||||
|
saveSpecValues(sku.getId(), sku.getSpecValueIdList());
|
||||||
|
saveUnits(sku.getId(), sku.getUnitList(), List.of());
|
||||||
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT_SKU, sku.getId(), sku.getAttachmentList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void update(GaoProductSku request) {
|
||||||
|
TimiException.required(request, "not found product sku");
|
||||||
|
TimiException.required(request.getId(), "not found product sku.id");
|
||||||
|
GaoProductSku sku = get(request.getId());
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
sku.setCode(TimiJava.defaultIfNull(request.getCode(), sku.getCode()));
|
||||||
|
sku.setBarcode(request.getBarcode());
|
||||||
|
sku.setName(TimiJava.defaultIfNull(request.getName(), sku.getName()));
|
||||||
|
sku.setSpecSummary(TimiJava.defaultIfNull(request.getSpecSummary(), sku.getSpecSummary()));
|
||||||
|
sku.setStockEnabled(TimiJava.defaultIfNull(request.getStockEnabled(), sku.getStockEnabled()));
|
||||||
|
sku.setAllowDecimalQuantity(TimiJava.defaultIfNull(request.getAllowDecimalQuantity(), sku.getAllowDecimalQuantity()));
|
||||||
|
sku.setMinStockQuantity(request.getMinStockQuantity());
|
||||||
|
sku.setStatus(TimiJava.defaultIfNull(request.getStatus(), sku.getStatus()));
|
||||||
|
sku.setSort(TimiJava.defaultIfNull(request.getSort(), sku.getSort()));
|
||||||
|
sku.setRemark(request.getRemark());
|
||||||
|
sku.setSpecValueIdList(request.getSpecValueIdList() == null ? currentSpecValueIds(sku.getId()) : request.getSpecValueIdList());
|
||||||
|
sku.setUnitList(request.getUnitList() == null ? unitMapper.selectBySkuId(sku.getId()) : request.getUnitList());
|
||||||
|
prepare(sku, sku.getId());
|
||||||
|
super.update(sku);
|
||||||
|
if (request.getSpecValueIdList() != null) {
|
||||||
|
saveSpecValues(sku.getId(), sku.getSpecValueIdList());
|
||||||
|
}
|
||||||
|
if (request.getUnitList() != null) {
|
||||||
|
saveUnits(sku.getId(), sku.getUnitList(), unitMapper.selectBySkuId(sku.getId()));
|
||||||
|
}
|
||||||
|
if (request.getAttachmentList() != null) {
|
||||||
|
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT_SKU, sku.getId(), request.getAttachmentList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void disable(String id) {
|
||||||
|
GaoProductSku sku = get(id);
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
sku.setStatus(GaoProductSku.Status.INACTIVE);
|
||||||
|
sku.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
|
sku.setUpdatedAt(Time.now());
|
||||||
|
super.update(sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(String id) {
|
||||||
|
disable(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy(String id) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "已建立 SKU 的产品不允许物理删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepare(GaoProductSku sku, String excludeId) {
|
||||||
|
TimiException.required(sku, "not found product sku");
|
||||||
|
GaoProduct product = productServiceProvider.getObject().get(sku.getProductId());
|
||||||
|
TimiException.required(product, "产品不存在");
|
||||||
|
sku.setStoreId(product.getStoreId());
|
||||||
|
sku.setCode(requiredText(sku.getCode(), "SKU 编码不能为空"));
|
||||||
|
sku.setName(requiredText(sku.getName(), "SKU 名称不能为空"));
|
||||||
|
sku.setBarcode(trimToNull(sku.getBarcode()));
|
||||||
|
sku.setSpecSummary(trimToNull(sku.getSpecSummary()));
|
||||||
|
sku.setStockEnabled(TimiJava.defaultIfNull(sku.getStockEnabled(), product.getStockEnabled()));
|
||||||
|
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !sku.getStockEnabled(), "SERVICE 产品的 SKU 不能启用库存");
|
||||||
|
sku.setAllowDecimalQuantity(TimiJava.defaultIfNull(sku.getAllowDecimalQuantity(), false));
|
||||||
|
sku.setStatus(TimiJava.defaultIfNull(sku.getStatus(), GaoProductSku.Status.ACTIVE));
|
||||||
|
if (!Boolean.TRUE.equals(sku.getStockEnabled())) {
|
||||||
|
TimiException.requiredNull(sku.getMinStockQuantity(), "未启用库存的 SKU 不能填写最低库存");
|
||||||
|
}
|
||||||
|
if (sku.getMinStockQuantity() != null) {
|
||||||
|
TimiException.requiredTrue(0 <= sku.getMinStockQuantity().signum(), "最低库存不能为负数");
|
||||||
|
TimiException.requiredTrue(3 >= sku.getMinStockQuantity().scale(), "最低库存最多保留三位小数");
|
||||||
|
}
|
||||||
|
sku.setSort(TimiJava.defaultIfNull(sku.getSort(), 0));
|
||||||
|
TimiException.requiredNull(mapper.selectByStoreIdAndCode(sku.getStoreId(), sku.getCode(), excludeId), "门店内 SKU 编码不能重复");
|
||||||
|
if (sku.getBarcode() != null) {
|
||||||
|
TimiException.requiredNull(mapper.selectByStoreIdAndBarcode(sku.getStoreId(), sku.getBarcode(), excludeId), "门店内 SKU 条码不能重复");
|
||||||
|
}
|
||||||
|
validateSpecValues(sku);
|
||||||
|
sku.setSpecValueKey(buildSpecValueKey(sku.getSpecValueIdList()));
|
||||||
|
TimiException.requiredNull(mapper.selectByProductIdAndSpecValueKey(sku.getProductId(), sku.getSpecValueKey(), excludeId), "同一产品的 SKU 规格组合不能重复");
|
||||||
|
sku.setUnitList(validateAndNormalizeUnits(sku.getUnitList()));
|
||||||
|
sku.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateSpecValues(GaoProductSku sku) {
|
||||||
|
List<String> specValueIdList = sku.getSpecValueIdList() == null ? List.of() : sku.getSpecValueIdList();
|
||||||
|
TimiException.requiredTrue(specValueIdList.size() == new HashSet<>(specValueIdList).size(), "SKU 规格值不能重复");
|
||||||
|
Set<String> specIdSet = new HashSet<>();
|
||||||
|
for (String specValueId : specValueIdList) {
|
||||||
|
GaoProductSpecValue value = valueMapper.selectById(specValueId);
|
||||||
|
TimiException.required(value, "SKU 规格值不存在");
|
||||||
|
TimiException.requiredTrue(Boolean.TRUE.equals(value.getEnabled()), "SKU 规格值已停用");
|
||||||
|
TimiException.requiredTrue(sku.getProductId().equals(specMapper.selectProductIdBySpecValueId(specValueId)), "SKU 规格值不属于当前产品");
|
||||||
|
TimiException.requiredTrue(specIdSet.add(value.getSpecId()), "同一规格项只能选择一个规格值");
|
||||||
|
}
|
||||||
|
Set<String> requiredSpecIdSet = specMapper.selectByProductId(sku.getProductId()).stream().map(GaoProductSpec::getId).collect(Collectors.toSet());
|
||||||
|
TimiException.requiredTrue(requiredSpecIdSet.equals(specIdSet), "SKU 必须为每个规格项选择一个规格值");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildSpecValueKey(List<String> specValueIdList) {
|
||||||
|
if (specValueIdList == null || specValueIdList.isEmpty()) {
|
||||||
|
return "DEFAULT";
|
||||||
|
}
|
||||||
|
return specValueIdList.stream().sorted().collect(Collectors.joining(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GaoProductSkuUnit> validateAndNormalizeUnits(List<GaoProductSkuUnit> sourceList) {
|
||||||
|
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "SKU 至少需要一个单位");
|
||||||
|
List<GaoProductSkuUnit> result = new ArrayList<>();
|
||||||
|
Set<String> nameSet = new HashSet<>();
|
||||||
|
Set<String> idSet = new HashSet<>();
|
||||||
|
int baseCount = 0;
|
||||||
|
for (int i = 0; i < sourceList.size(); i++) {
|
||||||
|
GaoProductSkuUnit source = sourceList.get(i);
|
||||||
|
TimiException.required(source, "SKU 单位不能为空");
|
||||||
|
GaoProductSkuUnit unit = new GaoProductSkuUnit();
|
||||||
|
unit.setId(source.getId());
|
||||||
|
if (unit.getId() != null) {
|
||||||
|
TimiException.requiredTrue(idSet.add(unit.getId()), "SKU 单位 ID 不能重复");
|
||||||
|
}
|
||||||
|
unit.setName(requiredText(source.getName(), "SKU 单位名称不能为空"));
|
||||||
|
TimiException.requiredTrue(nameSet.add(unit.getName()), "同一 SKU 的单位名称不能重复");
|
||||||
|
unit.setBaseQuantityPerUnit(TimiJava.defaultIfNull(source.getBaseQuantityPerUnit(), ONE));
|
||||||
|
TimiException.requiredTrue(0 < unit.getBaseQuantityPerUnit().signum(), "基础单位数量系数必须大于 0");
|
||||||
|
TimiException.requiredTrue(6 >= unit.getBaseQuantityPerUnit().scale(), "基础单位数量系数最多保留六位小数");
|
||||||
|
if (source.getSalePrice() != null) {
|
||||||
|
TimiException.requiredTrue(0 <= source.getSalePrice(), "单位建议售价不能为负数");
|
||||||
|
}
|
||||||
|
unit.setSalePrice(source.getSalePrice());
|
||||||
|
unit.setIsBase(Boolean.TRUE.equals(source.getIsBase()));
|
||||||
|
unit.setAllowPurchase(TimiJava.defaultIfNull(source.getAllowPurchase(), true));
|
||||||
|
unit.setAllowSale(TimiJava.defaultIfNull(source.getAllowSale(), true));
|
||||||
|
unit.setSort(TimiJava.defaultIfNull(source.getSort(), i));
|
||||||
|
if (unit.getIsBase()) {
|
||||||
|
baseCount++;
|
||||||
|
TimiException.requiredTrue(1 >= baseCount, "一个 SKU 只能有一个基础单位");
|
||||||
|
TimiException.requiredTrue(ONE.compareTo(unit.getBaseQuantityPerUnit()) == 0, "基础单位数量系数必须为 1");
|
||||||
|
}
|
||||||
|
result.add(unit);
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(0 < baseCount, "SKU 必须有一个基础单位");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveUnits(String skuId, List<GaoProductSkuUnit> unitList, List<GaoProductSkuUnit> existingUnitList) {
|
||||||
|
Map<String, GaoProductSkuUnit> existingById = existingUnitList.stream().collect(Collectors.toMap(GaoProductSkuUnit::getId, item -> item));
|
||||||
|
Set<String> retainedIdSet = new HashSet<>();
|
||||||
|
for (GaoProductSkuUnit source : unitList) {
|
||||||
|
GaoProductSkuUnit existing = source.getId() == null ? null : existingById.get(source.getId());
|
||||||
|
if (source.getId() != null && existing == null) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "SKU 单位不存在或不属于当前 SKU");
|
||||||
|
}
|
||||||
|
GaoProductSkuUnit unit = new GaoProductSkuUnit();
|
||||||
|
unit.setId(existing == null ? null : existing.getId());
|
||||||
|
unit.setSkuId(skuId);
|
||||||
|
unit.setName(source.getName());
|
||||||
|
unit.setBaseQuantityPerUnit(source.getBaseQuantityPerUnit());
|
||||||
|
unit.setSalePrice(source.getSalePrice());
|
||||||
|
unit.setIsBase(source.getIsBase());
|
||||||
|
unit.setAllowPurchase(source.getAllowPurchase());
|
||||||
|
unit.setAllowSale(source.getAllowSale());
|
||||||
|
unit.setSort(source.getSort());
|
||||||
|
if (existing == null) {
|
||||||
|
unitMapper.insert(unit);
|
||||||
|
} else {
|
||||||
|
retainedIdSet.add(existing.getId());
|
||||||
|
TimiException.requiredTrue(1 == unitMapper.updateUnit(unit), "SKU 单位更新失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (GaoProductSkuUnit existing : existingUnitList) {
|
||||||
|
if (!retainedIdSet.contains(existing.getId())) {
|
||||||
|
unitMapper.delete(existing.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveSpecValues(String skuId, List<String> specValueIdList) {
|
||||||
|
skuSpecValueMapper.deleteBySkuId(skuId);
|
||||||
|
for (String specValueId : specValueIdList == null ? List.<String>of() : specValueIdList) {
|
||||||
|
GaoProductSkuSpecValue relation = new GaoProductSkuSpecValue();
|
||||||
|
relation.setSkuId(skuId);
|
||||||
|
relation.setSpecValueId(specValueId);
|
||||||
|
skuSpecValueMapper.insert(relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> currentSpecValueIds(String skuId) {
|
||||||
|
return skuSpecValueMapper.selectBySkuIdList(List.of(skuId)).stream().map(GaoProductSkuSpecValue::getSpecValueId).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillTransient(List<GaoProductSku> skuList) {
|
||||||
|
if (skuList.isEmpty()) return;
|
||||||
|
List<String> skuIdList = skuList.stream().map(GaoProductSku::getId).toList();
|
||||||
|
Map<String, List<GaoProductSkuUnit>> unitMap = unitMapper.selectBySkuIdList(skuIdList).stream().collect(Collectors.groupingBy(GaoProductSkuUnit::getSkuId));
|
||||||
|
Map<String, List<String>> specValueIdMap = skuSpecValueMapper.selectBySkuIdList(skuIdList)
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.groupingBy(GaoProductSkuSpecValue::getSkuId, Collectors.mapping(GaoProductSkuSpecValue::getSpecValueId, Collectors.toList())));
|
||||||
|
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_PRODUCT_SKU, skuIdList);
|
||||||
|
for (GaoProductSku sku : skuList) {
|
||||||
|
sku.setUnitList(unitMap.getOrDefault(sku.getId(), List.of()));
|
||||||
|
sku.setSpecValueIdList(specValueIdMap.getOrDefault(sku.getId(), List.of()));
|
||||||
|
sku.setAttachmentList(attachmentMap.getOrDefault(sku.getId(), List.of()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String requiredText(String value, String message) {
|
||||||
|
TimiException.required(value, message);
|
||||||
|
String result = value.trim();
|
||||||
|
TimiException.requiredTrue(!result.isEmpty(), message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
return value == null || value.trim().isEmpty() ? null : value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSpecMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.mapper.GaoProductSpecValueMapper;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
|
import com.imyeyu.api.modules.gao.service.GaoProductSpecService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/// GAO 产品规格服务实现
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GaoProductSpecServiceImplement implements GaoProductSpecService {
|
||||||
|
|
||||||
|
private final GaoProductSpecMapper specMapper;
|
||||||
|
private final GaoProductSpecValueMapper valueMapper;
|
||||||
|
private final GaoProductService productService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoProductSpec> listByProductId(String productId) {
|
||||||
|
List<GaoProductSpec> specList = specMapper.selectByProductId(productId);
|
||||||
|
Map<String, List<GaoProductSpecValue>> valueMap = valueMapper.selectBySpecIdList(specList.stream().map(GaoProductSpec::getId).toList()).stream().collect(Collectors.groupingBy(GaoProductSpecValue::getSpecId));
|
||||||
|
for (GaoProductSpec spec : specList) spec.setValueList(valueMap.getOrDefault(spec.getId(), List.of()));
|
||||||
|
return specList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductIdBySpecId(String specId) {
|
||||||
|
return specMapper.selectProductIdBySpecId(specId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createSpec(GaoProductSpec spec) {
|
||||||
|
TimiException.required(spec, "not found product spec");
|
||||||
|
GaoProduct product = productService.get(spec.getProductId());
|
||||||
|
TimiException.required(product, "产品不存在");
|
||||||
|
spec.setName(requiredText(spec.getName(), "规格名称不能为空"));
|
||||||
|
spec.setSort(TimiJava.defaultIfNull(spec.getSort(), 0));
|
||||||
|
specMapper.insert(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createValue(GaoProductSpecValue value) {
|
||||||
|
TimiException.required(value, "not found product spec value");
|
||||||
|
TimiException.required(specMapper.selectProductIdBySpecId(value.getSpecId()), "规格项不存在");
|
||||||
|
value.setName(requiredText(value.getName(), "规格值名称不能为空"));
|
||||||
|
value.setEnabled(TimiJava.defaultIfNull(value.getEnabled(), true));
|
||||||
|
value.setSort(TimiJava.defaultIfNull(value.getSort(), 0));
|
||||||
|
valueMapper.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String requiredText(String value, String message) { TimiException.required(value, message); String result = value.trim(); TimiException.requiredTrue(!result.isEmpty(), message); return result; }
|
||||||
|
}
|
||||||
+3
-3
@@ -86,13 +86,13 @@ public class GaoProductStockRecordServiceImplement extends AbstractEntityService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStockRecord getByBizProduct(GaoProductStockRecord.BizType bizType, String bizId, String productId,
|
public GaoProductStockRecord getByBizSku(GaoProductStockRecord.BizType bizType, String bizId, String skuId,
|
||||||
GaoProductStockRecord.OperationType operationType) {
|
GaoProductStockRecord.OperationType operationType) {
|
||||||
TimiException.required(bizType, "not found stock bizType");
|
TimiException.required(bizType, "not found stock bizType");
|
||||||
TimiException.required(bizId, "not found stock bizId");
|
TimiException.required(bizId, "not found stock bizId");
|
||||||
TimiException.required(productId, "not found productId");
|
TimiException.required(skuId, "not found skuId");
|
||||||
TimiException.required(operationType, "not found operationType");
|
TimiException.required(operationType, "not found operationType");
|
||||||
return mapper.selectByBizProduct(bizType, bizId, productId, operationType);
|
return mapper.selectByBizSku(bizType, bizId, skuId, operationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+73
-54
@@ -5,18 +5,20 @@ import com.imyeyu.api.modules.common.entity.Attachment;
|
|||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
import com.imyeyu.api.modules.gao.entity.GaoProductStock;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoProductStockRecord;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
import com.imyeyu.api.modules.gao.service.GaoProductStockService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoProductStockRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockChangeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockManualRevokeRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockPage;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection;
|
import com.imyeyu.api.modules.gao.vo.GaoProductStockProductView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
import com.imyeyu.api.modules.gao.vo.GaoStockChangeItem;
|
||||||
import com.imyeyu.api.modules.system.entity.Logger;
|
import com.imyeyu.api.modules.system.entity.Logger;
|
||||||
import com.imyeyu.api.modules.system.service.LoggerService;
|
import com.imyeyu.api.modules.system.service.LoggerService;
|
||||||
@@ -54,7 +56,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final GaoProductStockRecordService recordService;
|
private final GaoProductStockRecordService recordService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductService productService;
|
||||||
private final GaoProductUnitService unitService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
private final LoggerService loggerService;
|
private final LoggerService loggerService;
|
||||||
@@ -66,15 +68,14 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
result.setTotal(stockMapper.countByQuery(page));
|
result.setTotal(stockMapper.countByQuery(page));
|
||||||
List<GaoProductStock> list = stockMapper.selectByQuery(page);
|
List<GaoProductStock> list = stockMapper.selectByQuery(page);
|
||||||
Map<String, Attachment> photoMap = attachmentService.mapFirstImageByIdList(
|
Map<String, Attachment> photoMap = attachmentService.mapFirstImageByIdList(
|
||||||
Attachment.BizType.GAO_PRODUCT,
|
Attachment.BizType.GAO_PRODUCT_SKU,
|
||||||
list.stream().map(GaoProductStock::getProductId).filter(id -> id != null && !id.isBlank()).toList(),
|
list.stream().map(GaoProductStock::getSkuId).filter(id -> id != null && !id.isBlank()).toList()
|
||||||
GaoProduct.AttachType.PHOTO.name()
|
|
||||||
);
|
);
|
||||||
for (GaoProductStock stock : list) {
|
for (GaoProductStock stock : list) {
|
||||||
fillBalance(stock);
|
fillBalance(stock);
|
||||||
if (stock.getProduct() != null) {
|
if (stock.getSku() != null) {
|
||||||
Attachment photo = photoMap.get(stock.getProductId());
|
Attachment photo = photoMap.get(stock.getSkuId());
|
||||||
stock.getProduct().setAttachmentList(photo == null ? List.of() : List.of(photo));
|
stock.getSku().setAttachmentList(photo == null ? List.of() : List.of(photo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.setList(list);
|
result.setList(list);
|
||||||
@@ -82,22 +83,31 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GaoProductStock detail(String storeId, String productId) {
|
public PageResult<GaoProductStockProductView> pageProductByQuery(GaoProductStockPage page) {
|
||||||
|
TimiException.required(page, "not found stock product page");
|
||||||
|
PageResult<GaoProductStockProductView> result = new PageResult<>();
|
||||||
|
result.setTotal(stockMapper.countProductByQuery(page));
|
||||||
|
result.setList(stockMapper.selectProductByQuery(page));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GaoProductStock detail(String storeId, String skuId) {
|
||||||
TimiException.required(storeId, "not found storeId");
|
TimiException.required(storeId, "not found storeId");
|
||||||
TimiException.required(productId, "not found productId");
|
TimiException.required(skuId, "not found skuId");
|
||||||
storeScopeService.checkStoreId(storeId);
|
storeScopeService.checkStoreId(storeId);
|
||||||
GaoProduct product = productService.get(productId);
|
GaoProductSku sku = skuService.get(skuId);
|
||||||
TimiException.required(product, "not found product");
|
TimiException.required(sku, "not found sku");
|
||||||
TimiException.requiredTrue(storeId.equals(product.getStoreId()), "产品不属于当前门店");
|
TimiException.requiredTrue(storeId.equals(sku.getStoreId()), "SKU 不属于当前门店");
|
||||||
GaoProductStock stock = stockMapper.selectByStoreIdAndProductId(storeId, productId);
|
GaoProductStock stock = stockMapper.selectByStoreIdAndSkuId(storeId, skuId);
|
||||||
if (stock == null) {
|
if (stock == null) {
|
||||||
stock = new GaoProductStock();
|
stock = new GaoProductStock();
|
||||||
stock.setStoreId(storeId);
|
stock.setStoreId(storeId);
|
||||||
stock.setProductId(productId);
|
stock.setSkuId(skuId);
|
||||||
stock.setOnHandQuantity(ZERO);
|
stock.setOnHandQuantity(ZERO);
|
||||||
stock.setReservedQuantity(ZERO);
|
stock.setReservedQuantity(ZERO);
|
||||||
}
|
}
|
||||||
stock.setProduct(product);
|
stock.setSku(sku);
|
||||||
fillBalance(stock);
|
fillBalance(stock);
|
||||||
return stock;
|
return stock;
|
||||||
}
|
}
|
||||||
@@ -146,9 +156,9 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
List<GaoStockChangeItem> sortedItems = new ArrayList<>(items);
|
List<GaoStockChangeItem> sortedItems = new ArrayList<>(items);
|
||||||
sortedItems.sort(Comparator.comparing(GaoStockChangeItem::getProductId));
|
sortedItems.sort(Comparator.comparing(GaoStockChangeItem::getSkuId));
|
||||||
for (GaoStockChangeItem item : sortedItems) {
|
for (GaoStockChangeItem item : sortedItems) {
|
||||||
GaoProductStock stock = stockMapper.selectForUpdate(storeId, item.getProductId());
|
GaoProductStock stock = stockMapper.selectForUpdate(storeId, item.getSkuId());
|
||||||
if (stock == null || decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity())).compareTo(item.getBaseQuantity()) < 0) {
|
if (stock == null || decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity())).compareTo(item.getBaseQuantity()) < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -188,22 +198,23 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
boolean purchase = operationType == GaoProductStockRecord.OperationType.INITIAL_IN
|
boolean purchase = operationType == GaoProductStockRecord.OperationType.INITIAL_IN
|
||||||
|| operationType == GaoProductStockRecord.OperationType.MANUAL_IN
|
|| operationType == GaoProductStockRecord.OperationType.MANUAL_IN
|
||||||
|| operationType == GaoProductStockRecord.OperationType.STOCKTAKE_GAIN;
|
|| operationType == GaoProductStockRecord.OperationType.STOCKTAKE_GAIN;
|
||||||
GaoProductUnitSelection selection = productService.getProductUnit(storeId, request.getProductId(), request.getProductUnitId(), purchase);
|
GaoProductSkuUnitSelection selection = skuService.getSkuUnit(storeId, request.getSkuId(), request.getSkuUnitId(), purchase);
|
||||||
|
GaoProductSku sku = selection.getSku();
|
||||||
GaoProduct product = selection.getProduct();
|
GaoProduct product = selection.getProduct();
|
||||||
GaoProductUnit unit = selection.getUnit();
|
GaoProductSkuUnit unit = selection.getUnit();
|
||||||
TimiException.requiredTrue(Boolean.TRUE.equals(product.getStockEnabled()), "该产品未启用库存管理");
|
TimiException.requiredTrue(Boolean.TRUE.equals(sku.getStockEnabled()), "该 SKU 未启用库存管理");
|
||||||
BigDecimal quantity = validateQuantity(product, request.getQuantity());
|
BigDecimal quantity = validateQuantity(sku, request.getQuantity());
|
||||||
BigDecimal baseQuantity = unitService.convertQuantityToBase(unit, quantity);
|
BigDecimal baseQuantity = quantity.multiply(unit.getBaseQuantityPerUnit());
|
||||||
List<GaoProductStockRecord> existingList = recordService.listByRequestNo(storeId, requestNo);
|
List<GaoProductStockRecord> existingList = recordService.listByRequestNo(storeId, requestNo);
|
||||||
if (!existingList.isEmpty()) {
|
if (!existingList.isEmpty()) {
|
||||||
GaoProductStockRecord existing = existingList.getFirst();
|
GaoProductStockRecord existing = existingList.getFirst();
|
||||||
verifyExisting(existing, operationType, product.getId(), unit.getId(), quantity, baseQuantity, request.getRemark());
|
verifyExisting(existing, operationType, sku.getId(), unit.getId(), quantity, baseQuantity, request.getRemark());
|
||||||
return detail(storeId, product.getId());
|
return detail(storeId, sku.getId());
|
||||||
}
|
}
|
||||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "库存操作必须上传照片");
|
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "库存操作必须上传照片");
|
||||||
TimiException.requiredTrue(hasText(request.getRemark()), "库存操作必须填写备注");
|
TimiException.requiredTrue(hasText(request.getRemark()), "库存操作必须填写备注");
|
||||||
GaoStockChangeItem item = GaoStockChangeItem.of(product, unit, quantity, baseQuantity);
|
GaoStockChangeItem item = GaoStockChangeItem.of(product, sku, unit, quantity, baseQuantity);
|
||||||
List<GaoProductStock> result = executeInternal(storeId, product.getId(), List.of(item), requestNo,
|
List<GaoProductStock> result = executeInternal(storeId, sku.getId(), List.of(item), requestNo,
|
||||||
userLoginService.getRequireLoginUserId(), operationType, request.getRemark(), request.getAttachmentList());
|
userLoginService.getRequireLoginUserId(), operationType, request.getRemark(), request.getAttachmentList());
|
||||||
return result.getFirst();
|
return result.getFirst();
|
||||||
}
|
}
|
||||||
@@ -220,13 +231,13 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
|| source.getOperationType() == GaoProductStockRecord.OperationType.MANUAL_OUT, "只有手工入库或手工出库流水可以冲正");
|
|| source.getOperationType() == GaoProductStockRecord.OperationType.MANUAL_OUT, "只有手工入库或手工出库流水可以冲正");
|
||||||
TimiException.requiredNull(source.getSourceRecordId(), "只能冲正原始手工库存流水");
|
TimiException.requiredNull(source.getSourceRecordId(), "只能冲正原始手工库存流水");
|
||||||
String storeId = requiredText(source.getStoreId(), "原库存流水缺少门店");
|
String storeId = requiredText(source.getStoreId(), "原库存流水缺少门店");
|
||||||
String productId = requiredText(source.getProductId(), "原库存流水缺少产品");
|
String skuId = requiredText(source.getSkuId(), "原库存流水缺少 SKU");
|
||||||
storeScopeService.checkStoreId(storeId);
|
storeScopeService.checkStoreId(storeId);
|
||||||
|
|
||||||
List<GaoProductStockRecord> existingList = recordService.listByRequestNo(storeId, requestNo);
|
List<GaoProductStockRecord> existingList = recordService.listByRequestNo(storeId, requestNo);
|
||||||
if (!existingList.isEmpty()) {
|
if (!existingList.isEmpty()) {
|
||||||
verifyManualRevokeExisting(existingList, sourceRecordId, remark);
|
verifyManualRevokeExisting(existingList, sourceRecordId, remark);
|
||||||
return detail(storeId, productId);
|
return detail(storeId, skuId);
|
||||||
}
|
}
|
||||||
TimiException.requiredNull(recordService.getRevokeBySourceRecordId(sourceRecordId), "该手工库存流水已经冲正");
|
TimiException.requiredNull(recordService.getRevokeBySourceRecordId(sourceRecordId), "该手工库存流水已经冲正");
|
||||||
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "库存流水冲正必须上传照片");
|
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "库存流水冲正必须上传照片");
|
||||||
@@ -236,7 +247,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
BigDecimal sourceReservedDelta = decimal(source.getReservedDelta());
|
BigDecimal sourceReservedDelta = decimal(source.getReservedDelta());
|
||||||
TimiException.requiredTrue(sourceReservedDelta.signum() == 0, "手工库存流水不能包含预占变动");
|
TimiException.requiredTrue(sourceReservedDelta.signum() == 0, "手工库存流水不能包含预占变动");
|
||||||
|
|
||||||
GaoProductStock stock = lockOrCreateStock(storeId, productId);
|
GaoProductStock stock = lockOrCreateStock(storeId, skuId);
|
||||||
BigDecimal onHandBefore = decimal(stock.getOnHandQuantity());
|
BigDecimal onHandBefore = decimal(stock.getOnHandQuantity());
|
||||||
BigDecimal reservedBefore = decimal(stock.getReservedQuantity());
|
BigDecimal reservedBefore = decimal(stock.getReservedQuantity());
|
||||||
BigDecimal onHandDelta = source.getOnHandDelta().negate();
|
BigDecimal onHandDelta = source.getOnHandDelta().negate();
|
||||||
@@ -247,9 +258,13 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
BigDecimal reservedAfter = reservedBefore.add(reservedDelta);
|
BigDecimal reservedAfter = reservedBefore.add(reservedDelta);
|
||||||
GaoProductStockRecord revoke = new GaoProductStockRecord();
|
GaoProductStockRecord revoke = new GaoProductStockRecord();
|
||||||
revoke.setStoreId(storeId);
|
revoke.setStoreId(storeId);
|
||||||
revoke.setProductId(productId);
|
revoke.setSkuId(skuId);
|
||||||
revoke.setProductUnitId(source.getProductUnitId());
|
revoke.setSkuUnitId(source.getSkuUnitId());
|
||||||
|
revoke.setProductIdSnapshot(source.getProductIdSnapshot());
|
||||||
revoke.setProductNameSnapshot(source.getProductNameSnapshot());
|
revoke.setProductNameSnapshot(source.getProductNameSnapshot());
|
||||||
|
revoke.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
|
||||||
|
revoke.setSkuNameSnapshot(source.getSkuNameSnapshot());
|
||||||
|
revoke.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
|
||||||
revoke.setUnitSnapshot(source.getUnitSnapshot());
|
revoke.setUnitSnapshot(source.getUnitSnapshot());
|
||||||
revoke.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
revoke.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
|
||||||
revoke.setQuantity(source.getQuantity());
|
revoke.setQuantity(source.getQuantity());
|
||||||
@@ -289,11 +304,11 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
String normalizedRequestNo = requiredText(requestNo, "not found requestNo");
|
String normalizedRequestNo = requiredText(requestNo, "not found requestNo");
|
||||||
storeScopeService.checkStoreId(storeId);
|
storeScopeService.checkStoreId(storeId);
|
||||||
List<GaoStockChangeItem> itemList = new ArrayList<>(sourceList);
|
List<GaoStockChangeItem> itemList = new ArrayList<>(sourceList);
|
||||||
itemList.sort(Comparator.comparing(GaoStockChangeItem::getProductId));
|
itemList.sort(Comparator.comparing(GaoStockChangeItem::getSkuId));
|
||||||
Set<String> productIdSet = new HashSet<>();
|
Set<String> skuIdSet = new HashSet<>();
|
||||||
for (GaoStockChangeItem item : itemList) {
|
for (GaoStockChangeItem item : itemList) {
|
||||||
TimiException.required(item, "库存变更项不能为空");
|
TimiException.required(item, "库存变更项不能为空");
|
||||||
TimiException.requiredTrue(productIdSet.add(item.getProductId()), "同一库存操作不能重复产品");
|
TimiException.requiredTrue(skuIdSet.add(item.getSkuId()), "同一库存操作不能重复 SKU");
|
||||||
TimiException.required(item.getBaseQuantity(), "基础单位数量不能为空");
|
TimiException.required(item.getBaseQuantity(), "基础单位数量不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getBaseQuantity().signum(), "基础单位数量必须大于 0");
|
TimiException.requiredTrue(0 < item.getBaseQuantity().signum(), "基础单位数量必须大于 0");
|
||||||
}
|
}
|
||||||
@@ -302,13 +317,13 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
verifyExistingList(existingList, itemList, operationType, bizId);
|
verifyExistingList(existingList, itemList, operationType, bizId);
|
||||||
List<GaoProductStock> result = new ArrayList<>();
|
List<GaoProductStock> result = new ArrayList<>();
|
||||||
for (GaoStockChangeItem item : itemList) {
|
for (GaoStockChangeItem item : itemList) {
|
||||||
result.add(detail(storeId, item.getProductId()));
|
result.add(detail(storeId, item.getSkuId()));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
List<GaoProductStock> result = new ArrayList<>();
|
List<GaoProductStock> result = new ArrayList<>();
|
||||||
for (GaoStockChangeItem item : itemList) {
|
for (GaoStockChangeItem item : itemList) {
|
||||||
GaoProductStock stock = lockOrCreateStock(storeId, item.getProductId());
|
GaoProductStock stock = lockOrCreateStock(storeId, item.getSkuId());
|
||||||
BigDecimal onHandBefore = decimal(stock.getOnHandQuantity());
|
BigDecimal onHandBefore = decimal(stock.getOnHandQuantity());
|
||||||
BigDecimal reservedBefore = decimal(stock.getReservedQuantity());
|
BigDecimal reservedBefore = decimal(stock.getReservedQuantity());
|
||||||
BigDecimal onHandDelta = onHandDelta(operationType, item.getBaseQuantity());
|
BigDecimal onHandDelta = onHandDelta(operationType, item.getBaseQuantity());
|
||||||
@@ -319,9 +334,13 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
BigDecimal reservedAfter = reservedBefore.add(reservedDelta);
|
BigDecimal reservedAfter = reservedBefore.add(reservedDelta);
|
||||||
GaoProductStockRecord record = new GaoProductStockRecord();
|
GaoProductStockRecord record = new GaoProductStockRecord();
|
||||||
record.setStoreId(storeId);
|
record.setStoreId(storeId);
|
||||||
record.setProductId(item.getProductId());
|
record.setSkuId(item.getSkuId());
|
||||||
record.setProductUnitId(item.getProductUnitId());
|
record.setSkuUnitId(item.getSkuUnitId());
|
||||||
|
record.setProductIdSnapshot(item.getProductIdSnapshot());
|
||||||
record.setProductNameSnapshot(item.getProductNameSnapshot());
|
record.setProductNameSnapshot(item.getProductNameSnapshot());
|
||||||
|
record.setSkuCodeSnapshot(item.getSkuCodeSnapshot());
|
||||||
|
record.setSkuNameSnapshot(item.getSkuNameSnapshot());
|
||||||
|
record.setSpecSummarySnapshot(item.getSpecSummarySnapshot());
|
||||||
record.setUnitSnapshot(item.getUnitSnapshot());
|
record.setUnitSnapshot(item.getUnitSnapshot());
|
||||||
record.setBaseQuantityPerUnitSnapshot(item.getBaseQuantityPerUnitSnapshot());
|
record.setBaseQuantityPerUnitSnapshot(item.getBaseQuantityPerUnitSnapshot());
|
||||||
record.setQuantity(item.getQuantity());
|
record.setQuantity(item.getQuantity());
|
||||||
@@ -350,21 +369,21 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoProductStock lockOrCreateStock(String storeId, String productId) {
|
private GaoProductStock lockOrCreateStock(String storeId, String skuId) {
|
||||||
GaoProductStock stock = stockMapper.selectForUpdate(storeId, productId);
|
GaoProductStock stock = stockMapper.selectForUpdate(storeId, skuId);
|
||||||
if (stock != null) {
|
if (stock != null) {
|
||||||
return stock;
|
return stock;
|
||||||
}
|
}
|
||||||
stock = new GaoProductStock();
|
stock = new GaoProductStock();
|
||||||
stock.setId(UUID.randomUUID().toString());
|
stock.setId(UUID.randomUUID().toString());
|
||||||
stock.setStoreId(storeId);
|
stock.setStoreId(storeId);
|
||||||
stock.setProductId(productId);
|
stock.setSkuId(skuId);
|
||||||
stock.setOnHandQuantity(ZERO);
|
stock.setOnHandQuantity(ZERO);
|
||||||
stock.setReservedQuantity(ZERO);
|
stock.setReservedQuantity(ZERO);
|
||||||
stock.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
stock.setUpdatedBy(userLoginService.getRequireLoginUserId());
|
||||||
stock.setCreatedAt(Time.now());
|
stock.setCreatedAt(Time.now());
|
||||||
stockMapper.insertIgnore(stock);
|
stockMapper.insertIgnore(stock);
|
||||||
stock = stockMapper.selectForUpdate(storeId, productId);
|
stock = stockMapper.selectForUpdate(storeId, skuId);
|
||||||
TimiException.required(stock, "库存余额创建失败");
|
TimiException.required(stock, "库存余额创建失败");
|
||||||
return stock;
|
return stock;
|
||||||
}
|
}
|
||||||
@@ -386,21 +405,21 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal validateQuantity(GaoProduct product, BigDecimal quantity) {
|
private BigDecimal validateQuantity(GaoProductSku sku, BigDecimal quantity) {
|
||||||
TimiException.required(quantity, "数量不能为空");
|
TimiException.required(quantity, "数量不能为空");
|
||||||
TimiException.requiredTrue(0 < quantity.signum(), "数量必须大于 0");
|
TimiException.requiredTrue(0 < quantity.signum(), "数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "数量最多保留三位小数");
|
||||||
if (!Boolean.TRUE.equals(product.getAllowDecimalQuantity())) {
|
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
||||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
||||||
}
|
}
|
||||||
return quantity;
|
return quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyExisting(GaoProductStockRecord existing, GaoProductStockRecord.OperationType operationType, String productId, String unitId,
|
private void verifyExisting(GaoProductStockRecord existing, GaoProductStockRecord.OperationType operationType, String skuId, String unitId,
|
||||||
BigDecimal quantity, BigDecimal baseQuantity, String remark) {
|
BigDecimal quantity, BigDecimal baseQuantity, String remark) {
|
||||||
TimiException.requiredTrue(existing.getOperationType() == operationType
|
TimiException.requiredTrue(existing.getOperationType() == operationType
|
||||||
&& productId.equals(existing.getProductId())
|
&& skuId.equals(existing.getSkuId())
|
||||||
&& unitId.equals(existing.getProductUnitId())
|
&& unitId.equals(existing.getSkuUnitId())
|
||||||
&& quantity.compareTo(existing.getQuantity()) == 0
|
&& quantity.compareTo(existing.getQuantity()) == 0
|
||||||
&& baseQuantity.compareTo(existing.getBaseQuantity()) == 0
|
&& baseQuantity.compareTo(existing.getBaseQuantity()) == 0
|
||||||
&& sameText(existing.getRemark(), remark), "requestNo 对应库存操作内容不一致");
|
&& sameText(existing.getRemark(), remark), "requestNo 对应库存操作内容不一致");
|
||||||
@@ -418,11 +437,11 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
Map<String, GaoProductStockRecord> recordMap = new HashMap<>();
|
Map<String, GaoProductStockRecord> recordMap = new HashMap<>();
|
||||||
for (GaoProductStockRecord record : existingList) {
|
for (GaoProductStockRecord record : existingList) {
|
||||||
TimiException.requiredTrue(record.getOperationType() == operationType && bizId.equals(record.getBizId()), "requestNo 已被其他库存操作使用");
|
TimiException.requiredTrue(record.getOperationType() == operationType && bizId.equals(record.getBizId()), "requestNo 已被其他库存操作使用");
|
||||||
recordMap.put(record.getProductId(), record);
|
recordMap.put(record.getSkuId(), record);
|
||||||
}
|
}
|
||||||
TimiException.requiredTrue(recordMap.size() == itemList.size(), "requestNo 对应库存操作内容不一致");
|
TimiException.requiredTrue(recordMap.size() == itemList.size(), "requestNo 对应库存操作内容不一致");
|
||||||
for (GaoStockChangeItem item : itemList) {
|
for (GaoStockChangeItem item : itemList) {
|
||||||
GaoProductStockRecord record = recordMap.get(item.getProductId());
|
GaoProductStockRecord record = recordMap.get(item.getSkuId());
|
||||||
TimiException.required(record, "requestNo 对应库存操作内容不一致");
|
TimiException.required(record, "requestNo 对应库存操作内容不一致");
|
||||||
TimiException.requiredTrue(item.getBaseQuantity().compareTo(record.getBaseQuantity()) == 0, "requestNo 对应库存操作内容不一致");
|
TimiException.requiredTrue(item.getBaseQuantity().compareTo(record.getBaseQuantity()) == 0, "requestNo 对应库存操作内容不一致");
|
||||||
}
|
}
|
||||||
@@ -432,8 +451,8 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
stock.setOnHandQuantity(decimal(stock.getOnHandQuantity()));
|
stock.setOnHandQuantity(decimal(stock.getOnHandQuantity()));
|
||||||
stock.setReservedQuantity(decimal(stock.getReservedQuantity()));
|
stock.setReservedQuantity(decimal(stock.getReservedQuantity()));
|
||||||
stock.setAvailableQuantity(stock.getOnHandQuantity().subtract(stock.getReservedQuantity()));
|
stock.setAvailableQuantity(stock.getOnHandQuantity().subtract(stock.getReservedQuantity()));
|
||||||
if (stock.getProduct() == null && stock.getProductId() != null) {
|
if (stock.getSku() == null && stock.getSkuId() != null) {
|
||||||
stock.setProduct(productService.get(stock.getProductId()));
|
stock.setSku(skuService.get(stock.getSkuId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.gao.service.implement;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/// GAO 产品单位服务实现
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-09-02
|
|
||||||
@Service
|
|
||||||
public class GaoProductUnitServiceImplement implements GaoProductUnitService {
|
|
||||||
|
|
||||||
private static final BigDecimal ONE = BigDecimal.ONE;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<GaoProductUnit> validateAndNormalize(GaoProduct product, List<GaoProductUnit> sourceList) {
|
|
||||||
TimiException.required(product, "not found product");
|
|
||||||
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "产品至少需要一个单位");
|
|
||||||
List<GaoProductUnit> result = new ArrayList<>();
|
|
||||||
Set<String> nameSet = new HashSet<>();
|
|
||||||
Set<String> idSet = new HashSet<>();
|
|
||||||
int baseCount = 0;
|
|
||||||
for (int i = 0; i < sourceList.size(); i++) {
|
|
||||||
GaoProductUnit source = sourceList.get(i);
|
|
||||||
TimiException.required(source, "产品单位不能为空");
|
|
||||||
GaoProductUnit unit = new GaoProductUnit();
|
|
||||||
unit.setId(source.getId());
|
|
||||||
if (unit.getId() != null) {
|
|
||||||
TimiException.requiredTrue(idSet.add(unit.getId()), "产品单位 ID 不能重复");
|
|
||||||
}
|
|
||||||
unit.setName(requiredText(source.getName(), "产品单位名称不能为空"));
|
|
||||||
TimiException.requiredTrue(nameSet.add(unit.getName()), "同一产品的单位名称不能重复");
|
|
||||||
unit.setBaseQuantityPerUnit(TimiJava.defaultIfNull(source.getBaseQuantityPerUnit(), ONE));
|
|
||||||
TimiException.requiredTrue(0 < unit.getBaseQuantityPerUnit().signum(), "基础单位数量系数必须大于 0");
|
|
||||||
TimiException.requiredTrue(6 >= unit.getBaseQuantityPerUnit().scale(), "基础单位数量系数最多保留六位小数");
|
|
||||||
unit.setSalePrice(nonNegative(source.getSalePrice(), "单位建议售价不能为负数"));
|
|
||||||
unit.setIsBase(Boolean.TRUE.equals(source.getIsBase()));
|
|
||||||
unit.setAllowPurchase(TimiJava.defaultIfNull(source.getAllowPurchase(), true));
|
|
||||||
unit.setAllowSale(TimiJava.defaultIfNull(source.getAllowSale(), true));
|
|
||||||
unit.setSort(TimiJava.defaultIfNull(source.getSort(), i));
|
|
||||||
if (unit.getIsBase()) {
|
|
||||||
baseCount++;
|
|
||||||
TimiException.requiredTrue(1 >= baseCount, "一个产品只能有一个基础单位");
|
|
||||||
TimiException.requiredTrue(unit.getBaseQuantityPerUnit().compareTo(ONE) == 0, "基础单位数量系数必须为 1");
|
|
||||||
}
|
|
||||||
result.add(unit);
|
|
||||||
}
|
|
||||||
TimiException.requiredTrue(0 < baseCount, "产品必须有一个基础单位");
|
|
||||||
TimiException.requiredTrue(product.getProductType() != GaoProduct.ProductType.SERVICE || !Boolean.TRUE.equals(product.getStockEnabled()), "SERVICE 产品不能启用库存");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BigDecimal convertQuantityToBase(GaoProductUnit unit, BigDecimal quantity) {
|
|
||||||
TimiException.required(unit, "not found product unit");
|
|
||||||
TimiException.required(quantity, "数量不能为空");
|
|
||||||
TimiException.requiredTrue(0 < quantity.signum(), "数量必须大于 0");
|
|
||||||
BigDecimal result = quantity.multiply(unit.getBaseQuantityPerUnit());
|
|
||||||
try {
|
|
||||||
return result.setScale(3, RoundingMode.UNNECESSARY);
|
|
||||||
} catch (ArithmeticException e) {
|
|
||||||
throw new TimiException(com.imyeyu.java.bean.timi.TimiCode.ERROR, "业务数量换算后不能精确保存为三位小数", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String requiredText(String value, String message) {
|
|
||||||
TimiException.required(value, message);
|
|
||||||
String result = value.trim();
|
|
||||||
TimiException.requiredTrue(!result.isEmpty(), message);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long nonNegative(Long value, String message) {
|
|
||||||
if (value != null) {
|
|
||||||
TimiException.requiredTrue(0 <= value, message);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+9
@@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.mapper.GaoSalesOrderChartMapper;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderChartService;
|
import com.imyeyu.api.modules.gao.service.GaoSalesOrderChartService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderRankView;
|
||||||
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerOrderSkuRankView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatDailyStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
import com.imyeyu.api.modules.gao.vo.GaoCustomerRepeatIntervalStatView;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderChartReq;
|
||||||
@@ -66,6 +67,14 @@ public class GaoSalesOrderChartServiceImplement implements GaoSalesOrderChartSer
|
|||||||
return mapper.selectCustomerProductRank(req);
|
return mapper.selectCustomerProductRank(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GaoCustomerOrderSkuRankView> customerSkuRank(GaoSalesOrderChartReq req) {
|
||||||
|
checkRangeReq(req);
|
||||||
|
TimiException.required(req.getCustomerId(), "客户订单 SKU 排行必须指定客户");
|
||||||
|
normalizeTopN(req);
|
||||||
|
return mapper.selectCustomerSkuRank(req);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GaoSalesOrderStatusStatView> status(GaoSalesOrderChartReq req) {
|
public List<GaoSalesOrderStatusStatView> status(GaoSalesOrderChartReq req) {
|
||||||
checkRangeReq(req);
|
checkRangeReq(req);
|
||||||
|
|||||||
+40
-36
@@ -10,7 +10,8 @@ import com.imyeyu.api.modules.gao.entity.GaoCustomer;
|
|||||||
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoEventRecord;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
import com.imyeyu.api.modules.gao.entity.GaoFundRecord;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
import com.imyeyu.api.modules.gao.entity.GaoSalesOrder;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
import com.imyeyu.api.modules.gao.entity.GaoStore;
|
||||||
@@ -22,13 +23,13 @@ import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
|||||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
import com.imyeyu.api.modules.gao.service.GaoPointTriggerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductUnitService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreScopeService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
import com.imyeyu.api.modules.gao.service.GaoStoreService;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoDirectProductOrderCreateRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoDirectProductOrderCreateRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData;
|
import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection;
|
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest;
|
||||||
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage;
|
||||||
@@ -78,7 +79,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
private final ObjectProvider<GaoOrderFulfillmentService> fulfillmentServiceProvider;
|
private final ObjectProvider<GaoOrderFulfillmentService> fulfillmentServiceProvider;
|
||||||
private final GaoCustomerService customerService;
|
private final GaoCustomerService customerService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductService productService;
|
||||||
private final GaoProductUnitService unitService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoStoreService storeService;
|
private final GaoStoreService storeService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final GaoEventRecordService eventRecordService;
|
private final GaoEventRecordService eventRecordService;
|
||||||
@@ -347,20 +348,20 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
|
|
||||||
private List<GaoSalesOrderItem> resolveDirectItems(String storeId, List<GaoSalesOrderItemRequest> sourceList) {
|
private List<GaoSalesOrderItem> resolveDirectItems(String storeId, List<GaoSalesOrderItemRequest> sourceList) {
|
||||||
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "直接产品订单至少需要一个产品");
|
TimiException.requiredTrue(sourceList != null && !sourceList.isEmpty(), "直接产品订单至少需要一个产品");
|
||||||
Set<String> productUnitKeySet = new HashSet<>();
|
Set<String> skuUnitKeySet = new HashSet<>();
|
||||||
List<GaoSalesOrderItem> result = new ArrayList<>();
|
List<GaoSalesOrderItem> result = new ArrayList<>();
|
||||||
boolean canOverridePrice = permissionChecker.hasAny(ModuleCode.GAO, GaoPermissionCode.SALES_ORDER_PRICE_OVERRIDE.getValue());
|
boolean canOverridePrice = permissionChecker.hasAny(ModuleCode.GAO, GaoPermissionCode.SALES_ORDER_PRICE_OVERRIDE.getValue());
|
||||||
for (int i = 0; i < sourceList.size(); i++) {
|
for (int i = 0; i < sourceList.size(); i++) {
|
||||||
GaoSalesOrderItemRequest source = sourceList.get(i);
|
GaoSalesOrderItemRequest source = sourceList.get(i);
|
||||||
TimiException.required(source, "订单产品明细不能为空");
|
TimiException.required(source, "订单产品明细不能为空");
|
||||||
TimiException.required(source.getProductId(), "产品不能为空");
|
TimiException.required(source.getSkuId(), "SKU 不能为空");
|
||||||
TimiException.required(source.getProductUnitId(), "产品单位不能为空");
|
TimiException.required(source.getSkuUnitId(), "SKU 单位不能为空");
|
||||||
TimiException.requiredTrue(productUnitKeySet.add(source.getProductId() + "|" + source.getProductUnitId()), "同一订单不能重复产品和单位");
|
TimiException.requiredTrue(skuUnitKeySet.add(source.getSkuId() + "|" + source.getSkuUnitId()), "同一订单不能重复 SKU 和单位");
|
||||||
GaoProductUnitSelection selection = productService.getSellableProductUnit(storeId, source.getProductId(), source.getProductUnitId());
|
GaoProductSkuUnitSelection selection = skuService.getSellableSkuUnit(storeId, source.getSkuId(), source.getSkuUnitId());
|
||||||
GaoProduct product = selection.getProduct();
|
GaoProduct product = selection.getProduct();
|
||||||
GaoProductUnit unit = selection.getUnit();
|
GaoProductSku sku = selection.getSku();
|
||||||
BigDecimal quantity = validateQuantity(product, source.getQuantity());
|
GaoProductSkuUnit unit = selection.getUnit();
|
||||||
unitService.convertQuantityToBase(unit, quantity);
|
BigDecimal quantity = validateQuantity(sku, source.getQuantity());
|
||||||
Long unitPrice = TimiJava.defaultIfNull(source.getUnitPrice(), unit.getSalePrice());
|
Long unitPrice = TimiJava.defaultIfNull(source.getUnitPrice(), unit.getSalePrice());
|
||||||
TimiException.required(unitPrice, "产品单价不能为空");
|
TimiException.required(unitPrice, "产品单价不能为空");
|
||||||
TimiException.requiredTrue(0 <= unitPrice, "产品单价不能为负数");
|
TimiException.requiredTrue(0 <= unitPrice, "产品单价不能为负数");
|
||||||
@@ -370,10 +371,14 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
}
|
}
|
||||||
long lineAmount = toCentAmount(quantity, unitPrice);
|
long lineAmount = toCentAmount(quantity, unitPrice);
|
||||||
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
GaoSalesOrderItem item = new GaoSalesOrderItem();
|
||||||
item.setProductId(product.getId());
|
item.setSkuId(sku.getId());
|
||||||
item.setProductUnitId(unit.getId());
|
item.setSkuUnitId(unit.getId());
|
||||||
item.setItemType(GaoSalesOrderItem.ItemType.DIRECT_PRODUCT);
|
item.setItemType(GaoSalesOrderItem.ItemType.DIRECT_PRODUCT);
|
||||||
item.setProductNameSnapshot(product.getName());
|
item.setProductNameSnapshot(product.getName());
|
||||||
|
item.setProductIdSnapshot(product.getId());
|
||||||
|
item.setSkuCodeSnapshot(sku.getCode());
|
||||||
|
item.setSkuNameSnapshot(sku.getName());
|
||||||
|
item.setSpecSummarySnapshot(sku.getSpecSummary());
|
||||||
item.setUnitSnapshot(unit.getName());
|
item.setUnitSnapshot(unit.getName());
|
||||||
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
|
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
|
||||||
item.setQuantity(quantity);
|
item.setQuantity(quantity);
|
||||||
@@ -392,18 +397,18 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
private void verifyExistingDirectItems(GaoSalesOrder existing, List<GaoSalesOrderItemRequest> requestList) {
|
private void verifyExistingDirectItems(GaoSalesOrder existing, List<GaoSalesOrderItemRequest> requestList) {
|
||||||
List<GaoSalesOrderItem> existingList = itemMapper.selectBySalesOrderId(existing.getId());
|
List<GaoSalesOrderItem> existingList = itemMapper.selectBySalesOrderId(existing.getId());
|
||||||
TimiException.requiredTrue(requestList != null && existingList.size() == requestList.size(), "requestNo 对应订单内容不一致");
|
TimiException.requiredTrue(requestList != null && existingList.size() == requestList.size(), "requestNo 对应订单内容不一致");
|
||||||
Set<String> productUnitKeySet = new HashSet<>();
|
Set<String> skuUnitKeySet = new HashSet<>();
|
||||||
for (GaoSalesOrderItemRequest request : requestList) {
|
for (GaoSalesOrderItemRequest request : requestList) {
|
||||||
TimiException.required(request, "订单产品明细不能为空");
|
TimiException.required(request, "订单产品明细不能为空");
|
||||||
TimiException.required(request.getProductId(), "产品不能为空");
|
TimiException.required(request.getSkuId(), "SKU 不能为空");
|
||||||
TimiException.required(request.getProductUnitId(), "产品单位不能为空");
|
TimiException.required(request.getSkuUnitId(), "SKU 单位不能为空");
|
||||||
TimiException.required(request.getQuantity(), "购买数量不能为空");
|
TimiException.required(request.getQuantity(), "购买数量不能为空");
|
||||||
TimiException.requiredTrue(productUnitKeySet.add(request.getProductId() + "|" + request.getProductUnitId()), "同一订单不能重复产品和单位");
|
TimiException.requiredTrue(skuUnitKeySet.add(request.getSkuId() + "|" + request.getSkuUnitId()), "同一订单不能重复 SKU 和单位");
|
||||||
GaoSalesOrderItem item = existingList.stream()
|
GaoSalesOrderItem item = existingList.stream()
|
||||||
.filter(value -> value.getProductId().equals(request.getProductId()) && value.getProductUnitId().equals(request.getProductUnitId()))
|
.filter(value -> value.getSkuId().equals(request.getSkuId()) && value.getSkuUnitId().equals(request.getSkuUnitId()))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
TimiException.required(item, "requestNo 对应订单内容不一致");
|
TimiException.required(item, "requestNo 对应订单内容不一致");
|
||||||
TimiException.requiredTrue(item.getProductUnitId().equals(request.getProductUnitId())
|
TimiException.requiredTrue(item.getSkuUnitId().equals(request.getSkuUnitId())
|
||||||
&& item.getQuantity().compareTo(request.getQuantity()) == 0
|
&& item.getQuantity().compareTo(request.getQuantity()) == 0
|
||||||
&& (request.getUnitPrice() == null || item.getUnitPrice().equals(request.getUnitPrice())), "requestNo 对应订单内容不一致");
|
&& (request.getUnitPrice() == null || item.getUnitPrice().equals(request.getUnitPrice())), "requestNo 对应订单内容不一致");
|
||||||
}
|
}
|
||||||
@@ -434,15 +439,15 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
TimiException.requiredTrue(0 <= saleAmount, "成交价不能为负数");
|
TimiException.requiredTrue(0 <= saleAmount, "成交价不能为负数");
|
||||||
GaoSalesOrderItem.ItemType expectedItemType = orderType == GaoSalesOrder.OrderType.PACKAGE
|
GaoSalesOrderItem.ItemType expectedItemType = orderType == GaoSalesOrder.OrderType.PACKAGE
|
||||||
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
? GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT : GaoSalesOrderItem.ItemType.DIRECT_PRODUCT;
|
||||||
Set<String> productUnitKeySet = new HashSet<>();
|
Set<String> skuUnitKeySet = new HashSet<>();
|
||||||
long lineAmountSum = 0L;
|
long lineAmountSum = 0L;
|
||||||
for (GaoSalesOrderItem item : itemList) {
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
TimiException.required(item, "销售订单明细不能为空");
|
TimiException.required(item, "销售订单明细不能为空");
|
||||||
TimiException.requiredTrue(item.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
TimiException.requiredTrue(item.getItemType() == expectedItemType, "订单项类型与订单类型不匹配");
|
||||||
TimiException.required(item.getProductId(), "订单项产品不能为空");
|
TimiException.required(item.getSkuId(), "订单项 SKU 不能为空");
|
||||||
TimiException.required(item.getProductUnitId(), "订单项单位不能为空");
|
TimiException.required(item.getSkuUnitId(), "订单项 SKU 单位不能为空");
|
||||||
TimiException.requiredTrue(productUnitKeySet.add(item.getProductId() + "|" + item.getProductUnitId()),
|
TimiException.requiredTrue(skuUnitKeySet.add(item.getSkuId() + "|" + item.getSkuUnitId()),
|
||||||
"同一订单不能重复产品和单位");
|
"同一订单不能重复 SKU 和单位");
|
||||||
TimiException.required(item.getQuantity(), "订单项数量不能为空");
|
TimiException.required(item.getQuantity(), "订单项数量不能为空");
|
||||||
TimiException.requiredTrue(0 < item.getQuantity().signum(), "订单项数量必须大于 0");
|
TimiException.requiredTrue(0 < item.getQuantity().signum(), "订单项数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= item.getQuantity().stripTrailingZeros().scale(), "订单项数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= item.getQuantity().stripTrailingZeros().scale(), "订单项数量最多保留三位小数");
|
||||||
@@ -494,9 +499,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
List<GaoSalesOrderItem> existingItemList = itemMapper.selectBySalesOrderId(existing.getId());
|
List<GaoSalesOrderItem> existingItemList = itemMapper.selectBySalesOrderId(existing.getId());
|
||||||
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致");
|
TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致");
|
||||||
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream()
|
Map<String, GaoSalesOrderItem> existingItemMap = existingItemList.stream()
|
||||||
.collect(java.util.stream.Collectors.toMap(item -> item.getProductId() + "|" + item.getProductUnitId(), java.util.function.Function.identity()));
|
.collect(java.util.stream.Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), java.util.function.Function.identity()));
|
||||||
for (GaoSalesOrderItem requestItem : requestItemList) {
|
for (GaoSalesOrderItem requestItem : requestItemList) {
|
||||||
GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getProductId() + "|" + requestItem.getProductUnitId());
|
GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getSkuId() + "|" + requestItem.getSkuUnitId());
|
||||||
TimiException.required(existingItem, "requestNo 对应订单内容不一致");
|
TimiException.required(existingItem, "requestNo 对应订单内容不一致");
|
||||||
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT
|
||||||
&& existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0
|
&& existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0
|
||||||
@@ -550,20 +555,19 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
if (itemList == null || itemList.isEmpty()) {
|
if (itemList == null || itemList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> productIdList = itemList.stream()
|
List<String> skuIdList = itemList.stream()
|
||||||
.map(GaoSalesOrderItem::getProductId)
|
.map(GaoSalesOrderItem::getSkuId)
|
||||||
.filter(value -> value != null && !value.isBlank())
|
.filter(value -> value != null && !value.isBlank())
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
if (productIdList.isEmpty()) {
|
if (skuIdList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
Map<String, List<Attachment>> attachmentMap = attachmentService.mapByBizIdList(
|
||||||
Attachment.BizType.GAO_PRODUCT,
|
Attachment.BizType.GAO_PRODUCT_SKU,
|
||||||
productIdList,
|
skuIdList);
|
||||||
GaoProduct.AttachType.PHOTO.name());
|
|
||||||
for (GaoSalesOrderItem item : itemList) {
|
for (GaoSalesOrderItem item : itemList) {
|
||||||
item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of()));
|
item.setAttachmentList(attachmentMap.getOrDefault(item.getSkuId(), List.of()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,11 +595,11 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal validateQuantity(GaoProduct product, BigDecimal quantity) {
|
private BigDecimal validateQuantity(GaoProductSku sku, BigDecimal quantity) {
|
||||||
TimiException.required(quantity, "购买数量不能为空");
|
TimiException.required(quantity, "购买数量不能为空");
|
||||||
TimiException.requiredTrue(0 < quantity.signum(), "购买数量必须大于 0");
|
TimiException.requiredTrue(0 < quantity.signum(), "购买数量必须大于 0");
|
||||||
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "购买数量最多保留三位小数");
|
TimiException.requiredTrue(3 >= quantity.stripTrailingZeros().scale(), "购买数量最多保留三位小数");
|
||||||
if (!Boolean.TRUE.equals(product.getAllowDecimalQuantity())) {
|
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
|
||||||
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
|
||||||
}
|
}
|
||||||
return quantity;
|
return quantity;
|
||||||
|
|||||||
@@ -37,4 +37,19 @@ public class GaoCustomerOrderProductRankView {
|
|||||||
|
|
||||||
/// 直接产品销售金额,单位分
|
/// 直接产品销售金额,单位分
|
||||||
private Long directSaleAmount;
|
private Long directSaleAmount;
|
||||||
|
|
||||||
|
/// 直接产品购买数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益购买数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 已预占数量,基础单位
|
||||||
|
private BigDecimal reservedBaseQuantity;
|
||||||
|
|
||||||
|
/// 已履约数量,基础单位
|
||||||
|
private BigDecimal fulfilledBaseQuantity;
|
||||||
|
|
||||||
|
/// 当前可履约数量,基础单位
|
||||||
|
private BigDecimal availableBaseQuantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// 客户订单 SKU 排行视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoCustomerOrderSkuRankView {
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 订单数
|
||||||
|
private Long orderCount;
|
||||||
|
|
||||||
|
/// 直接产品购买数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益购买数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 待履约数量,基础单位
|
||||||
|
private BigDecimal reservedBaseQuantity;
|
||||||
|
|
||||||
|
/// 已履约数量,基础单位
|
||||||
|
private BigDecimal fulfilledBaseQuantity;
|
||||||
|
|
||||||
|
/// 当前可履约数量,基础单位
|
||||||
|
private BigDecimal availableBaseQuantity;
|
||||||
|
|
||||||
|
/// 直接产品销售金额,单位分
|
||||||
|
private Long directSaleAmount;
|
||||||
|
}
|
||||||
@@ -11,15 +11,27 @@ import java.math.BigDecimal;
|
|||||||
@Data
|
@Data
|
||||||
public class GaoCustomerPackagePurchaseItem {
|
public class GaoCustomerPackagePurchaseItem {
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 产品名称快照,由后端生成
|
/// 产品名称快照,由后端生成
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// 产品 ID 快照,由后端生成
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照,由后端生成
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照,由后端生成
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照,由后端生成
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位快照,由后端生成
|
/// 单位快照,由后端生成
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public class GaoProductChartReq {
|
|||||||
/// 产品 ID
|
/// 产品 ID
|
||||||
private String productId;
|
private String productId;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
/// 分类 ID
|
/// 分类 ID
|
||||||
private String categoryId;
|
private String categoryId;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ public class GaoProductFulfillmentDailyStatView {
|
|||||||
/// 套餐权益履约数量
|
/// 套餐权益履约数量
|
||||||
private BigDecimal packageQuantity;
|
private BigDecimal packageQuantity;
|
||||||
|
|
||||||
|
/// 直接产品履约数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益履约数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
/// 履约基础单位数量
|
/// 履约基础单位数量
|
||||||
private BigDecimal baseQuantity;
|
private BigDecimal baseQuantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,4 +34,19 @@ public class GaoProductFulfillmentRankView {
|
|||||||
|
|
||||||
/// 当前可履约数量
|
/// 当前可履约数量
|
||||||
private BigDecimal availableQuantity;
|
private BigDecimal availableQuantity;
|
||||||
|
|
||||||
|
/// 直接产品购买数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益购买数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 待履约数量,基础单位
|
||||||
|
private BigDecimal reservedBaseQuantity;
|
||||||
|
|
||||||
|
/// 已履约数量,基础单位
|
||||||
|
private BigDecimal fulfilledBaseQuantity;
|
||||||
|
|
||||||
|
/// 当前可履约数量,基础单位
|
||||||
|
private BigDecimal availableBaseQuantity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ public class GaoProductSalesDailyStatView {
|
|||||||
/// 套餐权益数量
|
/// 套餐权益数量
|
||||||
private BigDecimal packageQuantity;
|
private BigDecimal packageQuantity;
|
||||||
|
|
||||||
|
/// 直接产品销售数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
/// 直接产品销售金额,单位分
|
/// 直接产品销售金额,单位分
|
||||||
private Long directSaleAmount;
|
private Long directSaleAmount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ public class GaoProductSalesRankView {
|
|||||||
/// 套餐权益数量
|
/// 套餐权益数量
|
||||||
private BigDecimal packageQuantity;
|
private BigDecimal packageQuantity;
|
||||||
|
|
||||||
|
/// 直接产品销售数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
/// 直接产品销售金额,单位分
|
/// 直接产品销售金额,单位分
|
||||||
private Long directSaleAmount;
|
private Long directSaleAmount;
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 每日履约统计视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuFulfillmentDailyStatView {
|
||||||
|
|
||||||
|
/// 日期值,格式为 yyyyMMdd
|
||||||
|
private Integer dateValue;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 直接产品履约数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益履约数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 履约基础单位数量
|
||||||
|
private BigDecimal baseQuantity;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 履约进度排行视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuFulfillmentRankView {
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 订单数
|
||||||
|
private Long orderCount;
|
||||||
|
|
||||||
|
/// 直接产品购买数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益购买数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 待履约数量,基础单位
|
||||||
|
private BigDecimal reservedBaseQuantity;
|
||||||
|
|
||||||
|
/// 已履约数量,基础单位
|
||||||
|
private BigDecimal fulfilledBaseQuantity;
|
||||||
|
|
||||||
|
/// 当前可履约数量,基础单位
|
||||||
|
private BigDecimal availableBaseQuantity;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 每日销售统计视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuSalesDailyStatView {
|
||||||
|
|
||||||
|
/// 日期值,格式为 yyyyMMdd
|
||||||
|
private Integer dateValue;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 包含该 SKU 的订单数
|
||||||
|
private Long orderCount;
|
||||||
|
|
||||||
|
/// 直接产品销售数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 直接产品销售金额,单位分
|
||||||
|
private Long directSaleAmount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 销售排行视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuSalesRankView {
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 包含该 SKU 的订单数
|
||||||
|
private Long orderCount;
|
||||||
|
|
||||||
|
/// 直接产品销售数量,基础单位
|
||||||
|
private BigDecimal directBaseQuantity;
|
||||||
|
|
||||||
|
/// 套餐权益数量,基础单位
|
||||||
|
private BigDecimal packageBaseQuantity;
|
||||||
|
|
||||||
|
/// 直接产品销售金额,单位分
|
||||||
|
private Long directSaleAmount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 库存健康度视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuStockHealthView {
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 分类 ID
|
||||||
|
private String categoryId;
|
||||||
|
|
||||||
|
/// 分类名称
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
|
/// 产品类型
|
||||||
|
private GaoProduct.ProductType productType;
|
||||||
|
|
||||||
|
/// 实际库存,基础单位
|
||||||
|
private BigDecimal onHandQuantity;
|
||||||
|
|
||||||
|
/// 预占库存,基础单位
|
||||||
|
private BigDecimal reservedQuantity;
|
||||||
|
|
||||||
|
/// 可用库存,基础单位
|
||||||
|
private BigDecimal availableQuantity;
|
||||||
|
|
||||||
|
/// 最低库存,基础单位
|
||||||
|
private BigDecimal minStockQuantity;
|
||||||
|
|
||||||
|
/// 1 为低于最低库存,0 为正常
|
||||||
|
private Integer lowStock;
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 每日库存变动统计视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuStockMovementDailyStatView {
|
||||||
|
|
||||||
|
/// 日期值,格式为 yyyyMMdd
|
||||||
|
private Integer dateValue;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 实际入库数量,基础单位
|
||||||
|
private BigDecimal inQuantity;
|
||||||
|
|
||||||
|
/// 实际出库数量,基础单位
|
||||||
|
private BigDecimal outQuantity;
|
||||||
|
|
||||||
|
/// 实际库存净变化,基础单位
|
||||||
|
private BigDecimal netOnHandQuantity;
|
||||||
|
|
||||||
|
/// 新增预占数量,基础单位
|
||||||
|
private BigDecimal reserveQuantity;
|
||||||
|
|
||||||
|
/// 释放预占数量,基础单位
|
||||||
|
private BigDecimal releaseQuantity;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// SKU 单位销售排行视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductSkuUnitSalesRankView {
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品名称快照
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// SKU 单位 ID
|
||||||
|
private String skuUnitId;
|
||||||
|
|
||||||
|
/// 单位名称快照
|
||||||
|
private String unitName;
|
||||||
|
|
||||||
|
/// 订单数
|
||||||
|
private Long orderCount;
|
||||||
|
|
||||||
|
/// 销售数量
|
||||||
|
private BigDecimal quantity;
|
||||||
|
|
||||||
|
/// 销售数量,基础单位
|
||||||
|
private BigDecimal baseQuantity;
|
||||||
|
|
||||||
|
/// 销售金额,单位分
|
||||||
|
private Long saleAmount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/// 已校验的 SKU 单位选项
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-06
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class GaoProductSkuUnitSelection {
|
||||||
|
|
||||||
|
/// 产品
|
||||||
|
private GaoProduct product;
|
||||||
|
|
||||||
|
/// SKU
|
||||||
|
private GaoProductSku sku;
|
||||||
|
|
||||||
|
/// SKU 单位
|
||||||
|
private GaoProductSkuUnit unit;
|
||||||
|
}
|
||||||
@@ -19,11 +19,11 @@ public class GaoProductStockChangeRequest {
|
|||||||
/// 门店 ID
|
/// 门店 ID
|
||||||
private String storeId;
|
private String storeId;
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 业务数量
|
/// 业务数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|||||||
@@ -41,4 +41,10 @@ public class GaoProductStockHealthView {
|
|||||||
|
|
||||||
/// 1 为低于最低库存,0 为正常
|
/// 1 为低于最低库存,0 为正常
|
||||||
private Integer lowStock;
|
private Integer lowStock;
|
||||||
|
|
||||||
|
/// 参与库存统计的 SKU 数量
|
||||||
|
private Long skuCount;
|
||||||
|
|
||||||
|
/// 低于最低库存的 SKU 数量
|
||||||
|
private Long lowStockSkuCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ public class GaoProductStockPage extends BasePage {
|
|||||||
/// 产品 ID
|
/// 产品 ID
|
||||||
private String productId;
|
private String productId;
|
||||||
|
|
||||||
/// 产品名称或编码关键词
|
/// 产品或 SKU 名称、编码、规格摘要关键词
|
||||||
private String keyword;
|
private String keyword;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/// 产品库存第一层汇总视图
|
||||||
|
///
|
||||||
|
/// @author Codex
|
||||||
|
/// @since 2026-09-07
|
||||||
|
@Data
|
||||||
|
public class GaoProductStockProductView {
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// 产品编码
|
||||||
|
private String productCode;
|
||||||
|
|
||||||
|
/// 产品名称
|
||||||
|
private String productName;
|
||||||
|
|
||||||
|
/// 产品类型
|
||||||
|
private GaoProduct.ProductType productType;
|
||||||
|
|
||||||
|
/// 可库存 SKU 数量
|
||||||
|
private Long skuCount;
|
||||||
|
|
||||||
|
/// 库存数量,使用基础单位
|
||||||
|
private BigDecimal onHandQuantity;
|
||||||
|
|
||||||
|
/// 预占数量,使用基础单位
|
||||||
|
private BigDecimal reservedQuantity;
|
||||||
|
|
||||||
|
/// 可用数量,使用基础单位
|
||||||
|
private BigDecimal availableQuantity;
|
||||||
|
}
|
||||||
@@ -17,9 +17,24 @@ public class GaoProductUnitSalesRankView {
|
|||||||
/// 产品名称
|
/// 产品名称
|
||||||
private String productName;
|
private String productName;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuName;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummary;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// 产品单位 ID
|
||||||
private String productUnitId;
|
private String productUnitId;
|
||||||
|
|
||||||
|
/// SKU 单位 ID
|
||||||
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 单位名称
|
/// 单位名称
|
||||||
private String unitName;
|
private String unitName;
|
||||||
|
|
||||||
@@ -29,6 +44,9 @@ public class GaoProductUnitSalesRankView {
|
|||||||
/// 销售数量
|
/// 销售数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|
||||||
|
/// 销售数量,基础单位
|
||||||
|
private BigDecimal baseQuantity;
|
||||||
|
|
||||||
/// 销售金额,单位分
|
/// 销售金额,单位分
|
||||||
private Long saleAmount;
|
private Long saleAmount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.gao.vo;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/// 已校验的产品单位选项
|
|
||||||
///
|
|
||||||
/// @author Codex
|
|
||||||
/// @since 2026-09-02
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class GaoProductUnitSelection {
|
|
||||||
|
|
||||||
/// 产品
|
|
||||||
private GaoProduct product;
|
|
||||||
|
|
||||||
/// 单位
|
|
||||||
private GaoProductUnit unit;
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,12 @@ public class GaoSalesOrderChartReq {
|
|||||||
/// 客户 ID
|
/// 客户 ID
|
||||||
private String customerId;
|
private String customerId;
|
||||||
|
|
||||||
|
/// 产品 ID
|
||||||
|
private String productId;
|
||||||
|
|
||||||
|
/// SKU ID
|
||||||
|
private String skuId;
|
||||||
|
|
||||||
/// 订单类型
|
/// 订单类型
|
||||||
private GaoSalesOrder.OrderType orderType;
|
private GaoSalesOrder.OrderType orderType;
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import java.math.BigDecimal;
|
|||||||
@Data
|
@Data
|
||||||
public class GaoSalesOrderItemRequest {
|
public class GaoSalesOrderItemRequest {
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
/// 购买数量
|
/// 购买数量
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gao.vo;
|
package com.imyeyu.api.modules.gao.vo;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
import com.imyeyu.api.modules.gao.entity.GaoProduct;
|
||||||
import com.imyeyu.api.modules.gao.entity.GaoProductUnit;
|
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
|
||||||
|
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -13,15 +14,27 @@ import java.math.BigDecimal;
|
|||||||
@Data
|
@Data
|
||||||
public class GaoStockChangeItem {
|
public class GaoStockChangeItem {
|
||||||
|
|
||||||
/// 产品 ID
|
/// SKU ID
|
||||||
private String productId;
|
private String skuId;
|
||||||
|
|
||||||
/// 产品单位 ID
|
/// SKU 单位 ID
|
||||||
private String productUnitId;
|
private String skuUnitId;
|
||||||
|
|
||||||
|
/// 产品 ID 快照
|
||||||
|
private String productIdSnapshot;
|
||||||
|
|
||||||
/// 产品名称快照
|
/// 产品名称快照
|
||||||
private String productNameSnapshot;
|
private String productNameSnapshot;
|
||||||
|
|
||||||
|
/// SKU 编码快照
|
||||||
|
private String skuCodeSnapshot;
|
||||||
|
|
||||||
|
/// SKU 名称快照
|
||||||
|
private String skuNameSnapshot;
|
||||||
|
|
||||||
|
/// 规格摘要快照
|
||||||
|
private String specSummarySnapshot;
|
||||||
|
|
||||||
/// 单位名称快照
|
/// 单位名称快照
|
||||||
private String unitSnapshot;
|
private String unitSnapshot;
|
||||||
|
|
||||||
@@ -40,15 +53,20 @@ public class GaoStockChangeItem {
|
|||||||
/// 生成库存项
|
/// 生成库存项
|
||||||
///
|
///
|
||||||
/// @param product 产品
|
/// @param product 产品
|
||||||
/// @param unit 单位
|
/// @param sku SKU
|
||||||
|
/// @param unit SKU 单位
|
||||||
/// @param quantity 业务数量
|
/// @param quantity 业务数量
|
||||||
/// @param baseQuantity 基础单位数量
|
/// @param baseQuantity 基础单位数量
|
||||||
/// @return 库存变更项
|
/// @return 库存变更项
|
||||||
public static GaoStockChangeItem of(GaoProduct product, GaoProductUnit unit, BigDecimal quantity, BigDecimal baseQuantity) {
|
public static GaoStockChangeItem of(GaoProduct product, GaoProductSku sku, GaoProductSkuUnit unit, BigDecimal quantity, BigDecimal baseQuantity) {
|
||||||
GaoStockChangeItem result = new GaoStockChangeItem();
|
GaoStockChangeItem result = new GaoStockChangeItem();
|
||||||
result.productId = product.getId();
|
result.skuId = sku.getId();
|
||||||
result.productUnitId = unit.getId();
|
result.skuUnitId = unit.getId();
|
||||||
|
result.productIdSnapshot = product.getId();
|
||||||
result.productNameSnapshot = product.getName();
|
result.productNameSnapshot = product.getName();
|
||||||
|
result.skuCodeSnapshot = sku.getCode();
|
||||||
|
result.skuNameSnapshot = sku.getName();
|
||||||
|
result.specSummarySnapshot = sku.getSpecSummary();
|
||||||
result.unitSnapshot = unit.getName();
|
result.unitSnapshot = unit.getName();
|
||||||
result.baseQuantityPerUnitSnapshot = unit.getBaseQuantityPerUnit();
|
result.baseQuantityPerUnitSnapshot = unit.getBaseQuantityPerUnit();
|
||||||
result.quantity = quantity;
|
result.quantity = quantity;
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
-- GAO 产品模块尚未上线,直接切换为 SPU -> SKU -> SKU 单位模型,不保留旧产品单位引用
|
||||||
|
|
||||||
|
CREATE TABLE `gao_product_spec` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`product_id` VARCHAR(36) NOT NULL COMMENT '产品 ID',
|
||||||
|
`name` VARCHAR(32) NOT NULL COMMENT '规格项名称',
|
||||||
|
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序值',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_product_spec_product_name` (`product_id`, `name`),
|
||||||
|
KEY `idx_gao_product_spec_product` (`product_id`, `deleted_at`, `sort`, `created_at`)
|
||||||
|
) COMMENT='GAO 产品规格项';
|
||||||
|
|
||||||
|
CREATE TABLE `gao_product_spec_value` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`spec_id` VARCHAR(36) NOT NULL COMMENT '规格项 ID',
|
||||||
|
`name` VARCHAR(64) NOT NULL COMMENT '规格值名称',
|
||||||
|
`enabled` TINYINT(1) NOT NULL COMMENT 'true 为启用',
|
||||||
|
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序值',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_product_spec_value_spec_name` (`spec_id`, `name`),
|
||||||
|
KEY `idx_gao_product_spec_value_spec` (`spec_id`, `enabled`, `deleted_at`, `sort`, `created_at`)
|
||||||
|
) COMMENT='GAO 产品规格值';
|
||||||
|
|
||||||
|
CREATE TABLE `gao_product_sku` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`store_id` VARCHAR(36) NOT NULL COMMENT '所属门店 ID',
|
||||||
|
`product_id` VARCHAR(36) NOT NULL COMMENT '所属产品 ID',
|
||||||
|
`code` VARCHAR(64) NOT NULL COMMENT '门店内 SKU 编码',
|
||||||
|
`barcode` VARCHAR(128) NULL COMMENT '门店内条码',
|
||||||
|
`name` VARCHAR(128) NOT NULL COMMENT 'SKU 展示名称',
|
||||||
|
`spec_summary` VARCHAR(255) NULL COMMENT '规格展示摘要',
|
||||||
|
`spec_value_key` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '按规格项排序的规格值组合键',
|
||||||
|
`stock_enabled` TINYINT(1) NOT NULL COMMENT 'true 为启用库存管理',
|
||||||
|
`allow_decimal_quantity` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'true 为允许业务数量保留小数',
|
||||||
|
`min_stock_quantity` DECIMAL(18,3) NULL COMMENT '最低库存,基础单位',
|
||||||
|
`status` VARCHAR(32) NOT NULL COMMENT 'ACTIVE 启用,INACTIVE 停用',
|
||||||
|
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序值',
|
||||||
|
`remark` VARCHAR(255) NULL COMMENT '备注',
|
||||||
|
`updated_by` VARCHAR(36) NOT NULL COMMENT '最后修改用户 ID',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_product_sku_store_code` (`store_id`, `code`),
|
||||||
|
UNIQUE KEY `uk_gao_product_sku_store_barcode` (`store_id`, `barcode`),
|
||||||
|
UNIQUE KEY `uk_gao_product_sku_product_spec_value_key` (`product_id`, `spec_value_key`),
|
||||||
|
KEY `idx_gao_product_sku_product` (`product_id`, `status`, `deleted_at`, `sort`, `name`),
|
||||||
|
KEY `idx_gao_product_sku_store_status` (`store_id`, `status`, `deleted_at`, `sort`, `name`)
|
||||||
|
) COMMENT='GAO 产品 SKU';
|
||||||
|
|
||||||
|
CREATE TABLE `gao_product_sku_spec_value` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
`spec_value_id` VARCHAR(36) NOT NULL COMMENT '规格值 ID',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_product_sku_spec_value_sku_value` (`sku_id`, `spec_value_id`),
|
||||||
|
KEY `idx_gao_product_sku_spec_value_value` (`spec_value_id`, `sku_id`)
|
||||||
|
) COMMENT='GAO SKU 规格值关联';
|
||||||
|
|
||||||
|
CREATE TABLE `gao_product_sku_unit` (
|
||||||
|
`id` VARCHAR(36) NOT NULL,
|
||||||
|
`sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
`name` VARCHAR(32) NOT NULL COMMENT '单位名称',
|
||||||
|
`base_quantity_per_unit` DECIMAL(18,6) NOT NULL COMMENT '每个业务单位对应的基础单位数量',
|
||||||
|
`sale_price` BIGINT NULL COMMENT '该业务单位建议售价,单位分',
|
||||||
|
`is_base` TINYINT(1) NOT NULL COMMENT 'true 为基础库存单位',
|
||||||
|
`allow_purchase` TINYINT(1) NOT NULL COMMENT 'true 为允许采购入库',
|
||||||
|
`allow_sale` TINYINT(1) NOT NULL COMMENT 'true 为允许销售和套餐配置',
|
||||||
|
`sort` INT NOT NULL DEFAULT 0 COMMENT '排序值',
|
||||||
|
`created_at` BIGINT NOT NULL,
|
||||||
|
`updated_at` BIGINT NULL,
|
||||||
|
`deleted_at` BIGINT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_gao_product_sku_unit_sku_name` (`sku_id`, `name`),
|
||||||
|
KEY `idx_gao_product_sku_unit_sku` (`sku_id`, `deleted_at`, `sort`)
|
||||||
|
) COMMENT='GAO 产品 SKU 单位';
|
||||||
|
|
||||||
|
ALTER TABLE `gao_product`
|
||||||
|
DROP COLUMN `allow_decimal_quantity`,
|
||||||
|
DROP COLUMN `min_stock_quantity`;
|
||||||
|
|
||||||
|
DROP TABLE `gao_product_unit`;
|
||||||
|
|
||||||
|
ALTER TABLE `gao_product_stock`
|
||||||
|
DROP KEY `uk_gao_product_stock_store_product`,
|
||||||
|
DROP KEY `idx_gao_product_stock_product`,
|
||||||
|
CHANGE COLUMN `product_id` `sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
ADD UNIQUE KEY `uk_gao_product_stock_store_sku` (`store_id`, `sku_id`),
|
||||||
|
ADD KEY `idx_gao_product_stock_sku` (`sku_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `gao_product_stock_record`
|
||||||
|
DROP KEY `uk_gao_product_stock_record_request_product`,
|
||||||
|
DROP KEY `idx_gao_product_stock_record_product_time`,
|
||||||
|
CHANGE COLUMN `product_id` `sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
CHANGE COLUMN `product_unit_id` `sku_unit_id` VARCHAR(36) NOT NULL COMMENT 'SKU 单位 ID',
|
||||||
|
ADD COLUMN `product_id_snapshot` VARCHAR(36) NOT NULL COMMENT '产品 ID 快照' AFTER `sku_unit_id`,
|
||||||
|
ADD COLUMN `sku_code_snapshot` VARCHAR(64) NOT NULL COMMENT 'SKU 编码快照' AFTER `product_name_snapshot`,
|
||||||
|
ADD COLUMN `sku_name_snapshot` VARCHAR(128) NOT NULL COMMENT 'SKU 名称快照' AFTER `sku_code_snapshot`,
|
||||||
|
ADD COLUMN `spec_summary_snapshot` VARCHAR(255) NULL COMMENT '规格摘要快照' AFTER `sku_name_snapshot`,
|
||||||
|
ADD UNIQUE KEY `uk_gao_product_stock_record_request_sku` (`store_id`, `request_no`, `operation_type`, `sku_id`),
|
||||||
|
ADD KEY `idx_gao_product_stock_record_sku_time` (`store_id`, `sku_id`, `occurred_at`, `id`);
|
||||||
|
|
||||||
|
ALTER TABLE `gao_sales_order_item`
|
||||||
|
DROP KEY `uk_gao_sales_order_item_product_unit`,
|
||||||
|
CHANGE COLUMN `product_id` `sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
CHANGE COLUMN `product_unit_id` `sku_unit_id` VARCHAR(36) NOT NULL COMMENT 'SKU 单位 ID',
|
||||||
|
ADD COLUMN `product_id_snapshot` VARCHAR(36) NOT NULL COMMENT '产品 ID 快照' AFTER `sku_unit_id`,
|
||||||
|
ADD COLUMN `sku_code_snapshot` VARCHAR(64) NOT NULL COMMENT 'SKU 编码快照' AFTER `product_name_snapshot`,
|
||||||
|
ADD COLUMN `sku_name_snapshot` VARCHAR(128) NOT NULL COMMENT 'SKU 名称快照' AFTER `sku_code_snapshot`,
|
||||||
|
ADD COLUMN `spec_summary_snapshot` VARCHAR(255) NULL COMMENT '规格摘要快照' AFTER `sku_name_snapshot`,
|
||||||
|
ADD UNIQUE KEY `uk_gao_sales_order_item_sku_unit` (`sales_order_id`, `sku_id`, `sku_unit_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `gao_package_config_item`
|
||||||
|
DROP KEY `idx_gao_package_config_item_product`,
|
||||||
|
DROP KEY `uk_gao_package_config_item_product_unit`,
|
||||||
|
CHANGE COLUMN `product_id` `sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
CHANGE COLUMN `product_unit_id` `sku_unit_id` VARCHAR(36) NOT NULL COMMENT 'SKU 单位 ID',
|
||||||
|
ADD COLUMN `product_id_snapshot` VARCHAR(36) NOT NULL COMMENT '产品 ID 快照' AFTER `sku_unit_id`,
|
||||||
|
ADD COLUMN `sku_code_snapshot` VARCHAR(64) NOT NULL COMMENT 'SKU 编码快照' AFTER `product_name_snapshot`,
|
||||||
|
ADD COLUMN `sku_name_snapshot` VARCHAR(128) NOT NULL COMMENT 'SKU 名称快照' AFTER `sku_code_snapshot`,
|
||||||
|
ADD COLUMN `spec_summary_snapshot` VARCHAR(255) NULL COMMENT '规格摘要快照' AFTER `sku_name_snapshot`,
|
||||||
|
ADD KEY `idx_gao_package_config_item_sku` (`sku_id`, `sku_unit_id`),
|
||||||
|
ADD UNIQUE KEY `uk_gao_package_config_item_sku_unit` (`package_config_id`, `sku_id`, `sku_unit_id`);
|
||||||
|
|
||||||
|
ALTER TABLE `gao_order_fulfillment_item`
|
||||||
|
CHANGE COLUMN `product_id` `sku_id` VARCHAR(36) NOT NULL COMMENT 'SKU ID',
|
||||||
|
CHANGE COLUMN `product_unit_id` `sku_unit_id` VARCHAR(36) NOT NULL COMMENT 'SKU 单位 ID',
|
||||||
|
ADD COLUMN `product_id_snapshot` VARCHAR(36) NOT NULL COMMENT '产品 ID 快照' AFTER `sku_unit_id`,
|
||||||
|
ADD COLUMN `sku_code_snapshot` VARCHAR(64) NOT NULL COMMENT 'SKU 编码快照' AFTER `product_name_snapshot`,
|
||||||
|
ADD COLUMN `sku_name_snapshot` VARCHAR(128) NOT NULL COMMENT 'SKU 名称快照' AFTER `sku_code_snapshot`,
|
||||||
|
ADD COLUMN `spec_summary_snapshot` VARCHAR(255) NULL COMMENT '规格摘要快照' AFTER `sku_name_snapshot`,
|
||||||
|
ADD KEY `idx_gao_order_fulfillment_item_sku` (`sku_id`, `sku_unit_id`);
|
||||||
@@ -5,6 +5,10 @@
|
|||||||
AND p.`deleted_at` IS NULL
|
AND p.`deleted_at` IS NULL
|
||||||
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||||
<if test="productId != null and productId != ''">AND p.`id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND p.`id` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND EXISTS (
|
||||||
|
SELECT 1 FROM `gao_product_sku` k_filter
|
||||||
|
WHERE k_filter.`id` = #{skuId} AND k_filter.`product_id` = p.`id` AND k_filter.`deleted_at` IS NULL
|
||||||
|
)</if>
|
||||||
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
</sql>
|
</sql>
|
||||||
@@ -14,7 +18,8 @@
|
|||||||
AND i.`deleted_at` IS NULL
|
AND i.`deleted_at` IS NULL
|
||||||
AND o.`status` <> 'CANCELLED'
|
AND o.`status` <> 'CANCELLED'
|
||||||
<if test="storeId != null and storeId != ''">AND o.`store_id` = #{storeId}</if>
|
<if test="storeId != null and storeId != ''">AND o.`store_id` = #{storeId}</if>
|
||||||
<if test="productId != null and productId != ''">AND i.`product_id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND i.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND i.`sku_id` = #{skuId}</if>
|
||||||
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
AND o.`purchased_at` >= #{beginAt}
|
AND o.`purchased_at` >= #{beginAt}
|
||||||
@@ -27,10 +32,12 @@
|
|||||||
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
FROM `gao_sales_order_item` i
|
FROM `gao_sales_order_item` i
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
INNER JOIN `gao_product` p ON p.`id` = i.`product_id` AND p.`store_id` = o.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
WHERE 1 = 1
|
WHERE 1 = 1
|
||||||
<include refid="orderProductFilter"/>
|
<include refid="orderProductFilter"/>
|
||||||
GROUP BY DATE_FORMAT(FROM_UNIXTIME(o.`purchased_at` / 1000), '%Y%m%d')
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(o.`purchased_at` / 1000), '%Y%m%d')
|
||||||
@@ -39,22 +46,24 @@
|
|||||||
|
|
||||||
<select id="selectSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSalesRankView">
|
<select id="selectSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSalesRankView">
|
||||||
SELECT
|
SELECT
|
||||||
i.`product_id` AS `productId`,
|
i.`product_id_snapshot` AS `productId`,
|
||||||
MAX(i.`product_name_snapshot`) AS `productName`,
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
FROM `gao_sales_order_item` i
|
FROM `gao_sales_order_item` i
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
INNER JOIN `gao_product` p ON p.`id` = i.`product_id` AND p.`store_id` = o.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
WHERE 1 = 1
|
WHERE 1 = 1
|
||||||
<include refid="orderProductFilter"/>
|
<include refid="orderProductFilter"/>
|
||||||
GROUP BY i.`product_id`
|
GROUP BY i.`product_id_snapshot`
|
||||||
ORDER BY (SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END)
|
ORDER BY (SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)
|
||||||
+ SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END)) DESC,
|
+ SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)) DESC,
|
||||||
directSaleAmount DESC,
|
directSaleAmount DESC,
|
||||||
i.`product_id` ASC
|
i.`product_id_snapshot` ASC
|
||||||
LIMIT #{topN}
|
LIMIT #{topN}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -65,17 +74,22 @@
|
|||||||
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`quantity` ELSE -fi.`quantity` END ELSE 0 END), 0) AS `directQuantity`,
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`quantity` ELSE -fi.`quantity` END ELSE 0 END), 0) AS `directQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT'
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT'
|
||||||
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`quantity` ELSE -fi.`quantity` END ELSE 0 END), 0) AS `packageQuantity`,
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`quantity` ELSE -fi.`quantity` END ELSE 0 END), 0) AS `packageQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT'
|
||||||
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT'
|
||||||
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END), 0) AS `baseQuantity`
|
COALESCE(SUM(CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END), 0) AS `baseQuantity`
|
||||||
FROM `gao_order_fulfillment` f
|
FROM `gao_order_fulfillment` f
|
||||||
INNER JOIN `gao_order_fulfillment_item` fi ON fi.`fulfillment_id` = f.`id`
|
INNER JOIN `gao_order_fulfillment_item` fi ON fi.`fulfillment_id` = f.`id`
|
||||||
INNER JOIN `gao_sales_order_item` i ON i.`id` = fi.`source_order_item_id`
|
INNER JOIN `gao_sales_order_item` i ON i.`id` = fi.`source_order_item_id`
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = f.`sales_order_id` AND o.`deleted_at` IS NULL
|
INNER JOIN `gao_sales_order` o ON o.`id` = f.`sales_order_id` AND o.`deleted_at` IS NULL
|
||||||
INNER JOIN `gao_product` p ON p.`id` = fi.`product_id` AND p.`store_id` = f.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = fi.`product_id_snapshot` AND p.`store_id` = f.`store_id`
|
||||||
WHERE f.`store_id` = #{storeId}
|
WHERE f.`store_id` = #{storeId}
|
||||||
AND f.`status` = 'VERIFIED'
|
AND f.`status` = 'VERIFIED'
|
||||||
AND f.`delivered_at` >= #{beginAt}
|
AND f.`delivered_at` >= #{beginAt}
|
||||||
AND f.`delivered_at` <= #{endAt}
|
AND f.`delivered_at` <= #{endAt}
|
||||||
<if test="productId != null and productId != ''">AND fi.`product_id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND fi.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND fi.`sku_id` = #{skuId}</if>
|
||||||
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
GROUP BY DATE_FORMAT(FROM_UNIXTIME(f.`delivered_at` / 1000), '%Y%m%d')
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(f.`delivered_at` / 1000), '%Y%m%d')
|
||||||
@@ -84,21 +98,26 @@
|
|||||||
|
|
||||||
<select id="selectFulfillmentRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductFulfillmentRankView">
|
<select id="selectFulfillmentRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductFulfillmentRankView">
|
||||||
SELECT
|
SELECT
|
||||||
i.`product_id` AS `productId`,
|
i.`product_id_snapshot` AS `productId`,
|
||||||
MAX(i.`product_name_snapshot`) AS `productName`,
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END), 0) AS `packageQuantity`,
|
||||||
COALESCE(SUM(i.`reserved_quantity`), 0) AS `reservedQuantity`,
|
COALESCE(SUM(i.`reserved_quantity`), 0) AS `reservedQuantity`,
|
||||||
COALESCE(SUM(i.`fulfilled_quantity`), 0) AS `fulfilledQuantity`,
|
COALESCE(SUM(i.`fulfilled_quantity`), 0) AS `fulfilledQuantity`,
|
||||||
COALESCE(SUM(i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`), 0) AS `availableQuantity`
|
COALESCE(SUM(i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`), 0) AS `availableQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`reserved_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `reservedBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`fulfilled_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `fulfilledBaseQuantity`,
|
||||||
|
COALESCE(SUM((i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`) * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `availableBaseQuantity`
|
||||||
FROM `gao_sales_order_item` i
|
FROM `gao_sales_order_item` i
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
INNER JOIN `gao_product` p ON p.`id` = i.`product_id` AND p.`store_id` = o.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
WHERE 1 = 1
|
WHERE 1 = 1
|
||||||
<include refid="orderProductFilter"/>
|
<include refid="orderProductFilter"/>
|
||||||
GROUP BY i.`product_id`
|
GROUP BY i.`product_id_snapshot`
|
||||||
ORDER BY `availableQuantity` DESC, `fulfilledQuantity` DESC, i.`product_id` ASC
|
ORDER BY `availableBaseQuantity` DESC, `fulfilledBaseQuantity` DESC, i.`product_id_snapshot` ASC
|
||||||
LIMIT #{topN}
|
LIMIT #{topN}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -111,11 +130,12 @@
|
|||||||
COALESCE(SUM(CASE WHEN r.`reserved_delta` > 0 THEN r.`reserved_delta` ELSE 0 END), 0) AS `reserveQuantity`,
|
COALESCE(SUM(CASE WHEN r.`reserved_delta` > 0 THEN r.`reserved_delta` ELSE 0 END), 0) AS `reserveQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN r.`reserved_delta` < 0 THEN -r.`reserved_delta` ELSE 0 END), 0) AS `releaseQuantity`
|
COALESCE(SUM(CASE WHEN r.`reserved_delta` < 0 THEN -r.`reserved_delta` ELSE 0 END), 0) AS `releaseQuantity`
|
||||||
FROM `gao_product_stock_record` r
|
FROM `gao_product_stock_record` r
|
||||||
INNER JOIN `gao_product` p ON p.`id` = r.`product_id` AND p.`store_id` = r.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = r.`product_id_snapshot` AND p.`store_id` = r.`store_id`
|
||||||
WHERE r.`store_id` = #{storeId}
|
WHERE r.`store_id` = #{storeId}
|
||||||
AND r.`occurred_at` >= #{beginAt}
|
AND r.`occurred_at` >= #{beginAt}
|
||||||
AND r.`occurred_at` <= #{endAt}
|
AND r.`occurred_at` <= #{endAt}
|
||||||
<if test="productId != null and productId != ''">AND r.`product_id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND r.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND r.`sku_id` = #{skuId}</if>
|
||||||
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
GROUP BY DATE_FORMAT(FROM_UNIXTIME(r.`occurred_at` / 1000), '%Y%m%d')
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(r.`occurred_at` / 1000), '%Y%m%d')
|
||||||
@@ -129,19 +149,25 @@
|
|||||||
p.`category_id` AS `categoryId`,
|
p.`category_id` AS `categoryId`,
|
||||||
c.`name` AS `categoryName`,
|
c.`name` AS `categoryName`,
|
||||||
p.`product_type` AS `productType`,
|
p.`product_type` AS `productType`,
|
||||||
COALESCE(s.`on_hand_quantity`, 0) AS `onHandQuantity`,
|
COALESCE(SUM(s.`on_hand_quantity`), 0) AS `onHandQuantity`,
|
||||||
COALESCE(s.`reserved_quantity`, 0) AS `reservedQuantity`,
|
COALESCE(SUM(s.`reserved_quantity`), 0) AS `reservedQuantity`,
|
||||||
COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0) AS `availableQuantity`,
|
COALESCE(SUM(s.`on_hand_quantity` - s.`reserved_quantity`), 0) AS `availableQuantity`,
|
||||||
p.`min_stock_quantity` AS `minStockQuantity`,
|
CASE WHEN COUNT(k.`min_stock_quantity`) = 0 THEN NULL ELSE SUM(k.`min_stock_quantity`) END AS `minStockQuantity`,
|
||||||
CASE WHEN p.`min_stock_quantity` IS NOT NULL
|
MAX(CASE WHEN k.`min_stock_quantity` IS NOT NULL
|
||||||
AND COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0) < p.`min_stock_quantity`
|
AND COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0) < k.`min_stock_quantity`
|
||||||
THEN 1 ELSE 0 END AS `lowStock`
|
THEN 1 ELSE 0 END) AS `lowStock`
|
||||||
|
, COUNT(k.`id`) AS `skuCount`
|
||||||
|
, SUM(CASE WHEN k.`min_stock_quantity` IS NOT NULL
|
||||||
|
AND COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0) < k.`min_stock_quantity`
|
||||||
|
THEN 1 ELSE 0 END) AS `lowStockSkuCount`
|
||||||
FROM `gao_product` p
|
FROM `gao_product` p
|
||||||
LEFT JOIN `gao_product_category` c ON c.`id` = p.`category_id` AND c.`store_id` = p.`store_id` AND c.`deleted_at` IS NULL
|
LEFT JOIN `gao_product_category` c ON c.`id` = p.`category_id` AND c.`store_id` = p.`store_id` AND c.`deleted_at` IS NULL
|
||||||
LEFT JOIN `gao_product_stock` s ON s.`product_id` = p.`id` AND s.`store_id` = p.`store_id`
|
INNER JOIN `gao_product_sku` k ON k.`product_id` = p.`id` AND k.`deleted_at` IS NULL
|
||||||
|
LEFT JOIN `gao_product_stock` s ON s.`sku_id` = k.`id` AND s.`store_id` = p.`store_id`
|
||||||
WHERE p.`store_id` = #{storeId}
|
WHERE p.`store_id` = #{storeId}
|
||||||
AND p.`stock_enabled` = 1
|
AND k.`stock_enabled` = 1
|
||||||
<include refid="productFilter"/>
|
<include refid="productFilter"/>
|
||||||
|
GROUP BY p.`id`, p.`name`, p.`category_id`, c.`name`, p.`product_type`, p.`sort`
|
||||||
ORDER BY `lowStock` DESC, `availableQuantity` ASC, p.`sort` ASC, p.`name` ASC
|
ORDER BY `lowStock` DESC, `availableQuantity` ASC, p.`sort` ASC, p.`name` ASC
|
||||||
LIMIT #{topN}
|
LIMIT #{topN}
|
||||||
</select>
|
</select>
|
||||||
@@ -172,20 +198,26 @@
|
|||||||
|
|
||||||
<select id="selectUnitSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductUnitSalesRankView">
|
<select id="selectUnitSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductUnitSalesRankView">
|
||||||
SELECT
|
SELECT
|
||||||
i.`product_id` AS `productId`,
|
i.`product_id_snapshot` AS `productId`,
|
||||||
MAX(i.`product_name_snapshot`) AS `productName`,
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
i.`product_unit_id` AS `productUnitId`,
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
i.`sku_unit_id` AS `productUnitId`,
|
||||||
|
i.`sku_unit_id` AS `skuUnitId`,
|
||||||
MAX(i.`unit_snapshot`) AS `unitName`,
|
MAX(i.`unit_snapshot`) AS `unitName`,
|
||||||
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
COALESCE(SUM(i.`quantity`), 0) AS `quantity`,
|
COALESCE(SUM(i.`quantity`), 0) AS `quantity`,
|
||||||
|
COALESCE(SUM(i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `baseQuantity`,
|
||||||
COALESCE(SUM(i.`line_amount`), 0) AS `saleAmount`
|
COALESCE(SUM(i.`line_amount`), 0) AS `saleAmount`
|
||||||
FROM `gao_sales_order_item` i
|
FROM `gao_sales_order_item` i
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
INNER JOIN `gao_product` p ON p.`id` = i.`product_id` AND p.`store_id` = o.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
WHERE i.`item_type` = 'DIRECT_PRODUCT'
|
WHERE i.`item_type` = 'DIRECT_PRODUCT'
|
||||||
<include refid="orderProductFilter"/>
|
<include refid="orderProductFilter"/>
|
||||||
GROUP BY i.`product_id`, i.`product_unit_id`
|
GROUP BY i.`product_id_snapshot`, i.`sku_id`, i.`sku_unit_id`
|
||||||
ORDER BY `quantity` DESC, `saleAmount` DESC, i.`product_id` ASC, i.`product_unit_id` ASC
|
ORDER BY `baseQuantity` DESC, `saleAmount` DESC, i.`product_id_snapshot` ASC, i.`sku_id` ASC, i.`sku_unit_id` ASC
|
||||||
LIMIT #{topN}
|
LIMIT #{topN}
|
||||||
</select>
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSkuChartMapper">
|
||||||
|
<sql id="skuOrderProductFilter">
|
||||||
|
AND o.`deleted_at` IS NULL
|
||||||
|
AND i.`deleted_at` IS NULL
|
||||||
|
AND o.`status` <> 'CANCELLED'
|
||||||
|
AND o.`store_id` = #{storeId}
|
||||||
|
<if test="productId != null and productId != ''">AND i.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND i.`sku_id` = #{skuId}</if>
|
||||||
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
|
AND o.`purchased_at` >= #{beginAt}
|
||||||
|
AND o.`purchased_at` <= #{endAt}
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectSalesDaily" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesDailyStatView">
|
||||||
|
SELECT
|
||||||
|
CAST(DATE_FORMAT(FROM_UNIXTIME(o.`purchased_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,
|
||||||
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(i.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
|
FROM `gao_sales_order_item` i
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
|
WHERE 1 = 1
|
||||||
|
<include refid="skuOrderProductFilter"/>
|
||||||
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(o.`purchased_at` / 1000), '%Y%m%d'), i.`sku_id`
|
||||||
|
ORDER BY `dateValue` ASC, i.`sku_id` ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuSalesRankView">
|
||||||
|
SELECT
|
||||||
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(i.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
|
FROM `gao_sales_order_item` i
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
|
WHERE 1 = 1
|
||||||
|
<include refid="skuOrderProductFilter"/>
|
||||||
|
GROUP BY i.`sku_id`
|
||||||
|
ORDER BY (SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)
|
||||||
|
+ SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)) DESC,
|
||||||
|
directSaleAmount DESC, i.`sku_id` ASC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectUnitSalesRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSalesRankView">
|
||||||
|
SELECT
|
||||||
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(i.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
|
i.`sku_unit_id` AS `skuUnitId`,
|
||||||
|
MAX(i.`unit_snapshot`) AS `unitName`,
|
||||||
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
|
COALESCE(SUM(i.`quantity`), 0) AS `quantity`,
|
||||||
|
COALESCE(SUM(i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `baseQuantity`,
|
||||||
|
COALESCE(SUM(i.`line_amount`), 0) AS `saleAmount`
|
||||||
|
FROM `gao_sales_order_item` i
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
|
WHERE i.`item_type` = 'DIRECT_PRODUCT'
|
||||||
|
<include refid="skuOrderProductFilter"/>
|
||||||
|
GROUP BY i.`sku_id`, i.`sku_unit_id`
|
||||||
|
ORDER BY `baseQuantity` DESC, `saleAmount` DESC, i.`sku_id` ASC, i.`sku_unit_id` ASC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectFulfillmentDaily" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentDailyStatView">
|
||||||
|
SELECT
|
||||||
|
CAST(DATE_FORMAT(FROM_UNIXTIME(f.`delivered_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,
|
||||||
|
fi.`sku_id` AS `skuId`,
|
||||||
|
MAX(fi.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(fi.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(fi.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(fi.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(fi.`product_name_snapshot`) AS `productName`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT'
|
||||||
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT'
|
||||||
|
THEN CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN f.`source_fulfillment_id` IS NULL THEN fi.`base_quantity` ELSE -fi.`base_quantity` END), 0) AS `baseQuantity`
|
||||||
|
FROM `gao_order_fulfillment` f
|
||||||
|
INNER JOIN `gao_order_fulfillment_item` fi ON fi.`fulfillment_id` = f.`id`
|
||||||
|
INNER JOIN `gao_sales_order_item` i ON i.`id` = fi.`source_order_item_id`
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = f.`sales_order_id` AND o.`deleted_at` IS NULL
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = fi.`product_id_snapshot` AND p.`store_id` = f.`store_id`
|
||||||
|
WHERE f.`store_id` = #{storeId}
|
||||||
|
AND f.`status` = 'VERIFIED'
|
||||||
|
AND i.`deleted_at` IS NULL
|
||||||
|
AND f.`delivered_at` >= #{beginAt}
|
||||||
|
AND f.`delivered_at` <= #{endAt}
|
||||||
|
<if test="productId != null and productId != ''">AND fi.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND fi.`sku_id` = #{skuId}</if>
|
||||||
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(f.`delivered_at` / 1000), '%Y%m%d'), fi.`sku_id`
|
||||||
|
ORDER BY `dateValue` ASC, fi.`sku_id` ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectFulfillmentRank" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuFulfillmentRankView">
|
||||||
|
SELECT
|
||||||
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(i.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`reserved_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `reservedBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`fulfilled_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `fulfilledBaseQuantity`,
|
||||||
|
COALESCE(SUM((i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`) * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `availableBaseQuantity`
|
||||||
|
FROM `gao_sales_order_item` i
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
|
WHERE 1 = 1
|
||||||
|
<include refid="skuOrderProductFilter"/>
|
||||||
|
GROUP BY i.`sku_id`
|
||||||
|
ORDER BY `availableBaseQuantity` DESC, `fulfilledBaseQuantity` DESC, i.`sku_id` ASC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectStockMovementDaily" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuStockMovementDailyStatView">
|
||||||
|
SELECT
|
||||||
|
CAST(DATE_FORMAT(FROM_UNIXTIME(r.`occurred_at` / 1000), '%Y%m%d') AS UNSIGNED) AS `dateValue`,
|
||||||
|
r.`sku_id` AS `skuId`,
|
||||||
|
MAX(r.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(r.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(r.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
MAX(r.`product_id_snapshot`) AS `productId`,
|
||||||
|
MAX(r.`product_name_snapshot`) AS `productName`,
|
||||||
|
COALESCE(SUM(CASE WHEN r.`on_hand_delta` > 0 THEN r.`on_hand_delta` ELSE 0 END), 0) AS `inQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN r.`on_hand_delta` < 0 THEN -r.`on_hand_delta` ELSE 0 END), 0) AS `outQuantity`,
|
||||||
|
COALESCE(SUM(r.`on_hand_delta`), 0) AS `netOnHandQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN r.`reserved_delta` > 0 THEN r.`reserved_delta` ELSE 0 END), 0) AS `reserveQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN r.`reserved_delta` < 0 THEN -r.`reserved_delta` ELSE 0 END), 0) AS `releaseQuantity`
|
||||||
|
FROM `gao_product_stock_record` r
|
||||||
|
WHERE r.`store_id` = #{storeId}
|
||||||
|
AND r.`occurred_at` >= #{beginAt}
|
||||||
|
AND r.`occurred_at` <= #{endAt}
|
||||||
|
<if test="productId != null and productId != ''">AND r.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND r.`sku_id` = #{skuId}</if>
|
||||||
|
GROUP BY DATE_FORMAT(FROM_UNIXTIME(r.`occurred_at` / 1000), '%Y%m%d'), r.`sku_id`
|
||||||
|
ORDER BY `dateValue` ASC, r.`sku_id` ASC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectStockHealth" resultType="com.imyeyu.api.modules.gao.vo.GaoProductSkuStockHealthView">
|
||||||
|
SELECT
|
||||||
|
k.`id` AS `skuId`,
|
||||||
|
k.`code` AS `skuCode`,
|
||||||
|
k.`name` AS `skuName`,
|
||||||
|
k.`spec_summary` AS `specSummary`,
|
||||||
|
p.`id` AS `productId`,
|
||||||
|
p.`name` AS `productName`,
|
||||||
|
p.`category_id` AS `categoryId`,
|
||||||
|
c.`name` AS `categoryName`,
|
||||||
|
p.`product_type` AS `productType`,
|
||||||
|
COALESCE(s.`on_hand_quantity`, 0) AS `onHandQuantity`,
|
||||||
|
COALESCE(s.`reserved_quantity`, 0) AS `reservedQuantity`,
|
||||||
|
COALESCE(s.`on_hand_quantity` - s.`reserved_quantity`, 0) AS `availableQuantity`,
|
||||||
|
k.`min_stock_quantity` AS `minStockQuantity`,
|
||||||
|
CASE WHEN k.`min_stock_quantity` IS NOT NULL
|
||||||
|
AND COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0) < k.`min_stock_quantity`
|
||||||
|
THEN 1 ELSE 0 END AS `lowStock`
|
||||||
|
FROM `gao_product_sku` k
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = k.`product_id` AND p.`store_id` = k.`store_id` AND p.`deleted_at` IS NULL
|
||||||
|
LEFT JOIN `gao_product_category` c ON c.`id` = p.`category_id` AND c.`store_id` = p.`store_id` AND c.`deleted_at` IS NULL
|
||||||
|
LEFT JOIN `gao_product_stock` s ON s.`sku_id` = k.`id` AND s.`store_id` = k.`store_id`
|
||||||
|
WHERE k.`store_id` = #{storeId}
|
||||||
|
AND k.`deleted_at` IS NULL
|
||||||
|
AND k.`stock_enabled` = 1
|
||||||
|
<if test="productId != null and productId != ''">AND k.`product_id` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND k.`id` = #{skuId}</if>
|
||||||
|
<if test="categoryId != null and categoryId != ''">AND p.`category_id` = #{categoryId}</if>
|
||||||
|
<if test="productType != null">AND p.`product_type` = #{productType}</if>
|
||||||
|
ORDER BY `lowStock` DESC, `availableQuantity` ASC, p.`sort` ASC, k.`sort` ASC, k.`id` ASC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSkuMapper">
|
||||||
|
<select id="selectByProductId" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `product_id` = #{productId} AND `deleted_at` IS NULL ORDER BY `sort`, `created_at`
|
||||||
|
</select>
|
||||||
|
<select id="selectByProductIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `deleted_at` IS NULL
|
||||||
|
<choose><when test="productIdList != null and !productIdList.isEmpty()">AND `product_id` IN <foreach collection="productIdList" item="id" open="(" separator="," close=")">#{id}</foreach></when><otherwise>AND FALSE</otherwise></choose>
|
||||||
|
ORDER BY `product_id`, `sort`, `created_at`
|
||||||
|
</select>
|
||||||
|
<select id="selectByStoreIdAndCode" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `store_id` = #{storeId} AND `code` = #{code} AND `deleted_at` IS NULL <if test="excludeId != null">AND `id` != #{excludeId}</if> LIMIT 1
|
||||||
|
</select>
|
||||||
|
<select id="selectByStoreIdAndBarcode" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `store_id` = #{storeId} AND `barcode` = #{barcode} AND `deleted_at` IS NULL <if test="excludeId != null">AND `id` != #{excludeId}</if> LIMIT 1
|
||||||
|
</select>
|
||||||
|
<select id="selectByProductIdAndSpecValueKey" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `product_id` = #{productId} AND `spec_value_key` = #{specValueKey} AND `deleted_at` IS NULL <if test="excludeId != null">AND `id` != #{excludeId}</if> LIMIT 1
|
||||||
|
</select>
|
||||||
|
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSku">
|
||||||
|
SELECT * FROM `gao_product_sku` WHERE `id` = #{id} AND `deleted_at` IS NULL FOR UPDATE
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSkuSpecValueMapper">
|
||||||
|
<select id="selectBySkuIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSkuSpecValue">SELECT * FROM `gao_product_sku_spec_value` WHERE 1 = 1 <choose><when test="skuIdList != null and !skuIdList.isEmpty()">AND `sku_id` IN <foreach collection="skuIdList" item="id" open="(" separator="," close=")">#{id}</foreach></when><otherwise>AND FALSE</otherwise></choose></select>
|
||||||
|
<delete id="deleteBySkuId">DELETE FROM `gao_product_sku_spec_value` WHERE `sku_id`=#{skuId}</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSkuUnitMapper">
|
||||||
|
<select id="selectBySkuId" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit">
|
||||||
|
SELECT * FROM `gao_product_sku_unit` WHERE `sku_id` = #{skuId} AND `deleted_at` IS NULL ORDER BY `sort`, `created_at`
|
||||||
|
</select>
|
||||||
|
<select id="selectBySkuIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit">
|
||||||
|
SELECT * FROM `gao_product_sku_unit` WHERE `deleted_at` IS NULL
|
||||||
|
<choose><when test="skuIdList != null and !skuIdList.isEmpty()">AND `sku_id` IN <foreach collection="skuIdList" item="id" open="(" separator="," close=")">#{id}</foreach></when><otherwise>AND FALSE</otherwise></choose>
|
||||||
|
ORDER BY `sku_id`, `sort`, `created_at`
|
||||||
|
</select>
|
||||||
|
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit">
|
||||||
|
SELECT * FROM `gao_product_sku_unit` WHERE `id` = #{id} AND `deleted_at` IS NULL FOR UPDATE
|
||||||
|
</select>
|
||||||
|
<update id="updateUnit">UPDATE `gao_product_sku_unit` SET `name`=#{name}, `base_quantity_per_unit`=#{baseQuantityPerUnit}, `sale_price`=#{salePrice}, `is_base`=#{isBase}, `allow_purchase`=#{allowPurchase}, `allow_sale`=#{allowSale}, `sort`=#{sort}, `updated_at`=FLOOR(UNIX_TIMESTAMP(NOW(3))*1000) WHERE `id`=#{id} AND `sku_id`=#{skuId} AND `deleted_at` IS NULL</update>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSpecMapper">
|
||||||
|
<select id="selectByProductId" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSpec">SELECT * FROM `gao_product_spec` WHERE `product_id`=#{productId} AND `deleted_at` IS NULL ORDER BY `sort`, `created_at`</select>
|
||||||
|
<select id="selectByProductIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSpec">SELECT * FROM `gao_product_spec` WHERE `deleted_at` IS NULL <choose><when test="productIdList != null and !productIdList.isEmpty()">AND `product_id` IN <foreach collection="productIdList" item="id" open="(" separator="," close=")">#{id}</foreach></when><otherwise>AND FALSE</otherwise></choose> ORDER BY `product_id`, `sort`, `created_at`</select>
|
||||||
|
<select id="selectProductIdBySpecValueId" resultType="string">SELECT `s`.`product_id` FROM `gao_product_spec_value` `v` INNER JOIN `gao_product_spec` `s` ON `s`.`id`=`v`.`spec_id` AND `s`.`deleted_at` IS NULL WHERE `v`.`id`=#{specValueId} AND `v`.`deleted_at` IS NULL</select>
|
||||||
|
<select id="selectProductIdBySpecId" resultType="string">SELECT `product_id` FROM `gao_product_spec` WHERE `id`=#{specId} AND `deleted_at` IS NULL</select>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?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.imyeyu.api.modules.gao.mapper.GaoProductSpecValueMapper">
|
||||||
|
<select id="selectById" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSpecValue">SELECT * FROM `gao_product_spec_value` WHERE `id` = #{id} AND `deleted_at` IS NULL</select>
|
||||||
|
<select id="selectBySpecIdList" resultType="com.imyeyu.api.modules.gao.entity.GaoProductSpecValue">SELECT * FROM `gao_product_spec_value` WHERE `deleted_at` IS NULL <choose><when test="specIdList != null and !specIdList.isEmpty()">AND `spec_id` IN <foreach collection="specIdList" item="id" open="(" separator="," close=")">#{id}</foreach></when><otherwise>AND FALSE</otherwise></choose> ORDER BY `spec_id`, `sort`, `created_at`</select>
|
||||||
|
</mapper>
|
||||||
@@ -3,55 +3,92 @@
|
|||||||
<mapper namespace="com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper">
|
<mapper namespace="com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper">
|
||||||
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
||||||
SELECT * FROM `gao_product_stock`
|
SELECT * FROM `gao_product_stock`
|
||||||
WHERE `store_id` = #{storeId} AND `product_id` = #{productId}
|
WHERE `store_id` = #{storeId} AND `sku_id` = #{skuId}
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
</select>
|
</select>
|
||||||
<select id="selectByStoreIdAndProductId" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
<select id="selectByStoreIdAndSkuId" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
||||||
SELECT * FROM `gao_product_stock`
|
SELECT * FROM `gao_product_stock`
|
||||||
WHERE `store_id` = #{storeId} AND `product_id` = #{productId}
|
WHERE `store_id` = #{storeId} AND `sku_id` = #{skuId}
|
||||||
</select>
|
</select>
|
||||||
<select id="countByQuery" resultType="long">
|
<select id="countByQuery" resultType="long">
|
||||||
SELECT COUNT(1)
|
SELECT COUNT(1)
|
||||||
FROM `gao_product` p
|
FROM `gao_product_sku` k
|
||||||
LEFT JOIN `gao_product_stock` s ON s.`product_id` = p.`id`
|
JOIN `gao_product` p ON p.`id` = k.`product_id`
|
||||||
|
LEFT JOIN `gao_product_stock` s ON s.`sku_id` = k.`id`
|
||||||
<choose>
|
<choose>
|
||||||
<when test="storeId != null and storeId != ''">AND s.`store_id` = #{storeId}</when>
|
<when test="storeId != null and storeId != ''">AND s.`store_id` = #{storeId}</when>
|
||||||
<otherwise>AND s.`store_id` = p.`store_id`</otherwise>
|
<otherwise>AND s.`store_id` = k.`store_id`</otherwise>
|
||||||
</choose>
|
</choose>
|
||||||
WHERE p.`deleted_at` IS NULL
|
WHERE p.`deleted_at` IS NULL AND k.`deleted_at` IS NULL
|
||||||
AND p.`stock_enabled` = 1
|
AND k.`stock_enabled` = 1 AND k.`status` = 'ACTIVE'
|
||||||
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||||
<if test="productId != null and productId != ''">AND p.`id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND k.`product_id` = #{productId}</if>
|
||||||
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%') OR k.`code` LIKE CONCAT('%', #{keyword}, '%') OR k.`name` LIKE CONCAT('%', #{keyword}, '%') OR k.`spec_summary` LIKE CONCAT('%', #{keyword}, '%') OR k.`barcode` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||||
</select>
|
</select>
|
||||||
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStock">
|
||||||
SELECT s.`id`,
|
SELECT s.`id`,
|
||||||
p.`store_id`,
|
k.`store_id`,
|
||||||
p.`id` AS `product_id`,
|
k.`id` AS `sku_id`,
|
||||||
COALESCE(s.`on_hand_quantity`, 0) AS `on_hand_quantity`,
|
COALESCE(s.`on_hand_quantity`, 0) AS `on_hand_quantity`,
|
||||||
COALESCE(s.`reserved_quantity`, 0) AS `reserved_quantity`,
|
COALESCE(s.`reserved_quantity`, 0) AS `reserved_quantity`,
|
||||||
s.`updated_by`,
|
s.`updated_by`,
|
||||||
s.`created_at`,
|
s.`created_at`,
|
||||||
s.`updated_at`
|
s.`updated_at`
|
||||||
FROM `gao_product` p
|
FROM `gao_product_sku` k
|
||||||
LEFT JOIN `gao_product_stock` s ON s.`product_id` = p.`id`
|
JOIN `gao_product` p ON p.`id` = k.`product_id`
|
||||||
|
LEFT JOIN `gao_product_stock` s ON s.`sku_id` = k.`id`
|
||||||
<choose>
|
<choose>
|
||||||
<when test="storeId != null and storeId != ''">AND s.`store_id` = #{storeId}</when>
|
<when test="storeId != null and storeId != ''">AND s.`store_id` = #{storeId}</when>
|
||||||
<otherwise>AND s.`store_id` = p.`store_id`</otherwise>
|
<otherwise>AND s.`store_id` = k.`store_id`</otherwise>
|
||||||
</choose>
|
</choose>
|
||||||
WHERE p.`deleted_at` IS NULL
|
WHERE p.`deleted_at` IS NULL AND k.`deleted_at` IS NULL
|
||||||
|
AND k.`stock_enabled` = 1 AND k.`status` = 'ACTIVE'
|
||||||
|
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||||
|
<if test="productId != null and productId != ''">AND k.`product_id` = #{productId}</if>
|
||||||
|
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%') OR k.`code` LIKE CONCAT('%', #{keyword}, '%') OR k.`name` LIKE CONCAT('%', #{keyword}, '%') OR k.`spec_summary` LIKE CONCAT('%', #{keyword}, '%') OR k.`barcode` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||||
|
ORDER BY p.`sort`, p.`name`, p.`code`, k.`sort`, k.`name`, k.`code`
|
||||||
|
LIMIT #{offset}, #{limit}
|
||||||
|
</select>
|
||||||
|
<select id="countProductByQuery" resultType="long">
|
||||||
|
SELECT COUNT(DISTINCT p.`id`)
|
||||||
|
FROM `gao_product_sku` k
|
||||||
|
JOIN `gao_product` p ON p.`id` = k.`product_id`
|
||||||
|
WHERE p.`deleted_at` IS NULL AND k.`deleted_at` IS NULL
|
||||||
AND p.`stock_enabled` = 1
|
AND p.`stock_enabled` = 1
|
||||||
|
AND k.`stock_enabled` = 1 AND k.`status` = 'ACTIVE'
|
||||||
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||||
<if test="productId != null and productId != ''">AND p.`id` = #{productId}</if>
|
|
||||||
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||||
|
</select>
|
||||||
|
<select id="selectProductByQuery" resultType="com.imyeyu.api.modules.gao.vo.GaoProductStockProductView">
|
||||||
|
SELECT p.`id` AS `product_id`,
|
||||||
|
p.`code` AS `product_code`,
|
||||||
|
p.`name` AS `product_name`,
|
||||||
|
p.`product_type`,
|
||||||
|
COUNT(k.`id`) AS `sku_count`,
|
||||||
|
COALESCE(SUM(COALESCE(s.`on_hand_quantity`, 0)), 0) AS `on_hand_quantity`,
|
||||||
|
COALESCE(SUM(COALESCE(s.`reserved_quantity`, 0)), 0) AS `reserved_quantity`,
|
||||||
|
COALESCE(SUM(COALESCE(s.`on_hand_quantity`, 0) - COALESCE(s.`reserved_quantity`, 0)), 0) AS `available_quantity`
|
||||||
|
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`
|
||||||
|
<choose>
|
||||||
|
<when test="storeId != null and storeId != ''">AND s.`store_id` = #{storeId}</when>
|
||||||
|
<otherwise>AND s.`store_id` = k.`store_id`</otherwise>
|
||||||
|
</choose>
|
||||||
|
WHERE p.`deleted_at` IS NULL AND k.`deleted_at` IS NULL
|
||||||
|
AND p.`stock_enabled` = 1
|
||||||
|
AND k.`stock_enabled` = 1 AND k.`status` = 'ACTIVE'
|
||||||
|
<if test="storeId != null and storeId != ''">AND p.`store_id` = #{storeId}</if>
|
||||||
|
<if test="keyword != null and keyword.trim() != ''">AND (p.`code` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||||
|
GROUP BY p.`id`, p.`code`, p.`name`, p.`product_type`, p.`sort`
|
||||||
ORDER BY p.`sort`, p.`name`, p.`code`
|
ORDER BY p.`sort`, p.`name`, p.`code`
|
||||||
LIMIT #{offset}, #{limit}
|
LIMIT #{offset}, #{limit}
|
||||||
</select>
|
</select>
|
||||||
<insert id="insertIgnore">
|
<insert id="insertIgnore">
|
||||||
INSERT IGNORE INTO `gao_product_stock`
|
INSERT IGNORE INTO `gao_product_stock`
|
||||||
(`id`, `store_id`, `product_id`, `on_hand_quantity`, `reserved_quantity`, `updated_by`, `created_at`)
|
(`id`, `store_id`, `sku_id`, `on_hand_quantity`, `reserved_quantity`, `updated_by`, `created_at`)
|
||||||
VALUES
|
VALUES
|
||||||
(#{id}, #{storeId}, #{productId}, #{onHandQuantity}, #{reservedQuantity}, #{updatedBy}, #{createdAt})
|
(#{id}, #{storeId}, #{skuId}, #{onHandQuantity}, #{reservedQuantity}, #{updatedBy}, #{createdAt})
|
||||||
</insert>
|
</insert>
|
||||||
<update id="updateQuantity">
|
<update id="updateQuantity">
|
||||||
UPDATE `gao_product_stock`
|
UPDATE `gao_product_stock`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<select id="selectByRequestNo" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
<select id="selectByRequestNo" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
||||||
SELECT * FROM `gao_product_stock_record`
|
SELECT * FROM `gao_product_stock_record`
|
||||||
WHERE `store_id` = #{storeId} AND `request_no` = #{requestNo}
|
WHERE `store_id` = #{storeId} AND `request_no` = #{requestNo}
|
||||||
ORDER BY `product_id`, `id`
|
ORDER BY `sku_id`, `id`
|
||||||
</select>
|
</select>
|
||||||
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
<select id="selectForUpdate" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
||||||
SELECT * FROM `gao_product_stock_record`
|
SELECT * FROM `gao_product_stock_record`
|
||||||
@@ -21,16 +21,16 @@
|
|||||||
WHERE `biz_type` = #{bizType} AND `biz_id` = #{bizId}
|
WHERE `biz_type` = #{bizType} AND `biz_id` = #{bizId}
|
||||||
ORDER BY `occurred_at`, `id`
|
ORDER BY `occurred_at`, `id`
|
||||||
</select>
|
</select>
|
||||||
<select id="selectByBizProduct" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
<select id="selectByBizSku" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
||||||
SELECT * FROM `gao_product_stock_record`
|
SELECT * FROM `gao_product_stock_record`
|
||||||
WHERE `biz_type` = #{bizType} AND `biz_id` = #{bizId} AND `product_id` = #{productId} AND `operation_type` = #{operationType}
|
WHERE `biz_type` = #{bizType} AND `biz_id` = #{bizId} AND `sku_id` = #{skuId} AND `operation_type` = #{operationType}
|
||||||
ORDER BY `occurred_at`, `id`
|
ORDER BY `occurred_at`, `id`
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
<sql id="queryWhere">
|
<sql id="queryWhere">
|
||||||
1 = 1
|
1 = 1
|
||||||
<if test="storeId != null and storeId != ''">AND r.`store_id` = #{storeId}</if>
|
<if test="storeId != null and storeId != ''">AND r.`store_id` = #{storeId}</if>
|
||||||
<if test="productId != null and productId != ''">AND r.`product_id` = #{productId}</if>
|
<if test="productId != null and productId != ''">AND k.`product_id` = #{productId}</if>
|
||||||
<if test="keyword != null and keyword.trim() != ''">AND (r.`product_name_snapshot` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%') OR p.`code` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
<if test="keyword != null and keyword.trim() != ''">AND (r.`product_name_snapshot` LIKE CONCAT('%', #{keyword}, '%') OR p.`name` LIKE CONCAT('%', #{keyword}, '%') OR p.`code` LIKE CONCAT('%', #{keyword}, '%'))</if>
|
||||||
<if test="bizType != null">AND r.`biz_type` = #{bizType}</if>
|
<if test="bizType != null">AND r.`biz_type` = #{bizType}</if>
|
||||||
<if test="bizId != null and bizId != ''">AND r.`biz_id` = #{bizId}</if>
|
<if test="bizId != null and bizId != ''">AND r.`biz_id` = #{bizId}</if>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
WHERE c.`store_id` = r.`store_id`
|
WHERE c.`store_id` = r.`store_id`
|
||||||
AND c.`biz_type` = r.`biz_type`
|
AND c.`biz_type` = r.`biz_type`
|
||||||
AND c.`biz_id` = r.`biz_id`
|
AND c.`biz_id` = r.`biz_id`
|
||||||
AND c.`product_id` = r.`product_id`
|
AND c.`sku_id` = r.`sku_id`
|
||||||
AND c.`operation_type` IN ('ORDER_RELEASE', 'ORDER_OUT')
|
AND c.`operation_type` IN ('ORDER_RELEASE', 'ORDER_OUT')
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
@@ -53,10 +53,10 @@
|
|||||||
<if test="endAt != null">AND r.`occurred_at` <= #{endAt}</if>
|
<if test="endAt != null">AND r.`occurred_at` <= #{endAt}</if>
|
||||||
</sql>
|
</sql>
|
||||||
<select id="countByQuery" resultType="long">
|
<select id="countByQuery" resultType="long">
|
||||||
SELECT COUNT(1) FROM `gao_product_stock_record` r LEFT JOIN `gao_product` p ON p.`id` = r.`product_id` AND p.`store_id` = r.`store_id` WHERE <include refid="queryWhere"/>
|
SELECT COUNT(1) FROM `gao_product_stock_record` r LEFT JOIN `gao_product_sku` k ON k.`id` = r.`sku_id` AND k.`store_id` = r.`store_id` LEFT JOIN `gao_product` p ON p.`id` = k.`product_id` WHERE <include refid="queryWhere"/>
|
||||||
</select>
|
</select>
|
||||||
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
<select id="selectByQuery" resultType="com.imyeyu.api.modules.gao.entity.GaoProductStockRecord">
|
||||||
SELECT r.* FROM `gao_product_stock_record` r LEFT JOIN `gao_product` p ON p.`id` = r.`product_id` AND p.`store_id` = r.`store_id` WHERE <include refid="queryWhere"/>
|
SELECT r.* FROM `gao_product_stock_record` r LEFT JOIN `gao_product_sku` k ON k.`id` = r.`sku_id` AND k.`store_id` = r.`store_id` LEFT JOIN `gao_product` p ON p.`id` = k.`product_id` WHERE <include refid="queryWhere"/>
|
||||||
ORDER BY r.`occurred_at` DESC, r.`id` DESC
|
ORDER BY r.`occurred_at` DESC, r.`id` DESC
|
||||||
LIMIT #{offset}, #{limit}
|
LIMIT #{offset}, #{limit}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?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.imyeyu.api.modules.gao.mapper.GaoProductUnitMapper">
|
|
||||||
<resultMap id="unitResultMap" type="com.imyeyu.api.modules.gao.entity.GaoProductUnit">
|
|
||||||
<id column="id" property="id"/>
|
|
||||||
<result column="product_id" property="productId"/>
|
|
||||||
<result column="name" property="name"/>
|
|
||||||
<result column="base_quantity_per_unit" property="baseQuantityPerUnit"/>
|
|
||||||
<result column="sale_price" property="salePrice"/>
|
|
||||||
<result column="is_base" property="isBase"/>
|
|
||||||
<result column="allow_purchase" property="allowPurchase"/>
|
|
||||||
<result column="allow_sale" property="allowSale"/>
|
|
||||||
<result column="sort" property="sort"/>
|
|
||||||
<result column="created_at" property="createdAt"/>
|
|
||||||
<result column="updated_at" property="updatedAt"/>
|
|
||||||
<result column="deleted_at" property="deletedAt"/>
|
|
||||||
</resultMap>
|
|
||||||
<select id="selectByProductId" resultMap="unitResultMap">
|
|
||||||
SELECT * FROM `gao_product_unit`
|
|
||||||
WHERE `product_id` = #{productId} AND `deleted_at` IS NULL
|
|
||||||
ORDER BY `sort`, `created_at`
|
|
||||||
</select>
|
|
||||||
<select id="selectByProductIdList" resultMap="unitResultMap">
|
|
||||||
SELECT * FROM `gao_product_unit`
|
|
||||||
WHERE `deleted_at` IS NULL
|
|
||||||
<choose>
|
|
||||||
<when test="productIdList != null and !productIdList.isEmpty()">
|
|
||||||
AND `product_id` IN <foreach collection="productIdList" item="id" open="(" separator="," close=")">#{id}</foreach>
|
|
||||||
</when>
|
|
||||||
<otherwise>AND FALSE</otherwise>
|
|
||||||
</choose>
|
|
||||||
ORDER BY `product_id`, `sort`, `created_at`
|
|
||||||
</select>
|
|
||||||
<select id="selectForUpdate" resultMap="unitResultMap">
|
|
||||||
SELECT * FROM `gao_product_unit`
|
|
||||||
WHERE `id` = #{id} AND `deleted_at` IS NULL
|
|
||||||
FOR UPDATE
|
|
||||||
</select>
|
|
||||||
<update id="updateUnit">
|
|
||||||
UPDATE `gao_product_unit`
|
|
||||||
SET `name` = #{name}, `base_quantity_per_unit` = #{baseQuantityPerUnit}, `sale_price` = #{salePrice}, `is_base` = #{isBase},
|
|
||||||
`allow_purchase` = #{allowPurchase}, `allow_sale` = #{allowSale}, `sort` = #{sort},
|
|
||||||
`updated_at` = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000)
|
|
||||||
WHERE `id` = #{id} AND `product_id` = #{productId} AND `deleted_at` IS NULL
|
|
||||||
</update>
|
|
||||||
</mapper>
|
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
<select id="selectCustomerProductRank" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView">
|
<select id="selectCustomerProductRank" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerOrderProductRankView">
|
||||||
SELECT
|
SELECT
|
||||||
i.`product_id` AS `productId`,
|
i.`product_id_snapshot` AS `productId`,
|
||||||
MAX(i.`product_name_snapshot`) AS `productName`,
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END), 0) AS `directQuantity`,
|
||||||
@@ -89,10 +89,15 @@
|
|||||||
COALESCE(SUM(i.`reserved_quantity`), 0) AS `reservedQuantity`,
|
COALESCE(SUM(i.`reserved_quantity`), 0) AS `reservedQuantity`,
|
||||||
COALESCE(SUM(i.`fulfilled_quantity`), 0) AS `fulfilledQuantity`,
|
COALESCE(SUM(i.`fulfilled_quantity`), 0) AS `fulfilledQuantity`,
|
||||||
COALESCE(SUM(i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`), 0) AS `availableQuantity`,
|
COALESCE(SUM(i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`), 0) AS `availableQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`reserved_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `reservedBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`fulfilled_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `fulfilledBaseQuantity`,
|
||||||
|
COALESCE(SUM((i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`) * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `availableBaseQuantity`,
|
||||||
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
FROM `gao_sales_order_item` i
|
FROM `gao_sales_order_item` i
|
||||||
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
INNER JOIN `gao_product` p ON p.`id` = i.`product_id` AND p.`store_id` = o.`store_id`
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
WHERE o.`store_id` = #{storeId}
|
WHERE o.`store_id` = #{storeId}
|
||||||
AND o.`customer_id` = #{customerId}
|
AND o.`customer_id` = #{customerId}
|
||||||
AND o.`deleted_at` IS NULL
|
AND o.`deleted_at` IS NULL
|
||||||
@@ -100,11 +105,45 @@
|
|||||||
AND o.`status` <> 'CANCELLED'
|
AND o.`status` <> 'CANCELLED'
|
||||||
AND o.`purchased_at` >= #{beginAt}
|
AND o.`purchased_at` >= #{beginAt}
|
||||||
AND o.`purchased_at` <= #{endAt}
|
AND o.`purchased_at` <= #{endAt}
|
||||||
GROUP BY i.`product_id`
|
<if test="productId != null and productId != ''">AND i.`product_id_snapshot` = #{productId}</if>
|
||||||
ORDER BY (SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` ELSE 0 END)
|
<if test="skuId != null and skuId != ''">AND i.`sku_id` = #{skuId}</if>
|
||||||
+ SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` ELSE 0 END)) DESC,
|
GROUP BY i.`product_id_snapshot`
|
||||||
|
ORDER BY (SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)
|
||||||
|
+ SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END)) DESC,
|
||||||
directSaleAmount DESC,
|
directSaleAmount DESC,
|
||||||
i.`product_id` ASC
|
i.`product_id_snapshot` ASC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectCustomerSkuRank" resultType="com.imyeyu.api.modules.gao.vo.GaoCustomerOrderSkuRankView">
|
||||||
|
SELECT
|
||||||
|
i.`sku_id` AS `skuId`,
|
||||||
|
MAX(i.`sku_code_snapshot`) AS `skuCode`,
|
||||||
|
MAX(i.`sku_name_snapshot`) AS `skuName`,
|
||||||
|
MAX(i.`spec_summary_snapshot`) AS `specSummary`,
|
||||||
|
i.`product_id_snapshot` AS `productId`,
|
||||||
|
MAX(i.`product_name_snapshot`) AS `productName`,
|
||||||
|
COUNT(DISTINCT o.`id`) AS `orderCount`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `directBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'PACKAGE_ENTITLEMENT' THEN i.`quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1) ELSE 0 END), 0) AS `packageBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`reserved_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `reservedBaseQuantity`,
|
||||||
|
COALESCE(SUM(i.`fulfilled_quantity` * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `fulfilledBaseQuantity`,
|
||||||
|
COALESCE(SUM((i.`quantity` - i.`reserved_quantity` - i.`fulfilled_quantity`) * COALESCE(i.`base_quantity_per_unit_snapshot`, 1)), 0) AS `availableBaseQuantity`,
|
||||||
|
COALESCE(SUM(CASE WHEN i.`item_type` = 'DIRECT_PRODUCT' THEN COALESCE(i.`line_amount`, 0) ELSE 0 END), 0) AS `directSaleAmount`
|
||||||
|
FROM `gao_sales_order_item` i
|
||||||
|
INNER JOIN `gao_sales_order` o ON o.`id` = i.`sales_order_id`
|
||||||
|
INNER JOIN `gao_product` p ON p.`id` = i.`product_id_snapshot` AND p.`store_id` = o.`store_id`
|
||||||
|
WHERE o.`store_id` = #{storeId}
|
||||||
|
AND o.`customer_id` = #{customerId}
|
||||||
|
AND o.`deleted_at` IS NULL
|
||||||
|
AND i.`deleted_at` IS NULL
|
||||||
|
AND o.`status` <> 'CANCELLED'
|
||||||
|
AND o.`purchased_at` >= #{beginAt}
|
||||||
|
AND o.`purchased_at` <= #{endAt}
|
||||||
|
<if test="productId != null and productId != ''">AND i.`product_id_snapshot` = #{productId}</if>
|
||||||
|
<if test="skuId != null and skuId != ''">AND i.`sku_id` = #{skuId}</if>
|
||||||
|
GROUP BY i.`sku_id`, i.`product_id_snapshot`
|
||||||
|
ORDER BY `availableBaseQuantity` DESC, `fulfilledBaseQuantity` DESC, `directSaleAmount` DESC, i.`sku_id` ASC
|
||||||
LIMIT #{topN}
|
LIMIT #{topN}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user