|
|
@@ -21,6 +21,13 @@ import com.ruoyi.system.domain.PromotionActivityRule;
|
|
|
import com.ruoyi.system.domain.PromotionCouponBatch;
|
|
|
import com.ruoyi.system.domain.PromotionCouponRule;
|
|
|
import com.ruoyi.system.domain.PromotionUserCoupon;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcRequest;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse.AvailableCoupon;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse.LineItem;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse.MatchedRule;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse.PathResult;
|
|
|
+import com.ruoyi.system.dto.PromotionCalcResponse.PromotionDetail;
|
|
|
import com.ruoyi.system.mapper.PosFoodMapper;
|
|
|
import com.ruoyi.system.mapper.PosOrderMapper;
|
|
|
import com.ruoyi.system.mapper.PromotionActivityMapper;
|
|
|
@@ -35,8 +42,6 @@ import com.ruoyi.system.service.IPromotionCalcService;
|
|
|
*
|
|
|
* 两路算价:Path A(折扣/第二份半价)vs Path B(满减),取最优路径。
|
|
|
* 叠加新客立减、优惠券后得出最终金额。
|
|
|
- *
|
|
|
- * @author ruoyi
|
|
|
*/
|
|
|
@Service
|
|
|
public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
@@ -65,10 +70,24 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
private PosOrderMapper posOrderMapper;
|
|
|
|
|
|
@Override
|
|
|
- public Map<String, Object> calculate(Long storeId, List<Map<String, Object>> items,
|
|
|
- Long userId, Long couponId, String forcePath)
|
|
|
+ public PromotionCalcResponse calculate(PromotionCalcRequest request, Long userId)
|
|
|
{
|
|
|
- Map<String, Object> result = new LinkedHashMap<>();
|
|
|
+ Long storeId = request.getStoreId();
|
|
|
+ Long couponId = request.getCouponId();
|
|
|
+ String forcePath = request.getForcePath();
|
|
|
+
|
|
|
+ // 将 DTO items 转为内部 Map 格式
|
|
|
+ List<Map<String, Object>> items = new ArrayList<>();
|
|
|
+ if (request.getItems() != null)
|
|
|
+ {
|
|
|
+ for (PromotionCalcRequest.CartItem ci : request.getItems())
|
|
|
+ {
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
+ m.put("productId", ci.getProductId());
|
|
|
+ m.put("quantity", ci.getQuantity());
|
|
|
+ items.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// ---- 1. 获取商品真实价格,计算 originalAmount ----
|
|
|
Map<Long, BigDecimal> priceMap = new HashMap<>();
|
|
|
@@ -88,7 +107,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
|
|
|
BigDecimal originalAmount = BigDecimal.ZERO;
|
|
|
- // itemsWithPrice: [{productId, quantity, unitPrice, name}]
|
|
|
List<Map<String, Object>> itemsWithPrice = new ArrayList<>();
|
|
|
for (Map<String, Object> item : items)
|
|
|
{
|
|
|
@@ -105,21 +123,17 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
itemsWithPrice.add(ip);
|
|
|
}
|
|
|
originalAmount = originalAmount.setScale(2, RoundingMode.HALF_UP);
|
|
|
- result.put("originalAmount", originalAmount);
|
|
|
|
|
|
// ---- 2. 查询门店生效的促销活动 ----
|
|
|
List<PromotionActivity> activeActivities = activityMapper.selectActiveByStoreId(storeId);
|
|
|
List<PromotionActivityRule> activeRules = activityRuleMapper.selectActiveRulesByStoreId(storeId);
|
|
|
|
|
|
- // 按活动类型分组
|
|
|
Map<Long, PromotionActivity> activityMap = activeActivities.stream()
|
|
|
.collect(Collectors.toMap(PromotionActivity::getId, a -> a, (a, b) -> a));
|
|
|
|
|
|
- // activityId -> rules
|
|
|
Map<Long, List<PromotionActivityRule>> rulesByActivity = activeRules.stream()
|
|
|
.collect(Collectors.groupingBy(PromotionActivityRule::getActivityId));
|
|
|
|
|
|
- // productId -> rules (for discount/half-price matching)
|
|
|
Map<Long, List<PromotionActivityRule>> rulesByProduct = activeRules.stream()
|
|
|
.filter(r -> r.getProductId() != null)
|
|
|
.collect(Collectors.groupingBy(PromotionActivityRule::getProductId));
|
|
|
@@ -127,12 +141,10 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
// ---- 3. PATH A: 折扣(type=2) / 第二份半价(type=3) ----
|
|
|
Map<String, Object> pathAResult = calculatePathA(itemsWithPrice, activeActivities,
|
|
|
rulesByActivity, rulesByProduct, activityMap);
|
|
|
- result.put("pathA", pathAResult);
|
|
|
|
|
|
// ---- 4. PATH B: 满减(type=1) ----
|
|
|
Map<String, Object> pathBResult = calculatePathB(itemsWithPrice, originalAmount,
|
|
|
activeActivities, rulesByActivity, activityMap);
|
|
|
- result.put("pathB", pathBResult);
|
|
|
|
|
|
// ---- 5. 比较两条路径 ----
|
|
|
BigDecimal subtotalA = (BigDecimal) pathAResult.get("subtotal");
|
|
|
@@ -147,7 +159,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
{
|
|
|
optimalPath = subtotalA.compareTo(subtotalB) < 0 ? "A" : "B";
|
|
|
}
|
|
|
- result.put("optimalPath", optimalPath);
|
|
|
|
|
|
BigDecimal afterPromotion = "A".equals(optimalPath) ? subtotalA : subtotalB;
|
|
|
BigDecimal promotionReduce = originalAmount.subtract(afterPromotion);
|
|
|
@@ -159,12 +170,11 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
LambdaQueryWrapper<PosOrder> orderWrapper = new LambdaQueryWrapper<>();
|
|
|
orderWrapper.eq(PosOrder::getUserId, userId)
|
|
|
.eq(PosOrder::getMdId, storeId)
|
|
|
- .eq(PosOrder::getState, 3L); // state=3 表示已完成
|
|
|
+ .eq(PosOrder::getState, 3L);
|
|
|
Long completedCount = posOrderMapper.selectCount(orderWrapper);
|
|
|
|
|
|
if (completedCount == 0)
|
|
|
{
|
|
|
- // 新客
|
|
|
for (PromotionActivity act : activeActivities)
|
|
|
{
|
|
|
if (act.getType() != null && act.getType() == 4)
|
|
|
@@ -183,16 +193,13 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if (newCustomerReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
- {
|
|
|
- result.put("newCustomerReduce", newCustomerReduce);
|
|
|
- }
|
|
|
|
|
|
// ---- 7. 优惠券 ----
|
|
|
BigDecimal couponReduce = BigDecimal.ZERO;
|
|
|
String couponName = null;
|
|
|
Boolean couponConflict = null;
|
|
|
String conflictNote = null;
|
|
|
+ PromotionCouponBatch batch = null;
|
|
|
|
|
|
if (couponId != null)
|
|
|
{
|
|
|
@@ -200,49 +207,40 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
if (userCoupon != null && userCoupon.getUserId().equals(userId)
|
|
|
&& userCoupon.getStatus() != null && userCoupon.getStatus() == 0)
|
|
|
{
|
|
|
- // 检查是否过期
|
|
|
if (userCoupon.getExpireTime() != null && userCoupon.getExpireTime().before(new Date()))
|
|
|
{
|
|
|
// 已过期,不处理
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- PromotionCouponBatch batch = couponBatchMapper.selectById(userCoupon.getBatchId());
|
|
|
+ batch = couponBatchMapper.selectById(userCoupon.getBatchId());
|
|
|
PromotionCouponRule couponRule = couponRuleMapper.selectRuleByBatchId(userCoupon.getBatchId());
|
|
|
|
|
|
if (batch != null && couponRule != null)
|
|
|
{
|
|
|
couponName = batch.getName();
|
|
|
-
|
|
|
int isMutex = couponRule.getIsMutex() != null ? couponRule.getIsMutex() : 0;
|
|
|
|
|
|
- // 互斥检查
|
|
|
if (isMutex == 1 && promotionReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
{
|
|
|
couponConflict = true;
|
|
|
conflictNote = "互斥券不可与满减/折扣叠加,选择此券将取消促销优惠";
|
|
|
- // 互斥券:以原价为基础计算优惠
|
|
|
couponReduce = calcCouponReduce(couponRule, batch, itemsWithPrice, originalAmount, priceMap);
|
|
|
- // 使用互斥券时,促销优惠取消,以原价减去券优惠
|
|
|
afterPromotion = originalAmount;
|
|
|
promotionReduce = BigDecimal.ZERO;
|
|
|
newCustomerReduce = BigDecimal.ZERO;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // 同享券:基于促销后金额判断门槛
|
|
|
BigDecimal baseForCoupon = afterPromotion.subtract(newCustomerReduce);
|
|
|
baseForCoupon = baseForCoupon.max(BigDecimal.ZERO);
|
|
|
|
|
|
- // 门槛检查
|
|
|
if (couponRule.getThreshold() != null
|
|
|
&& baseForCoupon.compareTo(couponRule.getThreshold()) >= 0)
|
|
|
{
|
|
|
- // 商品券(type=2)特殊检查:商品是否在折扣/半价活动中
|
|
|
if (batch.getCouponType() != null && batch.getCouponType() == 2
|
|
|
&& couponRule.getProductId() != null && "A".equals(optimalPath))
|
|
|
{
|
|
|
- // 检查商品券的商品是否在当前Path A的活动中
|
|
|
List<PromotionActivityRule> productRules = rulesByProduct.get(couponRule.getProductId());
|
|
|
if (productRules != null)
|
|
|
{
|
|
|
@@ -271,80 +269,199 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ---- 8. 最终金额 ----
|
|
|
+ BigDecimal finalAmount = afterPromotion.subtract(newCustomerReduce).subtract(couponReduce);
|
|
|
+ finalAmount = finalAmount.max(MIN_AMOUNT).setScale(2, RoundingMode.HALF_UP);
|
|
|
+
|
|
|
+ // ---- 9. 构建响应DTO ----
|
|
|
+ PromotionCalcResponse response = new PromotionCalcResponse();
|
|
|
+ response.setOriginalAmount(originalAmount);
|
|
|
+ response.setPathA(toPathResult(pathAResult));
|
|
|
+ response.setPathB(toPathResult(pathBResult));
|
|
|
+ response.setOptimalPath(optimalPath);
|
|
|
+ response.setFinalAmount(finalAmount);
|
|
|
+
|
|
|
+ if (newCustomerReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
+ {
|
|
|
+ response.setNewCustomerReduce(newCustomerReduce);
|
|
|
+ }
|
|
|
if (couponId != null)
|
|
|
{
|
|
|
- result.put("couponId", couponId);
|
|
|
+ response.setCouponId(couponId);
|
|
|
}
|
|
|
if (couponName != null)
|
|
|
{
|
|
|
- result.put("couponName", couponName);
|
|
|
+ response.setCouponName(couponName);
|
|
|
+ }
|
|
|
+ if (couponReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
+ {
|
|
|
+ response.setCouponReduce(couponReduce);
|
|
|
}
|
|
|
if (couponConflict != null)
|
|
|
{
|
|
|
- result.put("couponConflict", couponConflict);
|
|
|
+ response.setCouponConflict(couponConflict);
|
|
|
}
|
|
|
if (conflictNote != null)
|
|
|
{
|
|
|
- result.put("conflictNote", conflictNote);
|
|
|
+ response.setConflictNote(conflictNote);
|
|
|
}
|
|
|
- if (couponReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
+ // 设置优惠券批次ID(用于下单快照写入)
|
|
|
+ if (couponId != null && batch != null)
|
|
|
{
|
|
|
- result.put("couponReduce", couponReduce);
|
|
|
+ response.setCouponBatchId(batch.getId());
|
|
|
}
|
|
|
|
|
|
- // ---- 8. 最终金额 ----
|
|
|
- BigDecimal finalAmount = afterPromotion.subtract(newCustomerReduce).subtract(couponReduce);
|
|
|
- finalAmount = finalAmount.max(MIN_AMOUNT).setScale(2, RoundingMode.HALF_UP);
|
|
|
- result.put("finalAmount", finalAmount);
|
|
|
-
|
|
|
- // ---- 9. 明细列表 ----
|
|
|
- List<Map<String, Object>> details = new ArrayList<>();
|
|
|
+ // ---- 10. 明细列表 ----
|
|
|
+ List<PromotionDetail> details = new ArrayList<>();
|
|
|
if (promotionReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
{
|
|
|
- Map<String, Object> detail = new LinkedHashMap<>();
|
|
|
- detail.put("type", "promotion");
|
|
|
+ PromotionDetail detail = new PromotionDetail();
|
|
|
+ detail.setType("promotion");
|
|
|
if ("A".equals(optimalPath))
|
|
|
{
|
|
|
- detail.put("subType", pathAResult.get("appliedType"));
|
|
|
- detail.put("name", pathAResult.get("label"));
|
|
|
+ detail.setSubType((Integer) pathAResult.get("appliedType"));
|
|
|
+ detail.setName((String) pathAResult.get("label"));
|
|
|
+ // 找到对应类型的促销活动ID
|
|
|
+ Integer appliedType = (Integer) pathAResult.get("appliedType");
|
|
|
+ for (PromotionActivity act : activeActivities)
|
|
|
+ {
|
|
|
+ if (act.getType() != null && act.getType().equals(appliedType))
|
|
|
+ {
|
|
|
+ detail.setRefId(act.getId());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- detail.put("subType", 1);
|
|
|
- detail.put("name", pathBResult.get("label"));
|
|
|
+ detail.setSubType(1);
|
|
|
+ detail.setName((String) pathBResult.get("label"));
|
|
|
+ // 从满减匹配规则中获取活动ID
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> matchedRule = (Map<String, Object>) pathBResult.get("matchedRule");
|
|
|
+ if (matchedRule != null)
|
|
|
+ {
|
|
|
+ detail.setRefId(toLong(matchedRule.get("activityId")));
|
|
|
+ }
|
|
|
}
|
|
|
- detail.put("reduce", promotionReduce);
|
|
|
+ detail.setReduce(promotionReduce);
|
|
|
details.add(detail);
|
|
|
}
|
|
|
if (newCustomerReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
{
|
|
|
- Map<String, Object> detail = new LinkedHashMap<>();
|
|
|
- detail.put("type", "promotion");
|
|
|
- detail.put("subType", 4);
|
|
|
- detail.put("name", "新客立减");
|
|
|
- detail.put("reduce", newCustomerReduce);
|
|
|
+ PromotionDetail detail = new PromotionDetail();
|
|
|
+ detail.setType("promotion");
|
|
|
+ detail.setSubType(4);
|
|
|
+ detail.setName("新客立减");
|
|
|
+ detail.setReduce(newCustomerReduce);
|
|
|
+ // 找到新客立减活动ID
|
|
|
+ for (PromotionActivity act : activeActivities)
|
|
|
+ {
|
|
|
+ if (act.getType() != null && act.getType() == 4)
|
|
|
+ {
|
|
|
+ detail.setRefId(act.getId());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
details.add(detail);
|
|
|
}
|
|
|
if (couponReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
{
|
|
|
- Map<String, Object> detail = new LinkedHashMap<>();
|
|
|
- detail.put("type", "coupon");
|
|
|
- detail.put("subType", 0);
|
|
|
- detail.put("name", couponName);
|
|
|
- detail.put("reduce", couponReduce);
|
|
|
+ PromotionDetail detail = new PromotionDetail();
|
|
|
+ detail.setType("coupon");
|
|
|
+ detail.setSubType(0);
|
|
|
+ detail.setName(couponName);
|
|
|
+ detail.setReduce(couponReduce);
|
|
|
+ // 优惠券的refId设为券批次ID
|
|
|
+ if (batch != null)
|
|
|
+ {
|
|
|
+ detail.setRefId(batch.getId());
|
|
|
+ }
|
|
|
details.add(detail);
|
|
|
}
|
|
|
- result.put("details", details);
|
|
|
+ response.setDetails(details);
|
|
|
|
|
|
- // ---- 10. 可用优惠券列表 ----
|
|
|
- List<Map<String, Object>> availableCoupons = buildAvailableCoupons(storeId, userId,
|
|
|
+ // ---- 11. 可用优惠券列表 ----
|
|
|
+ List<Map<String, Object>> availableCouponMaps = buildAvailableCoupons(storeId, userId,
|
|
|
originalAmount, afterPromotion, promotionReduce, itemsWithPrice,
|
|
|
rulesByProduct, activityMap);
|
|
|
- result.put("availableCoupons", availableCoupons);
|
|
|
+ response.setAvailableCoupons(availableCouponMaps.stream()
|
|
|
+ .map(this::toAvailableCoupon)
|
|
|
+ .collect(Collectors.toList()));
|
|
|
|
|
|
- return result;
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== DTO转换方法 ====================
|
|
|
+
|
|
|
+ private PathResult toPathResult(Map<String, Object> pathMap)
|
|
|
+ {
|
|
|
+ PathResult pr = new PathResult();
|
|
|
+ pr.setLabel((String) pathMap.get("label"));
|
|
|
+ pr.setSubtotal((BigDecimal) pathMap.get("subtotal"));
|
|
|
+ if (pathMap.containsKey("appliedType"))
|
|
|
+ {
|
|
|
+ pr.setAppliedType((Integer) pathMap.get("appliedType"));
|
|
|
+ }
|
|
|
+ if (pathMap.containsKey("promotionReduce"))
|
|
|
+ {
|
|
|
+ pr.setPromotionReduce((BigDecimal) pathMap.get("promotionReduce"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> itemList = (List<Map<String, Object>>) pathMap.get("items");
|
|
|
+ if (itemList != null)
|
|
|
+ {
|
|
|
+ pr.setItems(itemList.stream().map(this::toLineItem).collect(Collectors.toList()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> matchedRuleMap = (Map<String, Object>) pathMap.get("matchedRule");
|
|
|
+ if (matchedRuleMap != null)
|
|
|
+ {
|
|
|
+ MatchedRule mr = new MatchedRule();
|
|
|
+ mr.setId(toLong(matchedRuleMap.get("id")));
|
|
|
+ mr.setActivityId(toLong(matchedRuleMap.get("activityId")));
|
|
|
+ mr.setActivityName((String) matchedRuleMap.get("activityName"));
|
|
|
+ mr.setThreshold((BigDecimal) matchedRuleMap.get("threshold"));
|
|
|
+ mr.setReduceAmount((BigDecimal) matchedRuleMap.get("reduceAmount"));
|
|
|
+ pr.setMatchedRule(mr);
|
|
|
+ }
|
|
|
+
|
|
|
+ return pr;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LineItem toLineItem(Map<String, Object> itemMap)
|
|
|
+ {
|
|
|
+ LineItem li = new LineItem();
|
|
|
+ li.setProductId(toLong(itemMap.get("productId")));
|
|
|
+ li.setQuantity(toInt(itemMap.get("quantity")));
|
|
|
+ li.setUnitPrice((BigDecimal) itemMap.get("unitPrice"));
|
|
|
+ li.setName((String) itemMap.get("name"));
|
|
|
+ li.setOriginalLineTotal((BigDecimal) itemMap.get("originalLineTotal"));
|
|
|
+ li.setFinalLineTotal((BigDecimal) itemMap.get("finalLineTotal"));
|
|
|
+ li.setDiscountRate((BigDecimal) itemMap.get("discountRate"));
|
|
|
+ li.setHalfPriceApplied((Boolean) itemMap.get("halfPriceApplied"));
|
|
|
+ return li;
|
|
|
}
|
|
|
|
|
|
+ private AvailableCoupon toAvailableCoupon(Map<String, Object> couponMap)
|
|
|
+ {
|
|
|
+ AvailableCoupon ac = new AvailableCoupon();
|
|
|
+ ac.setId(toLong(couponMap.get("id")));
|
|
|
+ ac.setName((String) couponMap.get("name"));
|
|
|
+ ac.setCouponType((Integer) couponMap.get("couponType"));
|
|
|
+ ac.setIsMutex((Integer) couponMap.get("isMutex"));
|
|
|
+ ac.setThreshold((BigDecimal) couponMap.get("threshold"));
|
|
|
+ ac.setAmount((BigDecimal) couponMap.get("amount"));
|
|
|
+ ac.setDiscountRate((BigDecimal) couponMap.get("discountRate"));
|
|
|
+ ac.setUsable((Boolean) couponMap.get("usable"));
|
|
|
+ ac.setConflictWithPromotion((Boolean) couponMap.get("conflictWithPromotion"));
|
|
|
+ return ac;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 内部计算方法(保持Map) ====================
|
|
|
+
|
|
|
/**
|
|
|
* PATH A: 折扣(type=2) vs 第二份半价(type=3),取更优
|
|
|
*/
|
|
|
@@ -358,7 +475,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
pathResult.put("label", "折扣");
|
|
|
pathResult.put("appliedType", 2);
|
|
|
|
|
|
- // 分别计算折扣和第二份半价
|
|
|
Map<String, Object> discountResult = calcDiscount(itemsWithPrice, rulesByProduct, activityMap);
|
|
|
Map<String, Object> halfPriceResult = calcHalfPrice(itemsWithPrice, rulesByProduct, activityMap);
|
|
|
|
|
|
@@ -367,7 +483,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
|
|
|
if (halfPriceSubtotal.compareTo(discountSubtotal) < 0)
|
|
|
{
|
|
|
- // 第二份半价更优
|
|
|
pathResult.put("label", "第二份半价");
|
|
|
pathResult.put("appliedType", 3);
|
|
|
pathResult.put("items", halfPriceResult.get("items"));
|
|
|
@@ -379,7 +494,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // 折扣更优或相等
|
|
|
pathResult.put("items", discountResult.get("items"));
|
|
|
pathResult.put("subtotal", discountSubtotal);
|
|
|
pathResult.put("promotionReduce",
|
|
|
@@ -412,7 +526,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
BigDecimal lineOriginal = unitPrice.multiply(BigDecimal.valueOf(quantity));
|
|
|
originalRef = originalRef.add(lineOriginal);
|
|
|
|
|
|
- // 查找此商品是否有折扣规则
|
|
|
BigDecimal finalPrice = lineOriginal;
|
|
|
PromotionActivityRule matchedRule = null;
|
|
|
|
|
|
@@ -424,7 +537,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
PromotionActivity act = activityMap.get(rule.getActivityId());
|
|
|
if (act != null && act.getType() == 2 && rule.getDiscountRate() != null)
|
|
|
{
|
|
|
- // 满足最低数量
|
|
|
if (rule.getMinQuantity() == null || quantity >= rule.getMinQuantity())
|
|
|
{
|
|
|
BigDecimal discounted = unitPrice.multiply(rule.getDiscountRate())
|
|
|
@@ -495,7 +607,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
{
|
|
|
if (rule.getMinQuantity() == null || quantity >= rule.getMinQuantity())
|
|
|
{
|
|
|
- // 每2件中第2件半价: pairs * 1.5 * unitPrice + remainder * unitPrice
|
|
|
int pairs = quantity / 2;
|
|
|
int remainder = quantity % 2;
|
|
|
BigDecimal halfPriceTotal = unitPrice.multiply(BigDecimal.valueOf(pairs))
|
|
|
@@ -562,7 +673,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
pathResult.put("items", itemList);
|
|
|
|
|
|
- // 查找满减活动
|
|
|
PromotionActivityRule bestMatch = null;
|
|
|
PromotionActivity bestActivity = null;
|
|
|
|
|
|
@@ -624,11 +734,9 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
{
|
|
|
if (batch.getCouponType() != null && batch.getCouponType() == 2)
|
|
|
{
|
|
|
- // 商品券: 基于商品原价和折扣率
|
|
|
if (couponRule.getProductId() != null)
|
|
|
{
|
|
|
BigDecimal productPrice = priceMap.getOrDefault(couponRule.getProductId(), BigDecimal.ZERO);
|
|
|
- // 找到此商品在购物车中的数量
|
|
|
int quantity = 0;
|
|
|
for (Map<String, Object> item : itemsWithPrice)
|
|
|
{
|
|
|
@@ -655,7 +763,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // 满减券: 直接减 amount
|
|
|
if (couponRule.getAmount() != null)
|
|
|
{
|
|
|
return couponRule.getAmount().min(baseAmount).setScale(2, RoundingMode.HALF_UP);
|
|
|
@@ -681,7 +788,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
return available;
|
|
|
}
|
|
|
|
|
|
- // 查询用户未使用、未过期的优惠券
|
|
|
LambdaQueryWrapper<PromotionUserCoupon> wrapper = new LambdaQueryWrapper<>();
|
|
|
wrapper.eq(PromotionUserCoupon::getUserId, userId)
|
|
|
.eq(PromotionUserCoupon::getStoreId, storeId)
|
|
|
@@ -711,17 +817,14 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
couponInfo.put("amount", rule.getAmount());
|
|
|
couponInfo.put("discountRate", rule.getDiscountRate());
|
|
|
|
|
|
- // 判断是否可用
|
|
|
boolean usable = true;
|
|
|
int isMutex = rule.getIsMutex() != null ? rule.getIsMutex() : 0;
|
|
|
|
|
|
if (isMutex == 1 && promotionReduce.compareTo(BigDecimal.ZERO) > 0)
|
|
|
{
|
|
|
- // 互斥券且当前有促销优惠 → 仍返回但标记
|
|
|
couponInfo.put("conflictWithPromotion", true);
|
|
|
}
|
|
|
|
|
|
- // 门槛检查
|
|
|
if (rule.getThreshold() != null)
|
|
|
{
|
|
|
BigDecimal baseForCheck = isMutex == 1 ? originalAmount : afterPromotion;
|
|
|
@@ -731,7 +834,6 @@ public class PromotionCalcServiceImpl implements IPromotionCalcService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 商品券: 检查商品是否在购物车中
|
|
|
if (batch.getCouponType() != null && batch.getCouponType() == 2 && rule.getProductId() != null)
|
|
|
{
|
|
|
boolean inCart = itemsWithPrice.stream()
|