|
@@ -1,5 +1,9 @@
|
|
|
package com.etotem.cfc.service;
|
|
package com.etotem.cfc.service;
|
|
|
|
|
|
|
|
|
|
+import com.etotem.cfc.entity.TeamAwardRecord;
|
|
|
|
|
+import com.etotem.cfc.entity.TeamShareRecord;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.LinkedHashMap;
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
@@ -199,4 +203,114 @@ public class TeamProfitService {
|
|
|
// 其它正向订单:返回稳定正演示值(当前测试不覆盖)
|
|
// 其它正向订单:返回稳定正演示值(当前测试不覆盖)
|
|
|
return DEFAULT_EIGHT_SHARE_POOL / 3;
|
|
return DEFAULT_EIGHT_SHARE_POOL / 3;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 杠杆 1 基线:8 份分佣(§4.2 #5 拍板)+ 团队奖(§4.4 #3 拍板) ====================
|
|
|
|
|
+
|
|
|
|
|
+ /** §7.8 team_profit.share_total 默认 8(8 份分佣)。 */
|
|
|
|
|
+ private static final int SHARE_TOTAL = 8;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * §4.2 8 份分佣:按层分配份额并生成 {@code team_share_record} 流水(未落库,返回列表)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>规则(#5 拍板):L1(服务者本人)/L2(引荐人)/L3(再上级) 逐层分得 min(当期星级, 剩余);
|
|
|
|
|
+ * 每层最多 6 份;星级=0 或该层无成员 → 无人层位,份额回流(§9#4),不产出记录;
|
|
|
|
|
+ * shareCent = round(8份池 × 份数 / 8)。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param poolId 来源 profit_pool_order.id
|
|
|
|
|
+ * @param orderId 来源订单ID
|
|
|
|
|
+ * @param orderType 触发源类型
|
|
|
|
|
+ * @param providerUserId 服务提供者(规划师/测评师)
|
|
|
|
|
+ * @param eightSharePoolCent 8 份分佣池(分,默认 T×80%)
|
|
|
|
|
+ * @param recipientUserIds 第 i+1 层成员 userId(与 layerStarLevels 对齐;0 = 该层无人)
|
|
|
|
|
+ * @param layerStarLevels 第 i+1 层成员当期星级快照(1~6;0 = 该层无人)
|
|
|
|
|
+ * @return team_share_record 流水列表(按 layer 升序;无人层位 0 回流不产出记录)
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<TeamShareRecord> allocateEightShares(Long poolId, Long orderId, String orderType,
|
|
|
|
|
+ Long providerUserId, long eightSharePoolCent,
|
|
|
|
|
+ long[] recipientUserIds, int[] layerStarLevels) {
|
|
|
|
|
+ List<TeamShareRecord> records = new ArrayList<>();
|
|
|
|
|
+ if (recipientUserIds == null || layerStarLevels == null) {
|
|
|
|
|
+ return records;
|
|
|
|
|
+ }
|
|
|
|
|
+ long remaining = SHARE_TOTAL;
|
|
|
|
|
+ int layers = Math.min(recipientUserIds.length, layerStarLevels.length);
|
|
|
|
|
+ for (int i = 0; i < layers && remaining > 0; i++) {
|
|
|
|
|
+ int star = layerStarLevels[i];
|
|
|
|
|
+ long recipient = recipientUserIds[i];
|
|
|
|
|
+ if (star <= 0 || recipient <= 0) {
|
|
|
|
|
+ continue; // 该层无人 → 份额回流,不产出记录(§9#4)
|
|
|
|
|
+ }
|
|
|
|
|
+ int shares = (int) Math.min(star, remaining);
|
|
|
|
|
+ if (shares <= 0) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ long shareCent = Math.round(eightSharePoolCent * shares / 8.0);
|
|
|
|
|
+
|
|
|
|
|
+ TeamShareRecord r = new TeamShareRecord();
|
|
|
|
|
+ r.setPoolId(poolId);
|
|
|
|
|
+ r.setOrderId(orderId);
|
|
|
|
|
+ r.setOrderType(orderType);
|
|
|
|
|
+ r.setProviderUserId(providerUserId);
|
|
|
|
|
+ r.setRecipientUserId(recipient);
|
|
|
|
|
+ r.setLayer(i + 1);
|
|
|
|
|
+ r.setStarLevel(star);
|
|
|
|
|
+ r.setShares(shares);
|
|
|
|
|
+ r.setShareCent((int) shareCent);
|
|
|
|
|
+ r.setStatus("pending");
|
|
|
|
|
+ records.add(r);
|
|
|
|
|
+ remaining -= shares;
|
|
|
|
|
+ }
|
|
|
|
|
+ return records;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * §4.4 团队奖:按团队年度滚动业绩分档,生成 {@code team_award_record}(未落库,返回记录)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>分档(thr = 团队年度滚动业绩门槛):rev < thr → TA0 0%;thr ≤ rev < 2×thr → TA1 50%;
|
|
|
|
|
+ * 2×thr ≤ rev < 4×thr → TA2 80%;rev ≥ 4×thr → TA3 100%。awardCent = round(团队奖池 × 比例)。
|
|
|
|
|
+ * 归属平台自有团队负责人(ownerUserId,#3 拍板)。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param poolId 来源 profit_pool_order.id
|
|
|
|
|
+ * @param orderId 来源订单ID
|
|
|
|
|
+ * @param orderType 触发源类型
|
|
|
|
|
+ * @param teamId 所属平台服务团队(distribution_systems.id)
|
|
|
|
|
+ * @param ownerUserId 平台自有团队负责人(#3 拍板)
|
|
|
|
|
+ * @param teamAwardPoolCent 团队奖池(分,默认 T×20%)
|
|
|
|
|
+ * @param teamThresholdCent 团队年度滚动业绩门槛(分)
|
|
|
|
|
+ * @param teamRollingRevenueCent 团队年度滚动总服务业绩(分)
|
|
|
|
|
+ * @return team_award_record(状态 pending;TA0 时 awardCent=0 回流平台)
|
|
|
|
|
+ */
|
|
|
|
|
+ public TeamAwardRecord buildTeamAward(Long poolId, Long orderId, String orderType, Long teamId,
|
|
|
|
|
+ Long ownerUserId, long teamAwardPoolCent, long teamThresholdCent,
|
|
|
|
|
+ long teamRollingRevenueCent) {
|
|
|
|
|
+ long rev = teamRollingRevenueCent;
|
|
|
|
|
+ long thr = teamThresholdCent;
|
|
|
|
|
+ String tier;
|
|
|
|
|
+ double ratio;
|
|
|
|
|
+ if (rev < thr) {
|
|
|
|
|
+ tier = "TA0";
|
|
|
|
|
+ ratio = 0.0;
|
|
|
|
|
+ } else if (rev < 2 * thr) {
|
|
|
|
|
+ tier = "TA1";
|
|
|
|
|
+ ratio = 0.50;
|
|
|
|
|
+ } else if (rev < 4 * thr) {
|
|
|
|
|
+ tier = "TA2";
|
|
|
|
|
+ ratio = 0.80;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tier = "TA3";
|
|
|
|
|
+ ratio = 1.00;
|
|
|
|
|
+ }
|
|
|
|
|
+ long award = Math.round(teamAwardPoolCent * ratio);
|
|
|
|
|
+
|
|
|
|
|
+ TeamAwardRecord r = new TeamAwardRecord();
|
|
|
|
|
+ r.setPoolId(poolId);
|
|
|
|
|
+ r.setOrderId(orderId);
|
|
|
|
|
+ r.setOrderType(orderType);
|
|
|
|
|
+ r.setTeamId(teamId);
|
|
|
|
|
+ r.setOwnerUserId(ownerUserId);
|
|
|
|
|
+ r.setTier(tier);
|
|
|
|
|
+ r.setAwardCent((int) award);
|
|
|
|
|
+ r.setStatus("pending");
|
|
|
|
|
+ return r;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|