|
|
@@ -771,4 +771,276 @@ class OrderServiceTest {
|
|
|
verify(commissionRepository, never()).save(argThat((Commission c) ->
|
|
|
"settled".equals(c.getStatus())));
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 完整分佣流程 (注册→付费→佣金分配) ====================
|
|
|
+ //
|
|
|
+ // 这些测试模拟会员从注册到分佣的完整链路, 而非仅测试单个方法。
|
|
|
+ // 数据流: 注册(选填推广码) → 付费 paySuccess → 系统创建佣金 → 佣金到账
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 场景: 用户B通过A的推广码注册 → B购买C端年费 ¥131
|
|
|
+ * 预期: A获得L1=40%佣金 ¥52.40, A的上级C获得L2=5%佣金 ¥6.55
|
|
|
+ * 流程: ① A已付费(referralCode≠null) ② A引荐B ③ B注册 ④ B支付
|
|
|
+ * ⑤ paySuccess 生成 B 的 referralCode ⑥ 佣金分配到 A(L1) 和 C(L2)
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void testCommissionFlow_cEnd_threeTier() {
|
|
|
+ // ── 用户A (引荐人, 已付费) ──
|
|
|
+ User inviterA = new User();
|
|
|
+ inviterA.setId(200L);
|
|
|
+ inviterA.setReferralCode("AAAAAA");
|
|
|
+ inviterA.setInvitedBy(300L); // A的上级C
|
|
|
+
|
|
|
+ // ── 用户C (A的上级, 已付费) ──
|
|
|
+ User uplineC = new User();
|
|
|
+ uplineC.setId(300L);
|
|
|
+ uplineC.setReferralCode("CCCCCC");
|
|
|
+ uplineC.setInvitedBy(null);
|
|
|
+
|
|
|
+ // ── 用户B (新买家, 通过A的推广码注册) ──
|
|
|
+ User buyerB = new User();
|
|
|
+ buyerB.setId(100L);
|
|
|
+ buyerB.setInvitedBy(200L); // 注册时通过 referralCode 绑定
|
|
|
+
|
|
|
+ // ── B创建年费订单 ──
|
|
|
+ Order order = new Order();
|
|
|
+ order.setId(1L);
|
|
|
+ order.setUserId(100L);
|
|
|
+ order.setTotalFee(13100); // ¥131 C端年费
|
|
|
+ order.setProductType("annual");
|
|
|
+ order.setStatus("pending");
|
|
|
+
|
|
|
+ when(configService.getInt("commission.annual.direct_rate", 4000)).thenReturn(4000);
|
|
|
+ when(configService.getInt("commission.annual.upstream_rate", 500)).thenReturn(500);
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_c_tier")).thenReturn(Optional.of(order));
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(order);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyerB));
|
|
|
+ when(userRepository.findById(200L)).thenReturn(Optional.of(inviterA));
|
|
|
+ when(userRepository.findById(300L)).thenReturn(Optional.of(uplineC));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_c_tier");
|
|
|
+
|
|
|
+ // L1 = 40% × 13100 = 5240 → 给A
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 1 == c.getLevel() && 5240 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 200L == c.getToUserId()
|
|
|
+ && "settled".equals(c.getStatus())));
|
|
|
+ // L2 = 5% × 13100 = 655 → 给C
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 2 == c.getLevel() && 655 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 300L == c.getToUserId()
|
|
|
+ && "settled".equals(c.getStatus())));
|
|
|
+ // B的 referralCode 在 paySuccess 中生成
|
|
|
+ verify(userService).generateReferralCodeForUser(100L);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 场景: 引荐人B(能量师) → 买家A(通过B推广码注册) → A购买能量师 ¥1,314
|
|
|
+ * 预期: B获得L1固定佣金 ¥500, B的上级C获得L2固定佣金 ¥100
|
|
|
+ * 流程: ① B已付费(vipType=practitioner) ② B引荐A ③ A注册 ④ A支付
|
|
|
+ * ⑤ 佣金分配到 B(L1=¥500) 和 C(L2=¥100)
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void testCommissionFlow_bEnd_fullPipeline() {
|
|
|
+ // ── 引荐人B (能量师, 已付费) ──
|
|
|
+ User inviterB = new User();
|
|
|
+ inviterB.setId(200L);
|
|
|
+ inviterB.setVipType("practitioner");
|
|
|
+ inviterB.setInvitedBy(300L);
|
|
|
+ inviterB.setDirectCount(0);
|
|
|
+ inviterB.setConvertedCount(0);
|
|
|
+
|
|
|
+ // ── B的上级C ──
|
|
|
+ User uplineC = new User();
|
|
|
+ uplineC.setId(300L);
|
|
|
+ uplineC.setDirectCount(0);
|
|
|
+ uplineC.setIndirectCount(0);
|
|
|
+
|
|
|
+ // ── 买家A (通过B的推广码注册) ──
|
|
|
+ User buyerA = new User();
|
|
|
+ buyerA.setId(100L);
|
|
|
+ buyerA.setInvitedBy(200L);
|
|
|
+
|
|
|
+ // ── A创建能量师订单 ──
|
|
|
+ Order order = new Order();
|
|
|
+ order.setId(1L);
|
|
|
+ order.setUserId(100L);
|
|
|
+ order.setTotalFee(131400); // ¥1,314
|
|
|
+ order.setProductType("practitioner");
|
|
|
+ order.setStatus("pending");
|
|
|
+
|
|
|
+ when(configService.getInt("commission.practitioner.l1", 50000)).thenReturn(50000);
|
|
|
+ when(configService.getInt("commission.practitioner.l2", 10000)).thenReturn(10000);
|
|
|
+ when(commissionRepository.findByOrderId(1L)).thenReturn(Collections.emptyList());
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_b_full")).thenReturn(Optional.of(order));
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(order);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyerA));
|
|
|
+ when(userRepository.findById(200L)).thenReturn(Optional.of(inviterB));
|
|
|
+ when(userRepository.findById(300L)).thenReturn(Optional.of(uplineC));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_b_full");
|
|
|
+
|
|
|
+ // L1 = ¥500(50000分) → 给B
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 1 == c.getLevel() && 50000 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 200L == c.getToUserId()
|
|
|
+ && "settled".equals(c.getStatus())));
|
|
|
+ // L2 = ¥100(10000分) → 给C
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 2 == c.getLevel() && 10000 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 300L == c.getToUserId()
|
|
|
+ && "settled".equals(c.getStatus())));
|
|
|
+ // 团队统计更新
|
|
|
+ verify(userRepository).incrementConvertedCount(200L);
|
|
|
+ verify(userRepository).incrementIndirectCount(300L);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 场景: A(引荐人)未付费(referralCode=null) → B通过A的推广码注册 → B购买C端年费
|
|
|
+ * 预期: A不获得佣金 (AC: referralCode=null 即未付费的引荐人无佣金)
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void testCommissionFlow_inviterNotPaid_skipsCommission() {
|
|
|
+ User inviter = new User();
|
|
|
+ inviter.setId(200L);
|
|
|
+ inviter.setReferralCode(null); // 引荐人自身未付费
|
|
|
+ inviter.setVipType(null);
|
|
|
+
|
|
|
+ User buyer = new User();
|
|
|
+ buyer.setId(100L);
|
|
|
+ buyer.setInvitedBy(200L); // 通过推广码注册
|
|
|
+
|
|
|
+ Order order = new Order();
|
|
|
+ order.setId(1L);
|
|
|
+ order.setUserId(100L);
|
|
|
+ order.setTotalFee(13100);
|
|
|
+ order.setProductType("annual");
|
|
|
+ order.setStatus("pending");
|
|
|
+
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_no_pay")).thenReturn(Optional.of(order));
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(order);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyer));
|
|
|
+ when(userRepository.findById(200L)).thenReturn(Optional.of(inviter));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_no_pay");
|
|
|
+
|
|
|
+ // C端: inviter.referralCode=null → handleAnnualCommission 直接 return
|
|
|
+ verify(commissionRepository, never()).save(any(Commission.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 场景: A(引荐人, 已付费) → B注册(无推广码, 自然流量) → B购买能量师
|
|
|
+ * 预期: 不创建任何佣金 (B没有invitedBy)
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void testCommissionFlow_noReferrer_noCommission() {
|
|
|
+ User buyer = new User();
|
|
|
+ buyer.setId(100L);
|
|
|
+ buyer.setInvitedBy(null); // 自然流量, 无引荐人
|
|
|
+
|
|
|
+ Order order = new Order();
|
|
|
+ order.setId(1L);
|
|
|
+ order.setUserId(100L);
|
|
|
+ order.setTotalFee(131400);
|
|
|
+ order.setProductType("practitioner");
|
|
|
+ order.setStatus("pending");
|
|
|
+
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_no_ref")).thenReturn(Optional.of(order));
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(order);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyer));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_no_ref");
|
|
|
+
|
|
|
+ // buyer.getInvitedBy() == null → paySuccess 直接 return
|
|
|
+ verify(commissionRepository, never()).save(any(Commission.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 场景: B端升级链路 — 用户A能量师引荐B注册 → B先买C端年费(¥131) → B升级能量师(¥1,986)
|
|
|
+ * 第一阶段(B购买年费): A获得L1=40% (¥52.40)
|
|
|
+ * 第二阶段(B升级能量师): A获得L1 = 默认¥500 - 已付¥52.40 = ¥447.60
|
|
|
+ * 验证: 两次付款后的佣金之和不超过默认L1
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ void testCommissionFlow_bEnd_upgradeDeduction_chain() {
|
|
|
+ // ── 引荐人A (能量师, 已付费, 有推广码) ──
|
|
|
+ User inviterA = new User();
|
|
|
+ inviterA.setId(200L);
|
|
|
+ inviterA.setVipType("practitioner");
|
|
|
+ inviterA.setReferralCode("AAAAAA"); // 已付费 → 有 referralCode, 支持 C端佣金分配
|
|
|
+ inviterA.setDirectCount(0);
|
|
|
+ inviterA.setConvertedCount(0);
|
|
|
+
|
|
|
+ // ── 买家B (通过A注册) ──
|
|
|
+ User buyerB = new User();
|
|
|
+ buyerB.setId(100L);
|
|
|
+ buyerB.setInvitedBy(200L);
|
|
|
+
|
|
|
+ // ══════ 阶段一: B购买C端年费 ══════
|
|
|
+ Order annualOrder = new Order();
|
|
|
+ annualOrder.setId(1L);
|
|
|
+ annualOrder.setUserId(100L);
|
|
|
+ annualOrder.setTotalFee(13100);
|
|
|
+ annualOrder.setProductType("annual");
|
|
|
+ annualOrder.setStatus("pending");
|
|
|
+
|
|
|
+ when(configService.getInt("commission.annual.direct_rate", 4000)).thenReturn(4000);
|
|
|
+ when(configService.getInt("commission.annual.upstream_rate", 500)).thenReturn(500);
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_annual")).thenReturn(Optional.of(annualOrder));
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(annualOrder);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyerB));
|
|
|
+ when(userRepository.findById(200L)).thenReturn(Optional.of(inviterA));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_annual");
|
|
|
+
|
|
|
+ // 阶段一: A获得L1=40%×13100=5240
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 1 == c.getLevel() && 5240 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 200L == c.getToUserId()));
|
|
|
+
|
|
|
+ // ══════ 阶段二: B升级能量师 ══════
|
|
|
+ Order upgradeOrder = new Order();
|
|
|
+ upgradeOrder.setId(2L);
|
|
|
+ upgradeOrder.setUserId(100L);
|
|
|
+ upgradeOrder.setTotalFee(198600);
|
|
|
+ upgradeOrder.setProductType("practitioner");
|
|
|
+ upgradeOrder.setStatus("pending");
|
|
|
+ upgradeOrder.setIsUpgrade(true);
|
|
|
+
|
|
|
+ // 阶段一已结算的 annual 佣金 → sumAnnualCommissionTo 会查到
|
|
|
+ Commission settledAnnual = new Commission();
|
|
|
+ settledAnnual.setId(10L);
|
|
|
+ settledAnnual.setFromUserId(100L);
|
|
|
+ settledAnnual.setToUserId(200L);
|
|
|
+ settledAnnual.setAmount(5240);
|
|
|
+ settledAnnual.setLevel(1);
|
|
|
+ settledAnnual.setStatus("settled");
|
|
|
+ settledAnnual.setOrderId(1L);
|
|
|
+
|
|
|
+ Order paidAnnualOrder = new Order();
|
|
|
+ paidAnnualOrder.setId(1L);
|
|
|
+ paidAnnualOrder.setProductType("annual");
|
|
|
+ paidAnnualOrder.setUserId(100L);
|
|
|
+
|
|
|
+ when(configService.getInt("commission.practitioner.l1", 50000)).thenReturn(50000);
|
|
|
+ when(configService.getInt("commission.practitioner.l2", 10000)).thenReturn(10000);
|
|
|
+ when(commissionRepository.findByOrderId(2L)).thenReturn(Collections.emptyList());
|
|
|
+ when(commissionRepository.findByFromUserId(100L)).thenReturn(Arrays.asList(settledAnnual));
|
|
|
+ when(orderRepository.findById(1L)).thenReturn(Optional.of(paidAnnualOrder));
|
|
|
+ when(orderRepository.findByOutTradeNo("flow_upgrade")).thenReturn(Optional.of(upgradeOrder));
|
|
|
+ // After paySuccess saves the upgrade order, the mock returns it
|
|
|
+ when(orderRepository.save(any(Order.class))).thenReturn(upgradeOrder);
|
|
|
+ when(userRepository.findById(100L)).thenReturn(Optional.of(buyerB));
|
|
|
+ when(userRepository.findById(200L)).thenReturn(Optional.of(inviterA));
|
|
|
+
|
|
|
+ orderService.paySuccess("flow_upgrade");
|
|
|
+
|
|
|
+ // 阶段二: A获得L1 = max(0, 50000 - 5240) = 44760
|
|
|
+ verify(commissionRepository).save(argThat((Commission c) ->
|
|
|
+ 1 == c.getLevel() && 44760 == c.getAmount()
|
|
|
+ && 100L == c.getFromUserId() && 200L == c.getToUserId()
|
|
|
+ && "settled".equals(c.getStatus())));
|
|
|
+ // L1总共 = 5240 + 44760 = 50000 = 默认L1, 没有超额
|
|
|
+ verify(userRepository).incrementConvertedCount(200L);
|
|
|
+ }
|
|
|
}
|