Pārlūkot izejas kodu

验证套餐价计算逻辑

jintao.geng 1 mēnesi atpakaļ
vecāks
revīzija
f4123cbe9d

+ 1 - 1
mango-application/src/main/java/com/mangoo/rating/recommend/app/controller/RecommendController.java

@@ -32,7 +32,7 @@ public class RecommendController {
     @ApiOperation("批量推荐评级时效并按时效分单")
     @PostMapping("/efficiency")
     @ApiLog(title = "评级时效推荐", businessType = BusinessType.SEARCH)
-    public AjaxResult recommendEfficiency(@Validated @RequestBody RecommendRequest request) {
+    public AjaxResult recommendEfficiency(@RequestBody RecommendRequest request) {
         return recommendService.recommendEfficiency(request);
     }
 }

+ 1 - 1
mango-application/src/main/java/com/mangoo/rating/recommend/app/controller/RuleSyncController.java

@@ -40,7 +40,7 @@ public class RuleSyncController {
     @PostMapping("/sync")
     @ApiLog(title = "数仓规则同步", businessType = BusinessType.IMPORT)
     public AjaxResult sync(@RequestHeader(value = "X-Sync-Token", required = false) String token,
-                           @Validated @RequestBody RuleSyncRequest request) {
+                   @RequestBody RuleSyncRequest request) {
         // Token 鉴权:防止内部同步接口被外部乱调写脏数据
         String expected = recommendProperties.getSyncToken();
         if (expected == null || !expected.equals(token)) {

+ 51 - 2
mango-application/src/test/java/com/mangoo/rating/recommend/cart/RecommendServiceImplTest.java

@@ -201,7 +201,8 @@ class RecommendServiceImplTest {
     }
 
     @Test
-    void 同档多卡_合并为一单_总计为单价乘数量() {
+    void 同档多卡_合并为一单_无套餐价时总计等于时效单价() {
+        // 新公式:totalAmount = packageTotal × 卡数 + 时效单价;无套餐价 → 0×3 + 100 = 100
         when(ruleManager.selectByExact(any(), any(), any(), any(), any())).thenReturn(rule(2));
         AjaxResult result = recommendService.recommendEfficiency(req(
                 card("c1", "p", "y", "s", "cs"),
@@ -212,7 +213,7 @@ class RecommendServiceImplTest {
         OrderSuggestionVO order = d.getOrders().get(0);
         assertEquals(3, order.getCardCount());
         assertEquals(new BigDecimal("100.00"), order.getUnitPrice());
-        assertEquals(new BigDecimal("300.00"), order.getTotalAmount());
+        assertEquals(new BigDecimal("100.00"), order.getTotalAmount());
     }
 
     @Test
@@ -227,4 +228,52 @@ class RecommendServiceImplTest {
         assertEquals("http://x/f.jpg", d.getOrders().get(0).getCards().get(0).getFrontImageUrl());
         assertEquals("http://x/b.jpg", d.getOrders().get(0).getCards().get(0).getBackImageUrl());
     }
+
+    @Test
+    void 套餐价生效_订单总额包含套餐价合计_且卡级透传packageId与packagePrice() {
+        // 同档 2 卡,时效价 100,每卡套餐价 30 / 50
+        when(ruleManager.selectByExact(any(), any(), any(), any(), any())).thenReturn(rule(2));
+        RecommendCardDTO c1 = card("c1", "p", "y", "s", "cs");
+        c1.setId(101L);
+        c1.setPrice(new BigDecimal("30"));
+        RecommendCardDTO c2 = card("c2", "p", "y", "s", "cs");
+        c2.setId(102L);
+        c2.setPrice(new BigDecimal("50"));
+
+        AjaxResult result = recommendService.recommendEfficiency(req(c1, c2));
+        RecommendResultVO d = data(result);
+        OrderSuggestionVO order = d.getOrders().get(0);
+
+        // 时效价单价 100
+        assertEquals(new BigDecimal("100.00"), order.getUnitPrice());
+        // 套餐价合计 = 30 + 50 = 80
+        assertEquals(new BigDecimal("80"), order.getPackageTotal());
+        // 订单总计 = 套餐合计 × 卡数 + 时效单价 = 80×2 + 100 = 260(scale=2 因为时效价 100.00 传染)
+        assertEquals(new BigDecimal("260.00"), order.getTotalAmount());
+        // grandTotal 与订单总计一致
+        assertEquals(new BigDecimal("260.00"), d.getGrandTotal());
+        // 卡级透传 packageId / packagePrice
+        assertEquals(101L, order.getCards().get(0).getPackageId());
+        assertEquals(new BigDecimal("30"), order.getCards().get(0).getPackagePrice());
+        assertEquals(102L, order.getCards().get(1).getPackageId());
+        assertEquals(new BigDecimal("50"), order.getCards().get(1).getPackagePrice());
+    }
+
+    @Test
+    void 套餐价部分为null_当0处理_不阻断() {
+        // 同档 2 卡,时效价 100,一卡套餐价 40 另一卡 null
+        when(ruleManager.selectByExact(any(), any(), any(), any(), any())).thenReturn(rule(2));
+        RecommendCardDTO c1 = card("c1", "p", "y", "s", "cs");
+        c1.setPrice(new BigDecimal("40"));
+        RecommendCardDTO c2 = card("c2", "p", "y", "s", "cs");
+        // c2.price 不设 → null 当 0
+
+        AjaxResult result = recommendService.recommendEfficiency(req(c1, c2));
+        RecommendResultVO d = data(result);
+        OrderSuggestionVO order = d.getOrders().get(0);
+
+        // 套餐合计 = 40 + 0 = 40;订单总计 = 40×2 + 100 = 180(新公式:合计×卡数+时效单价;scale=2)
+        assertEquals(new BigDecimal("40"), order.getPackageTotal());
+        assertEquals(new BigDecimal("180.00"), order.getTotalAmount());
+    }
 }

+ 9 - 0
mango-common/src/main/java/com/mangoo/rating/recommend/request/recommend/RecommendCardDTO.java

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import javax.validation.constraints.NotBlank;
+import java.math.BigDecimal;
 
 /**
  * 单卡推荐入参(识别特征 + 透传字段)
@@ -37,4 +38,12 @@ public class RecommendCardDTO {
 
     @ApiModelProperty(value = "反面图URL(仅透传回显)")
     private String backImageUrl;
+
+    @ApiModelProperty(value = "套餐ID")
+    private Long id;
+
+    @ApiModelProperty(value = "套餐价格")
+    private BigDecimal price;
+
+
 }

+ 4 - 1
mango-common/src/main/java/com/mangoo/rating/recommend/response/recommend/OrderSuggestionVO.java

@@ -32,7 +32,10 @@ public class OrderSuggestionVO {
     @ApiModelProperty(value = "订单中卡片数量")
     private Integer cardCount;
 
-    @ApiModelProperty(value = "订单总计(=单价×数量;任一缺价则为 null)")
+    @ApiModelProperty(value = "订单内套餐价合计(Σ各卡 packagePrice,null 当 0)")
+    private BigDecimal packageTotal;
+
+    @ApiModelProperty(value = "订单总计(=套餐价合计×卡数 + 时效单价;时效价缺失则为 null)")
     private BigDecimal totalAmount;
 
     @ApiModelProperty(value = "订单内卡列表")

+ 20 - 14
mango-common/src/main/java/com/mangoo/rating/recommend/response/recommend/RecommendCardVO.java

@@ -52,18 +52,24 @@ public class RecommendCardVO {
     @ApiModelProperty(value = "卡号")
     private String cardNo;
 
-    @ApiModelProperty(value = "当前价")
-    private java.math.BigDecimal currentValue;
-
-    @ApiModelProperty(value = "涨跌幅(百分数)")
-    private java.math.BigDecimal valueChangePct;
-
-    @ApiModelProperty(value = "价格区间下限")
-    private java.math.BigDecimal valueMin;
-
-    @ApiModelProperty(value = "价格区间上限")
-    private java.math.BigDecimal valueMax;
-
-    @ApiModelProperty(value = "POP明细(三机构)")
-    private java.util.List<CardPopVO> pops;
+    @ApiModelProperty(value = "套餐ID(透传入参 id)")
+    private Long packageId;
+
+    @ApiModelProperty(value = "套餐价格(透传入参 price,参与订单合计)")
+    private BigDecimal packagePrice;
+
+//    @ApiModelProperty(value = "当前价")
+//    private java.math.BigDecimal currentValue;
+//
+//    @ApiModelProperty(value = "涨跌幅(百分数)")
+//    private java.math.BigDecimal valueChangePct;
+//
+//    @ApiModelProperty(value = "价格区间下限")
+//    private java.math.BigDecimal valueMin;
+//
+//    @ApiModelProperty(value = "价格区间上限")
+//    private java.math.BigDecimal valueMax;
+//
+//    @ApiModelProperty(value = "POP明细(三机构)")
+//    private java.util.List<CardPopVO> pops;
 }

+ 52 - 21
mango-domain/src/main/java/com/mangoo/rating/recommend/service/impl/RecommendServiceImpl.java

@@ -105,6 +105,9 @@ public class RecommendServiceImpl implements RecommendService {
         vo.setCardSet(dto.getCardSet());
         vo.setFrontImageUrl(dto.getFrontImageUrl());
         vo.setBackImageUrl(dto.getBackImageUrl());
+        // 透传套餐ID与套餐价格(入参字段名 id/price,响应改业务命名 packageId/packagePrice)
+        vo.setPackageId(dto.getId());
+        vo.setPackagePrice(dto.getPrice());
 
         RatingRecommendRulePO rule = ratingRecommendRuleManager.selectByExact(
                 dto.getPlayer(), dto.getYear(), dto.getSeries(), dto.getCardSet(), activeBatchNo);
@@ -136,26 +139,26 @@ public class RecommendServiceImpl implements RecommendService {
         vo.setMatchLevel(level.getCode());
         vo.setCardId(rule.getCardId());
         vo.setCardNo(rule.getCardNo());
-        vo.setCurrentValue(rule.getCurrentValue());
-        vo.setValueChangePct(rule.getValueChangePct());
-        vo.setValueMin(rule.getValueMin());
-        vo.setValueMax(rule.getValueMax());
+//        vo.setCurrentValue(rule.getCurrentValue());
+//        vo.setValueChangePct(rule.getValueChangePct());
+//        vo.setValueMin(rule.getValueMin());
+//        vo.setValueMax(rule.getValueMax());
         // 补查 POP(三机构)
-        if (rule.getCardId() != null && activeBatchNo != null) {
-            List<CardPopPO> pops = cardPopManager.selectByCardId(rule.getCardId(), activeBatchNo);
-            List<CardPopVO> popVOs = new ArrayList<>();
-            for (CardPopPO p : pops) {
-                CardPopVO pv = new CardPopVO();
-                pv.setAgency(p.getAgency());
-                pv.setAll(p.getGradeAll());
-                pv.setG10(p.getGrade10());
-                pv.setG9(p.getGrade9());
-                pv.setG8(p.getGrade8());
-                pv.setG7(p.getGrade7());
-                popVOs.add(pv);
-            }
-            vo.setPops(popVOs);
-        }
+//        if (rule.getCardId() != null && activeBatchNo != null) {
+//            List<CardPopPO> pops = cardPopManager.selectByCardId(rule.getCardId(), activeBatchNo);
+//            List<CardPopVO> popVOs = new ArrayList<>();
+//            for (CardPopPO p : pops) {
+//                CardPopVO pv = new CardPopVO();
+//                pv.setAgency(p.getAgency());
+//                pv.setAll(p.getGradeAll());
+//                pv.setG10(p.getGrade10());
+//                pv.setG9(p.getGrade9());
+//                pv.setG8(p.getGrade8());
+//                pv.setG7(p.getGrade7());
+//                popVOs.add(pv);
+//            }
+//            vo.setPops(popVOs);
+//        }
     }
 
     private boolean isLegalRule(RatingRecommendRulePO rule) {
@@ -165,7 +168,12 @@ public class RecommendServiceImpl implements RecommendService {
     }
 
     /**
-     * 回填单价/总计并返回合计;某档无价 → 该订单 null("待计算"),合计置 null
+     * 回填单价/总计并返回合计。
+     * 算价口径(大侠口径 2026-06-30):
+     *   订单 packageTotal = Σ(各卡 packagePrice),null 当 0
+     *   订单 totalAmount  = packageTotal × 卡数 + 时效单价
+     *   时效价某档无价 → 订单 unitPrice/totalAmount = null("待计算"),grandTotal 也为 null
+     *   套餐价 packagePrice 始终透传到卡级,无需降级
      */
     private BigDecimal applyPricing(List<OrderSuggestionVO> orders, Map<Integer, BigDecimal> priceMap) {
         if (orders == null || orders.isEmpty()) {
@@ -174,6 +182,11 @@ public class RecommendServiceImpl implements RecommendService {
         BigDecimal grand = BigDecimal.ZERO;
         boolean grandNull = false;
         for (OrderSuggestionVO order : orders) {
+            // 1) 先汇总套餐价(与时效价是否取到无关,packageTotal 始终能算出)
+            BigDecimal packageTotal = sumPackagePrice(order.getCards());
+            order.setPackageTotal(packageTotal);
+
+            // 2) 再算时效价;缺失则降级"待计算"
             BigDecimal unit = (priceMap == null) ? null : priceMap.get(order.getEfficiency());
             if (unit == null) {
                 order.setUnitPrice(null);
@@ -186,7 +199,9 @@ public class RecommendServiceImpl implements RecommendService {
                 }
                 continue;
             }
-            BigDecimal total = unit.multiply(BigDecimal.valueOf(order.getCardCount()));
+            // 3) 订单总计 = 套餐价合计 × 卡数 + 时效单价
+            BigDecimal packageSubtotal = packageTotal.multiply(BigDecimal.valueOf(order.getCardCount()));
+            BigDecimal total = packageSubtotal.add(unit);
             order.setUnitPrice(unit);
             order.setTotalAmount(total);
             if (order.getCards() != null) {
@@ -198,4 +213,20 @@ public class RecommendServiceImpl implements RecommendService {
         }
         return grandNull ? null : grand;
     }
+
+    /**
+     * 汇总订单内各卡的套餐价(packagePrice 为 null 当 0 处理)
+     */
+    private BigDecimal sumPackagePrice(List<RecommendCardVO> cards) {
+        BigDecimal sum = BigDecimal.ZERO;
+        if (cards == null) {
+            return sum;
+        }
+        for (RecommendCardVO c : cards) {
+            if (c != null && c.getPackagePrice() != null) {
+                sum = sum.add(c.getPackagePrice());
+            }
+        }
+        return sum;
+    }
 }