From 37c866f665fc1d2255f0442f92516c686c6c9edf Mon Sep 17 00:00:00 2001
From: kong <sunuping1@sina.com>
Date: 星期日, 12 四月 2026 16:55:52 +0800
Subject: [PATCH] add 优惠券切换

---
 xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpAppUserCouponServiceImpl.java |  148 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 148 insertions(+), 0 deletions(-)

diff --git a/xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpAppUserCouponServiceImpl.java b/xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpAppUserCouponServiceImpl.java
index 2332280..220a92c 100644
--- a/xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpAppUserCouponServiceImpl.java
+++ b/xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/XpAppUserCouponServiceImpl.java
@@ -15,7 +15,11 @@
 import com.xiaopeng.account.model.vo.XpAppUserCouponVo;
 import com.xiaopeng.account.service.IXpAppUserCouponService;
 import com.xiaopeng.account.util.holiday.HolidayUtil;
+import com.xiaopeng.api.dto.account.NavigationCouponPickerRequest;
 import com.xiaopeng.api.dto.account.UcUserInfo;
+import com.xiaopeng.api.enums.NavigationCouponBubbleType;
+import com.xiaopeng.api.vo.account.NavigationCouponItemVo;
+import com.xiaopeng.api.vo.account.NavigationCouponPickerVo;
 import com.xiaopeng.api.feignClient.account.UserResourcesClient;
 import com.xiaopeng.api.feignClient.operation.XpCouponClient;
 import com.xiaopeng.api.feignClient.operation.XpWhiteListClient;
@@ -27,9 +31,13 @@
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -225,4 +233,144 @@
         return false;
     }
 
+    @Override
+    public NavigationCouponPickerVo navigationCouponPicker(Long userId, NavigationCouponPickerRequest req) {
+        NavigationCouponPickerVo vo = new NavigationCouponPickerVo();
+        double orderAmount = req.getOrderAmount() == null ? 0d : req.getOrderAmount();
+        List<XpAppUserCoupon> couponList = baseMapper.selectList(new LambdaQueryWrapper<XpAppUserCoupon>()
+                .eq(XpAppUserCoupon::getStatus, 1)
+                .eq(XpAppUserCoupon::getAppUserId, userId)
+                .apply("(FIND_IN_SET({0}, open_city_id) or open_city_id='0')", req.getOpenCityId()));
+        couponList = timeRule(couponList);
+
+        Long bestId = null;
+        double bestEff = -1d;
+        LocalDateTime bestExp = null;
+        for (XpAppUserCoupon c : couponList) {
+            if (!couponAmountAvailable(c, orderAmount)) {
+                continue;
+            }
+            double eff = effectiveDiscountAmount(c, orderAmount);
+            if (eff <= 0) {
+                continue;
+            }
+            LocalDateTime exp = c.getExpirationTime();
+            if (bestId == null || eff > bestEff + 1e-6
+                    || (Math.abs(eff - bestEff) < 1e-6 && exp != null && bestExp != null && exp.isBefore(bestExp))
+                    || (Math.abs(eff - bestEff) < 1e-6 && bestExp == null && exp != null)) {
+                bestId = c.getId();
+                bestEff = eff;
+                bestExp = exp;
+            }
+        }
+        vo.setRecommendedUserCouponId(bestId);
+
+        Long selected = req.getSelectedUserCouponId();
+        Long applied;
+        if (selected != null && selected == 0L) {
+            applied = null;
+        } else if (selected != null && selected > 0L) {
+            XpAppUserCoupon chosen = couponList.stream().filter(c -> selected.equals(c.getId())).findFirst().orElse(null);
+            if (chosen != null && couponAmountAvailable(chosen, orderAmount)) {
+                applied = selected;
+            } else {
+                applied = null;
+            }
+        } else {
+            applied = bestId;
+        }
+        vo.setAppliedUserCouponId(applied);
+
+        long availCount = couponList.stream().filter(c -> couponAmountAvailable(c, orderAmount)).count();
+        if (availCount == 0) {
+            vo.setBubbleType(NavigationCouponBubbleType.HIDDEN);
+            List<NavigationCouponItemVo> hiddenItems = buildNavigationItems(couponList, orderAmount, bestId, applied);
+            vo.setItems(hiddenItems);
+            vo.setListTotalCount(hiddenItems.size());
+            return vo;
+        }
+        if (applied == null) {
+            vo.setBubbleType(NavigationCouponBubbleType.MULTI_AVAILABLE);
+            vo.setAvailableCouponCount((int) availCount);
+        } else {
+            vo.setBubbleType(NavigationCouponBubbleType.SELECTED);
+            XpAppUserCoupon ap = couponList.stream().filter(c -> applied.equals(c.getId())).findFirst().orElse(null);
+            vo.setSelectedDiscountAmount(ap != null ? effectiveDiscountAmount(ap, orderAmount) : 0d);
+        }
+        List<NavigationCouponItemVo> items = buildNavigationItems(couponList, orderAmount, bestId, applied);
+        vo.setItems(items);
+        vo.setListTotalCount(items.size());
+        return vo;
+    }
+
+    private List<NavigationCouponItemVo> buildNavigationItems(List<XpAppUserCoupon> couponList, double orderAmount,
+            Long bestId, Long appliedId) {
+        List<NavigationCouponItemVo> raw = new ArrayList<>();
+        for (XpAppUserCoupon c : couponList) {
+            NavigationCouponItemVo it = new NavigationCouponItemVo();
+            it.setUserCouponId(c.getId());
+            it.setCouponName(c.getCouponName());
+            it.setCouponType(c.getCouponType());
+            it.setDiscountMoney(c.getMoney() != null ? c.getMoney().doubleValue() : 0d);
+            it.setFullMoney(c.getFullMoney() != null ? c.getFullMoney().doubleValue() : null);
+            it.setExpireTime(c.getExpirationTime());
+            boolean ok = couponAmountAvailable(c, orderAmount);
+            it.setAvailable(ok);
+            if (c.getCouponType() != null && c.getCouponType() == 2) {
+                it.setThresholdLabel("满" + moneyStr(c.getFullMoney()) + "元可用");
+                if (!ok) {
+                    it.setUnavailableReason("未满足使用门槛");
+                }
+            } else {
+                it.setThresholdLabel("无门槛");
+            }
+            it.setBestOffer(bestId != null && bestId.equals(c.getId()));
+            it.setSelected(appliedId != null && appliedId.equals(c.getId()));
+            raw.add(it);
+        }
+        List<NavigationCouponItemVo> available = raw.stream().filter(x -> Boolean.TRUE.equals(x.getAvailable()))
+                .sorted(Comparator
+                        .comparing((NavigationCouponItemVo x) -> !Boolean.TRUE.equals(x.getBestOffer()))
+                        .thenComparing((NavigationCouponItemVo x) -> -effectiveDiscountAmount(findCoupon(couponList, x.getUserCouponId()), orderAmount))
+                        .thenComparing(NavigationCouponItemVo::getExpireTime, Comparator.nullsLast(Comparator.naturalOrder())))
+                .collect(Collectors.toList());
+        List<NavigationCouponItemVo> unavailable = raw.stream().filter(x -> !Boolean.TRUE.equals(x.getAvailable()))
+                .sorted(Comparator.comparing(NavigationCouponItemVo::getExpireTime, Comparator.nullsLast(Comparator.naturalOrder())))
+                .collect(Collectors.toList());
+        available.addAll(unavailable);
+        return available;
+    }
+
+    private static XpAppUserCoupon findCoupon(List<XpAppUserCoupon> list, Long id) {
+        return list.stream().filter(c -> Objects.equals(c.getId(), id)).findFirst().orElse(null);
+    }
+
+    private static String moneyStr(BigDecimal b) {
+        if (b == null) {
+            return "0";
+        }
+        return b.stripTrailingZeros().toPlainString();
+    }
+
+    private static boolean couponAmountAvailable(XpAppUserCoupon c, double orderAmount) {
+        if (c.getCouponType() == null || c.getCouponType() == 1) {
+            return true;
+        }
+        if (c.getCouponType() == 2) {
+            return c.getFullMoney() != null && orderAmount >= c.getFullMoney().doubleValue();
+        }
+        return true;
+    }
+
+    private static double effectiveDiscountAmount(XpAppUserCoupon c, double orderAmount) {
+        if (c == null || c.getMoney() == null) {
+            return 0d;
+        }
+        double m = c.getMoney().doubleValue();
+        if (!couponAmountAvailable(c, orderAmount)) {
+            return 0d;
+        }
+        return Math.min(orderAmount, m);
+    }
+
 }

--
Gitblit v1.7.1