From a2d69fe7212839d985af67e92189f77b9cd2a311 Mon Sep 17 00:00:00 2001 From: Timi Date: Thu, 10 Sep 2026 01:31:06 +0800 Subject: [PATCH] support gift --- .../modules/gao/bean/GaoPermissionCode.java | 2 + .../modules/gao/entity/GaoSalesOrderItem.java | 3 + .../GaoCustomerPackageServiceImplement.java | 76 +++++++++++++++---- .../GaoSalesOrderServiceImplement.java | 11 ++- .../vo/GaoCustomerPackagePurchaseItem.java | 3 + ...V75__support_gao_package_creation_gift.sql | 5 ++ 6 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 src/main/resources/db/migration/timiserver/V75__support_gao_package_creation_gift.sql diff --git a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java index 1028d86..b67202c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java +++ b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java @@ -111,6 +111,8 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true), + CUSTOMER_PACKAGE_GIFT_CREATE("CUSTOMER_PACKAGE:GIFT_CREATE", "客户套餐创建赠送", true, true), + FUND_RECORD_READ("FUND_RECORD:READ", "资金流水读取", true, true), FUND_RECEIVE_CREATE("FUND_RECEIVE:CREATE", "线下收款创建", true, true), diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java index 2235453..7d9521b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java @@ -34,6 +34,9 @@ public class GaoSalesOrderItem extends UUIDEntity { /// 订单项类型 private ItemType itemType; + /// true 为赠送权益 + private Boolean gifted; + /// SKU ID private String skuId; diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java index d695043..c6faeb0 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java @@ -4,6 +4,7 @@ import com.imyeyu.api.bean.ModuleCode; 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.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage; import com.imyeyu.api.modules.gao.entity.GaoPackageConfig; @@ -44,6 +45,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -151,10 +153,13 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService requestItemList = new ArrayList<>(request.getItemList()); + normalizePurchaseItems(requestItemList); verifyPurchaseItems(requestItemList); boolean canOverridePriceAndQuantity = permissionChecker.hasAny(ModuleCode.GAO, - com.imyeyu.api.modules.gao.bean.GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue()); - List itemList = resolvePurchaseItems(config.getStoreId(), requestItemList, configItemList, canOverridePriceAndQuantity); + GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue()); + boolean canCreateGift = permissionChecker.hasAny(ModuleCode.GAO, GaoPermissionCode.CUSTOMER_PACKAGE_GIFT_CREATE.getValue()); + List itemList = resolvePurchaseItems(config.getStoreId(), requestItemList, configItemList, + canOverridePriceAndQuantity, canCreateGift); itemList.sort(Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort).thenComparing(GaoCustomerPackagePurchaseItem::getSkuId) .thenComparing(GaoCustomerPackagePurchaseItem::getSkuUnitId)); request.setItemList(itemList); @@ -246,6 +251,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService itemList) { + Map indexMap = new LinkedHashMap<>(); + for (int i = 0; i < itemList.size(); i++) { + GaoCustomerPackagePurchaseItem item = itemList.get(i); + if (item == null) { + continue; + } + item.setGifted(Boolean.TRUE.equals(item.getGifted())); + if (item.getSkuId() == null || item.getSkuUnitId() == null || item.getTotalQuantity() == null) { + continue; + } + String key = item.getSkuId() + "|" + item.getSkuUnitId() + "|" + item.getGifted(); + Integer existingIndex = indexMap.putIfAbsent(key, i); + if (existingIndex == null) { + continue; + } + GaoCustomerPackagePurchaseItem existingItem = itemList.get(existingIndex); + if (existingItem == null || existingItem.getTotalQuantity() == null) { + continue; + } + existingItem.setTotalQuantity(existingItem.getTotalQuantity().add(item.getTotalQuantity())); + itemList.set(i, null); + } + itemList.removeIf(item -> item == null); + } + private void verifyPurchaseItems(List itemList) { Set keySet = new HashSet<>(); for (int i = 0; i < itemList.size(); i++) { @@ -294,14 +327,15 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService= item.getTotalQuantity().stripTrailingZeros().scale(), "购买产品数量最多保留三位小数"); - String key = item.getSkuId() + "|" + item.getSkuUnitId(); + item.setGifted(Boolean.TRUE.equals(item.getGifted())); + String key = item.getSkuId() + "|" + item.getSkuUnitId() + "|" + item.getGifted(); TimiException.requiredTrue(keySet.add(key), "购买套餐内 SKU 和单位不能重复"); item.setSort(TimiJava.defaultIfNull(item.getSort(), i)); } } private List resolvePurchaseItems(String storeId, List requestItemList, - List configItemList, boolean canOverridePriceAndQuantity) { + List configItemList, boolean canOverridePriceAndQuantity, boolean canCreateGift) { Map> configItemMap = configItemList.stream() .collect(Collectors.groupingBy(GaoPackageConfigItem::getSkuId)); Map configBaseQuantityMap = new HashMap<>(); @@ -312,23 +346,32 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService requestBaseQuantityMap = new HashMap<>(); List result = new ArrayList<>(); for (GaoCustomerPackagePurchaseItem requestItem : requestItemList) { + boolean gifted = Boolean.TRUE.equals(requestItem.getGifted()); List skuConfigItemList = configItemMap.get(requestItem.getSkuId()); - TimiException.requiredTrue(skuConfigItemList != null && !skuConfigItemList.isEmpty(), "购买 SKU 不属于套餐配置"); + if (gifted) { + TimiException.requiredTrue(canCreateGift, "没有客户套餐创建赠送权限"); + } else { + TimiException.requiredTrue(skuConfigItemList != null && !skuConfigItemList.isEmpty(), "购买 SKU 不属于套餐配置"); + } GaoProductSkuUnitSelection selection = skuService.getSellableSkuUnit(storeId, requestItem.getSkuId(), requestItem.getSkuUnitId()); - TimiException.requiredTrue(skuConfigItemList.stream().anyMatch(configItem -> 0 <= configItem.getBaseQuantityPerUnitSnapshot() - .compareTo(selection.getUnit().getBaseQuantityPerUnit())), "套餐权益单位不能高于配置单位"); + if (!gifted) { + TimiException.requiredTrue(skuConfigItemList.stream().anyMatch(configItem -> 0 <= configItem.getBaseQuantityPerUnitSnapshot() + .compareTo(selection.getUnit().getBaseQuantityPerUnit())), "套餐权益单位不能高于配置单位"); + } BigDecimal requestBaseQuantity = toBaseQuantity(requestItem.getTotalQuantity(), selection.getUnit().getBaseQuantityPerUnit(), "套餐权益基础数量最多保留三位小数"); - requestBaseQuantityMap.merge(requestItem.getSkuId(), requestBaseQuantity, BigDecimal::add); + if (!gifted) { + requestBaseQuantityMap.merge(requestItem.getSkuId(), requestBaseQuantity, BigDecimal::add); + } if (requestItem.getTotalQuantity().signum() == 0) { continue; } if (!Boolean.TRUE.equals(selection.getSku().getAllowDecimalQuantity())) { TimiException.requiredTrue(requestItem.getTotalQuantity().stripTrailingZeros().scale() <= 0, "该 SKU 不允许小数数量"); } - GaoPackageConfigItem template = resolveItemTemplate(skuConfigItemList, selection.getUnit().getId()); GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem(); item.setSkuId(selection.getSku().getId()); item.setSkuUnitId(selection.getUnit().getId()); + item.setGifted(gifted); item.setProductNameSnapshot(selection.getProduct().getName()); item.setProductIdSnapshot(selection.getProduct().getId()); item.setSkuCodeSnapshot(selection.getSku().getCode()); @@ -337,8 +380,14 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService requestItemList = TimiJava.defaultIfNull(request.getItemList(), List.of()); verifyPurchaseItems(requestItemList); TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应购买请求内容不一致"); - Map existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), Function.identity())); + Map existingItemMap = existingItemList.stream().collect(Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId() + "|" + + Boolean.TRUE.equals(item.getGifted()), Function.identity())); for (GaoCustomerPackagePurchaseItem item : requestItemList) { - GaoSalesOrderItem existingItem = existingItemMap.get(item.getSkuId() + "|" + item.getSkuUnitId()); + GaoSalesOrderItem existingItem = existingItemMap.get(item.getSkuId() + "|" + item.getSkuUnitId() + "|" + Boolean.TRUE.equals(item.getGifted())); TimiException.required(existingItem, "requestNo 对应购买请求内容不一致"); TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT && existingItem.getQuantity().compareTo(item.getTotalQuantity()) == 0 diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java index 468656c..ed77d79 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java @@ -453,9 +453,11 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService existingItemList = itemMapper.selectBySalesOrderId(existing.getId()); TimiException.requiredTrue(existingItemList.size() == requestItemList.size(), "requestNo 对应订单内容不一致"); Map existingItemMap = existingItemList.stream() - .collect(java.util.stream.Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId(), java.util.function.Function.identity())); + .collect(java.util.stream.Collectors.toMap(item -> item.getSkuId() + "|" + item.getSkuUnitId() + "|" + Boolean.TRUE.equals(item.getGifted()), + java.util.function.Function.identity())); for (GaoSalesOrderItem requestItem : requestItemList) { - GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getSkuId() + "|" + requestItem.getSkuUnitId()); + GaoSalesOrderItem existingItem = existingItemMap.get(requestItem.getSkuId() + "|" + requestItem.getSkuUnitId() + "|" + + Boolean.TRUE.equals(requestItem.getGifted())); TimiException.required(existingItem, "requestNo 对应订单内容不一致"); TimiException.requiredTrue(existingItem.getItemType() == GaoSalesOrderItem.ItemType.PACKAGE_ENTITLEMENT + && Boolean.TRUE.equals(existingItem.getGifted()) == Boolean.TRUE.equals(requestItem.getGifted()) && existingItem.getQuantity().compareTo(requestItem.getQuantity()) == 0 && existingItem.getSort().equals(requestItem.getSort()) && sameText(existingItem.getRemark(), requestItem.getRemark()), "requestNo 对应订单内容不一致"); diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerPackagePurchaseItem.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerPackagePurchaseItem.java index 9058943..5f1bf2d 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerPackagePurchaseItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoCustomerPackagePurchaseItem.java @@ -17,6 +17,9 @@ public class GaoCustomerPackagePurchaseItem { /// SKU 单位 ID private String skuUnitId; + /// true 表示请求将该明细作为赠送权益创建 + private Boolean gifted; + /// 产品名称快照,由后端生成 private String productNameSnapshot; diff --git a/src/main/resources/db/migration/timiserver/V75__support_gao_package_creation_gift.sql b/src/main/resources/db/migration/timiserver/V75__support_gao_package_creation_gift.sql new file mode 100644 index 0000000..e48451c --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V75__support_gao_package_creation_gift.sql @@ -0,0 +1,5 @@ +ALTER TABLE `gao_sales_order_item` + ADD COLUMN `gifted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'true 为赠送权益' AFTER `item_type`, + DROP KEY `uk_gao_sales_order_item_sku_unit`, + ADD UNIQUE KEY `uk_gao_sales_order_item_sku_unit_gifted` (`sales_order_id`, `sku_id`, `sku_unit_id`, `gifted`), + ADD KEY `idx_gao_sales_order_item_order_gifted` (`sales_order_id`, `gifted`, `sort`, `created_at`);