You've already forked FrameTour-BE
feat(puzzle): 增强拼图填充引擎功能
- 新增 requireRuleMatch 参数控制是否必须匹配规则 - 重构 DeviceCountConditionStrategy 支持两种匹配模式 - 移除已废弃的 DeviceCountRangeConditionStrategy - 引入 FillResult 类封装填充结果信息 - 优化条件上下文和数据源上下文的 extra 字段类型 - 更新相关测试用例和文档说明
This commit is contained in:
@@ -4,6 +4,7 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 条件评估上下文
|
||||
@@ -31,5 +32,5 @@ public class ConditionContext {
|
||||
/**
|
||||
* 可扩展的其他上下文数据
|
||||
*/
|
||||
private Object extra;
|
||||
private Map<String, Object> extra;
|
||||
}
|
||||
|
||||
@@ -2,28 +2,142 @@ package com.ycwl.basic.puzzle.fill.condition;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.ycwl.basic.puzzle.fill.enums.ConditionType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 机位数量精确匹配策略
|
||||
*
|
||||
* <p>支持两种匹配模式:</p>
|
||||
* <ul>
|
||||
* <li>模式1:全局数量匹配 - 只指定 deviceCount,匹配所有机位的数量</li>
|
||||
* <li>模式2:指定列表数量匹配 - 同时指定 deviceCount + deviceIds,从指定列表中过滤并匹配数量</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>配置示例:</p>
|
||||
* <pre>
|
||||
* // 模式1:全局数量匹配
|
||||
* {
|
||||
* "deviceCount": 4
|
||||
* }
|
||||
*
|
||||
* // 模式2:指定列表数量匹配
|
||||
* {
|
||||
* "deviceCount": 2,
|
||||
* "deviceIds": [200, 300, 400]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>模式2匹配逻辑:</p>
|
||||
* <ul>
|
||||
* <li>从 deviceIds 列表中过滤出实际存在的机位</li>
|
||||
* <li>保持配置顺序(不按 deviceId 排序)</li>
|
||||
* <li>判断过滤后的数量是否等于 deviceCount</li>
|
||||
* <li>匹配成功后,将过滤后的机位列表存入 context.extra,供数据源解析使用</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Claude
|
||||
* @since 2025-01-20
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeviceCountConditionStrategy implements ConditionStrategy {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(JsonNode conditionValue, ConditionContext context) {
|
||||
if (conditionValue == null || !conditionValue.has("deviceCount")) {
|
||||
log.warn("DEVICE_COUNT条件缺少deviceCount字段");
|
||||
return false;
|
||||
}
|
||||
|
||||
int expectedCount = conditionValue.get("deviceCount").asInt();
|
||||
Integer actualCount = context.getDeviceCount();
|
||||
|
||||
if (actualCount == null) {
|
||||
if (expectedCount <= 0) {
|
||||
log.warn("deviceCount必须大于0, 当前值: {}", expectedCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualCount == expectedCount;
|
||||
// 检查是否指定了 deviceIds(模式2)
|
||||
if (conditionValue.has("deviceIds")) {
|
||||
return evaluateWithDeviceIdList(conditionValue, context, expectedCount);
|
||||
} else {
|
||||
// 模式1:全局数量匹配
|
||||
return evaluateGlobalCount(context, expectedCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模式1:全局数量匹配
|
||||
*/
|
||||
private boolean evaluateGlobalCount(ConditionContext context, int expectedCount) {
|
||||
Integer actualCount = context.getDeviceCount();
|
||||
if (actualCount == null) {
|
||||
log.debug("上下文中没有机位数量信息");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean matched = actualCount == expectedCount;
|
||||
if (matched) {
|
||||
log.info("DEVICE_COUNT全局匹配成功: 期望数量={}, 实际数量={}", expectedCount, actualCount);
|
||||
} else {
|
||||
log.debug("DEVICE_COUNT全局匹配失败: 期望数量={}, 实际数量={}", expectedCount, actualCount);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模式2:指定列表数量匹配
|
||||
*/
|
||||
private boolean evaluateWithDeviceIdList(JsonNode conditionValue, ConditionContext context, int expectedCount) {
|
||||
// 1. 读取配置的 deviceIds 列表
|
||||
JsonNode deviceIdsNode = conditionValue.get("deviceIds");
|
||||
if (!deviceIdsNode.isArray()) {
|
||||
log.warn("deviceIds字段必须是数组");
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Long> requiredDeviceIds = new ArrayList<>();
|
||||
deviceIdsNode.forEach(node -> requiredDeviceIds.add(node.asLong()));
|
||||
|
||||
if (requiredDeviceIds.isEmpty()) {
|
||||
log.warn("deviceIds数组为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 获取上下文中的机位列表
|
||||
List<Long> contextDeviceIds = context.getDeviceIds();
|
||||
if (contextDeviceIds == null || contextDeviceIds.isEmpty()) {
|
||||
log.debug("上下文中没有机位ID列表");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 按配置顺序过滤出实际存在的机位
|
||||
List<Long> matchedDeviceIds = requiredDeviceIds.stream()
|
||||
.filter(contextDeviceIds::contains)
|
||||
.collect(Collectors.toList()); // 保持配置顺序,不排序
|
||||
|
||||
// 4. 精确匹配数量
|
||||
boolean matched = matchedDeviceIds.size() == expectedCount;
|
||||
|
||||
if (matched) {
|
||||
// 5. 将过滤后的机位列表存入 context.extra
|
||||
Map<String, Object> extra = new HashMap<>();
|
||||
extra.put("filteredDeviceIds", matchedDeviceIds);
|
||||
context.setExtra(extra);
|
||||
|
||||
log.info("DEVICE_COUNT列表匹配成功: 配置列表={}, 过滤后={}, 期望数量={}, 实际数量={}",
|
||||
requiredDeviceIds, matchedDeviceIds, expectedCount, matchedDeviceIds.size());
|
||||
} else {
|
||||
log.debug("DEVICE_COUNT列表匹配失败: 配置列表={}, 过滤后={}, 期望数量={}, 实际数量={}",
|
||||
requiredDeviceIds, matchedDeviceIds, expectedCount, matchedDeviceIds.size());
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.ycwl.basic.puzzle.fill.condition;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.ycwl.basic.puzzle.fill.enums.ConditionType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 机位数量范围匹配策略
|
||||
*/
|
||||
@Component
|
||||
public class DeviceCountRangeConditionStrategy implements ConditionStrategy {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(JsonNode conditionValue, ConditionContext context) {
|
||||
if (conditionValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer actualCount = context.getDeviceCount();
|
||||
if (actualCount == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer minCount = conditionValue.has("minCount") ? conditionValue.get("minCount").asInt() : null;
|
||||
Integer maxCount = conditionValue.has("maxCount") ? conditionValue.get("maxCount").asInt() : null;
|
||||
|
||||
if (minCount != null && actualCount < minCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxCount != null && actualCount > maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupportedType() {
|
||||
return ConditionType.DEVICE_COUNT_RANGE.getCode();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user