feat(puzzle): 增强拼图填充引擎功能

- 新增 requireRuleMatch 参数控制是否必须匹配规则
- 重构 DeviceCountConditionStrategy 支持两种匹配模式
- 移除已废弃的 DeviceCountRangeConditionStrategy
- 引入 FillResult 类封装填充结果信息
- 优化条件上下文和数据源上下文的 extra 字段类型
- 更新相关测试用例和文档说明
This commit is contained in:
2025-11-20 15:11:13 +08:00
parent aaa8d8310a
commit 2fd852c5c6
13 changed files with 470 additions and 261 deletions

View File

@@ -6,6 +6,10 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -130,7 +134,198 @@ class DeviceCountConditionStrategyTest {
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result); // 修改:deviceCount必须大于0
}
// ==================== 模式2:指定列表数量匹配 ====================
@Test
@DisplayName("模式2:当指定列表中恰好有期望数量的机位时应该返回true")
void shouldReturnTrueWhenDeviceIdListMatchesCount() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 2, \"deviceIds\": [200, 300, 400]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L, 500L);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
assertNotNull(context.getExtra());
assertTrue(context.getExtra().containsKey("filteredDeviceIds"));
@SuppressWarnings("unchecked")
List<Long> filteredIds = (List<Long>) context.getExtra().get("filteredDeviceIds");
assertEquals(2, filteredIds.size());
assertEquals(Arrays.asList(200L, 300L), filteredIds); // 保持配置顺序
}
@Test
@DisplayName("模式2:当指定列表中的机位数量不足时应该返回false")
void shouldReturnFalseWhenDeviceIdListCountInsufficient() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 3, \"deviceIds\": [200, 300, 400]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L, 500L);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:当指定列表中的机位数量超过期望时应该返回false")
void shouldReturnFalseWhenDeviceIdListCountExcessive() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 1, \"deviceIds\": [200, 300, 400]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L, 500L);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:应该保持配置的机位顺序,不排序")
void shouldPreserveDeviceIdOrderInMode2() throws Exception {
// Given - 配置顺序为 400, 200, 300
String conditionValueJson = "{\"deviceCount\": 3, \"deviceIds\": [400, 200, 300]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L, 400L);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
@SuppressWarnings("unchecked")
List<Long> filteredIds = (List<Long>) context.getExtra().get("filteredDeviceIds");
assertEquals(Arrays.asList(400L, 200L, 300L), filteredIds); // 保持配置顺序,不是[200, 300, 400]
}
@Test
@DisplayName("模式2:当deviceIds为空数组时应该返回false")
void shouldReturnFalseWhenDeviceIdsIsEmptyArray() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 2, \"deviceIds\": []}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:当deviceIds不是数组类型时应该返回false")
void shouldReturnFalseWhenDeviceIdsIsNotArray() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 2, \"deviceIds\": \"not-an-array\"}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:当context中没有deviceIds时应该返回false")
void shouldReturnFalseWhenContextDeviceIdsIsNull() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 2, \"deviceIds\": [200, 300]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.deviceIds(null)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:当context中deviceIds为空列表时应该返回false")
void shouldReturnFalseWhenContextDeviceIdsIsEmpty() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 2, \"deviceIds\": [200, 300]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.deviceIds(Collections.emptyList())
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("模式2:当deviceCount为0时应该返回false")
void shouldReturnFalseWhenDeviceCountIsZeroInMode2() throws Exception {
// Given
String conditionValueJson = "{\"deviceCount\": 0, \"deviceIds\": [200, 300]}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
List<Long> contextDeviceIds = Arrays.asList(200L, 300L);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.deviceIds(contextDeviceIds)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
}

View File

@@ -1,190 +0,0 @@
package com.ycwl.basic.puzzle.fill.condition;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* 机位数量范围匹配策略测试
*/
@DisplayName("机位数量范围匹配策略测试")
class DeviceCountRangeConditionStrategyTest {
private DeviceCountRangeConditionStrategy strategy;
private ObjectMapper objectMapper;
@BeforeEach
void setUp() {
strategy = new DeviceCountRangeConditionStrategy();
objectMapper = new ObjectMapper();
}
@Test
@DisplayName("应该返回正确的支持类型")
void shouldReturnCorrectSupportedType() {
assertEquals("DEVICE_COUNT_RANGE", strategy.getSupportedType());
}
@Test
@DisplayName("当机位数量在范围内时应该返回true")
void shouldReturnTrueWhenDeviceCountInRange() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
}
@Test
@DisplayName("当机位数量等于最小值时应该返回true")
void shouldReturnTrueWhenDeviceCountEqualsMin() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(2)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
}
@Test
@DisplayName("当机位数量等于最大值时应该返回true")
void shouldReturnTrueWhenDeviceCountEqualsMax() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(5)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
}
@Test
@DisplayName("当机位数量小于最小值时应该返回false")
void shouldReturnFalseWhenDeviceCountBelowMin() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(1)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("当机位数量大于最大值时应该返回false")
void shouldReturnFalseWhenDeviceCountAboveMax() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(6)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("仅指定最小值时应该正确验证")
void shouldValidateWithMinCountOnly() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
}
@Test
@DisplayName("仅指定最大值时应该正确验证")
void shouldValidateWithMaxCountOnly() throws Exception {
// Given
String conditionValueJson = "{\"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertTrue(result);
}
@Test
@DisplayName("当conditionValue为null时应该返回false")
void shouldReturnFalseWhenConditionValueIsNull() {
// Given
ConditionContext context = ConditionContext.builder()
.deviceCount(3)
.build();
// When
boolean result = strategy.evaluate(null, context);
// Then
assertFalse(result);
}
@Test
@DisplayName("当context中deviceCount为null时应该返回false")
void shouldReturnFalseWhenContextDeviceCountIsNull() throws Exception {
// Given
String conditionValueJson = "{\"minCount\": 2, \"maxCount\": 5}";
JsonNode conditionValue = objectMapper.readTree(conditionValueJson);
ConditionContext context = ConditionContext.builder()
.deviceCount(null)
.build();
// When
boolean result = strategy.evaluate(conditionValue, context);
// Then
assertFalse(result);
}
}