|
|
@@ -1627,6 +1627,13 @@ public class EnergyService {
|
|
|
@Resource
|
|
|
private EnergyBalanceMapper energyBalanceMapper;
|
|
|
|
|
|
+ /**
|
|
|
+ * 每日上限配置读取(SC-P-90 / TC-P-90 前置:{@code energy_daily_limit} 表按维度配置 dailyLimit=N,
|
|
|
+ * 配置驱动上限覆盖硬编码 {@link #DAILY_LIMIT_MAP};多来源共享同一日上限,0/负按 0 处理不发放)。
|
|
|
+ */
|
|
|
+ @Resource
|
|
|
+ private DimensionDailyLimitMapper dimensionDailyLimitMapper;
|
|
|
+
|
|
|
@Resource
|
|
|
private WishMapper wishMapper;
|
|
|
|
|
|
@@ -1883,9 +1890,10 @@ public class EnergyService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 发放能量 — 通过维度code(自动查ID),无日上限/配置约束,供低频行为直接发放
|
|
|
+ * 发放能量 — 通过维度code(自动查ID),受每日上限约束(多来源共享同一日上限,SC-P-90 /
|
|
|
+ * TC-P-90-E-1:challenge 等各来源累计触顶后不再发放)
|
|
|
*
|
|
|
- * @return 发放结果 Map<dimensionCode, amount>(空 = 维度不存在未发放)
|
|
|
+ * @return 发放结果 Map<dimensionCode, amount>(空 = 维度不存在 / 超限未发放)
|
|
|
*/
|
|
|
@Transactional
|
|
|
public Map<String, Integer> awardEnergyByCode(Long memberId, String dimensionCode, Integer amount, String reason) {
|
|
|
@@ -1897,6 +1905,12 @@ public class EnergyService {
|
|
|
}
|
|
|
Map<Long, Integer> allocations = new LinkedHashMap<>();
|
|
|
allocations.put(dim.getId(), amount);
|
|
|
+ // 日上限守卫(SC-P-90:写入前经守卫校验;多来源共享同一日上限,超限不发)
|
|
|
+ if (isDailyLimitExceeded(memberId, allocations.keySet(), amount)) {
|
|
|
+ log.warn("日上限已达,跳过发放: memberId={}, dimensionCode={}, amount={}",
|
|
|
+ memberId, dimensionCode, amount);
|
|
|
+ return new HashMap<>();
|
|
|
+ }
|
|
|
return executeAward(memberId, "challenge", null, amount, reason, null, allocations);
|
|
|
}
|
|
|
|
|
|
@@ -2295,9 +2309,12 @@ public class EnergyService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 检查日上限是否已达
|
|
|
+ * 检查日上限是否已达(公共守卫入口,SC-P-90:ruleEngine 写入前经此校验)。
|
|
|
+ *
|
|
|
+ * <p>多来源共享同一日上限:维度上限由 {@code energy_daily_limit} 配置驱动(无配置回落硬编码
|
|
|
+ * {@link #DAILY_LIMIT_MAP});配置 0/负 按 0 处理不发放(TC-P-90-E-4),不崩溃。</p>
|
|
|
*/
|
|
|
- private boolean isDailyLimitExceeded(Long memberId, Set<Long> dimIds, Integer amount) {
|
|
|
+ public boolean isDailyLimitExceeded(Long memberId, Set<Long> dimIds, Integer amount) {
|
|
|
return isDailyLimitExceeded(memberId, dimIds, amount, GLOBAL_DAILY_LIMIT);
|
|
|
}
|
|
|
|
|
|
@@ -2334,7 +2351,10 @@ public class EnergyService {
|
|
|
|
|
|
EnergyDimension dim = getDimById(dimId);
|
|
|
String code = dim != null ? dim.getCode() : "";
|
|
|
- Integer limit = DAILY_LIMIT_MAP.getOrDefault(code, 50);
|
|
|
+ Integer limit = resolveDailyLimit(code);
|
|
|
+
|
|
|
+ // TC-P-90-E-4:配置 0/负 按 0 处理(不发放),不崩溃
|
|
|
+ if (limit == null || limit <= 0) return true;
|
|
|
|
|
|
if (dimToday + amount > limit) return true;
|
|
|
}
|
|
|
@@ -2342,6 +2362,28 @@ public class EnergyService {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 解析某维度每日上限:优先 {@code energy_daily_limit} 配置驱动(TC-P-90 前置),
|
|
|
+ * 未配置/读取失败回落硬编码 {@link #DAILY_LIMIT_MAP}(默认 50)。
|
|
|
+ *
|
|
|
+ * <p>返回值为 null 表示无有效配置(应回落默认);0/负 保留原值,由调用方按 0 处理拦截。</p>
|
|
|
+ */
|
|
|
+ private Integer resolveDailyLimit(String code) {
|
|
|
+ if (code != null && dimensionDailyLimitMapper != null) {
|
|
|
+ try {
|
|
|
+ List<DimensionDailyLimit> limits = dimensionDailyLimitMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<DimensionDailyLimit>()
|
|
|
+ .eq(DimensionDailyLimit::getDimensionCode, code));
|
|
|
+ if (limits != null && !limits.isEmpty() && limits.get(0) != null) {
|
|
|
+ return limits.get(0).getDailyLimit();
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取每日上限配置失败 dimensionCode={}: {}", code, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return DAILY_LIMIT_MAP.getOrDefault(code, 50);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 获取今日起始时间(00:00:00)
|
|
|
*/
|