diff --git a/base/src/main/java/com/tinyengine/it/dynamic/dao/DynamicSqlProvider.java b/base/src/main/java/com/tinyengine/it/dynamic/dao/DynamicSqlProvider.java index fba8eac1..f0ff398d 100644 --- a/base/src/main/java/com/tinyengine/it/dynamic/dao/DynamicSqlProvider.java +++ b/base/src/main/java/com/tinyengine/it/dynamic/dao/DynamicSqlProvider.java @@ -15,9 +15,7 @@ public String select(Map params) { Integer pageSize = (Integer) params.get("pageSize"); String orderBy = (String) params.get("orderBy"); String orderType = (String) params.get("orderType"); - SQL sql = new SQL(); - // 选择字段 if (fields != null && !fields.isEmpty()) { for (String field : fields) { @@ -29,6 +27,8 @@ public String select(Map params) { sql.FROM(tableName); + + // 条件 if (conditions != null && !conditions.isEmpty()) { for (Map.Entry entry : conditions.entrySet()) { diff --git a/base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java b/base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java index 79608a70..08385707 100644 --- a/base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java +++ b/base/src/main/java/com/tinyengine/it/dynamic/dto/DynamicQuery.java @@ -1,5 +1,7 @@ package com.tinyengine.it.dynamic.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Data; import java.util.List; @@ -7,13 +9,17 @@ @Data public class DynamicQuery { - + @NotBlank(message = "表英文名不能为空") + @Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "模型名称格式不正确") private String nameEn; // 表名 private String nameCh; // 表中文名 + @Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "字段名称格式不正确") private List fields; // 查询字段 private Map params; // 查询条件 private Integer currentPage = 1; // 页码 private Integer pageSize = 10; // 每页大小 + @Pattern(regexp = "^[a-zA-Z_][a-zA-Z0-9_]*$", message = "排序字段格式不正确") private String orderBy; // 排序字段 + @Pattern(regexp = "ASC|DESC", message = "排序方式必须为ASC或DESC") private String orderType = "ASC"; // 排序方式 } diff --git a/base/src/main/java/com/tinyengine/it/dynamic/service/DynamicService.java b/base/src/main/java/com/tinyengine/it/dynamic/service/DynamicService.java index 27ba63c3..027922f5 100644 --- a/base/src/main/java/com/tinyengine/it/dynamic/service/DynamicService.java +++ b/base/src/main/java/com/tinyengine/it/dynamic/service/DynamicService.java @@ -37,12 +37,11 @@ public List query(DynamicQuery dto) { String tableName = getTableName(dto.getNameEn()); Map params = new HashMap<>(); params.put("tableName", tableName); - params.put("fields", dto.getFields()); params.put("conditions", dto.getParams()); + params.put("fields", dto.getFields()); params.put("pageNum", dto.getCurrentPage()); params.put("pageSize", dto.getPageSize()); - params.put("orderBy", dto.getOrderBy()); - params.put("orderType", dto.getOrderType()); + return dynamicDao.select(params); } @@ -78,6 +77,10 @@ public Map queryWithPage(DynamicQuery dto) { if( dto.getPageSize() == null || dto.getPageSize() <= 0) { dto.setPageSize(10); } + List fields = dto.getFields(); + // 验证字段列表 + validateFields(fields); + // 验证表和数据 validateTableExists(dto.getNameEn()); validateTableAndData(dto.getNameEn(), dto.getParams()); List list = query(dto); @@ -206,6 +209,7 @@ public List> getTableStructure(String tableName) { * 验证表和数据 */ private void validateTableAndData(String tableName, Map data) { + if (tableName == null || tableName.trim().isEmpty()) { throw new IllegalArgumentException("表名不能为空"); } @@ -222,7 +226,21 @@ private void validateTableAndData(String tableName, Map data) { // 验证字段名格式 for (String field : data.keySet()) { if (!field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) { - throw new IllegalArgumentException("字段名格式不正确: " + field); + throw new IllegalArgumentException("查询字段名格式不正确: " + field); + } + } + } + + /** + * 验证字段列表 + * @param fields + */ + private void validateFields(List fields) { + if (fields != null) { + for (String field : fields) { + if (!field.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) { + throw new IllegalArgumentException("Field name format is invalid: " + field); + } } } } diff --git a/base/src/main/java/com/tinyengine/it/login/config/LoginConfig.java b/base/src/main/java/com/tinyengine/it/login/config/LoginConfig.java index 7b29d4fc..0d78e04e 100644 --- a/base/src/main/java/com/tinyengine/it/login/config/LoginConfig.java +++ b/base/src/main/java/com/tinyengine/it/login/config/LoginConfig.java @@ -44,9 +44,7 @@ public void addInterceptors(InterceptorRegistry registry) { "/app-center/api/ai/chat", "/app-center/api/chat/completions", // 图片文件资源下载 - "/material-center/api/resource/download/*", - //模型驱动 - "/platform-center/api/model-data/**" + "/material-center/api/resource/download/*" ); } } diff --git a/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java b/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java index 42f7ed03..0f5fc62c 100644 --- a/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java +++ b/base/src/main/java/com/tinyengine/it/login/utils/JwtUtil.java @@ -43,12 +43,10 @@ public class JwtUtil { private TokenBlacklistService tokenBlacklistService; private static final long EXPIRATION_TIME = 21600000L; // 6小时 = 6 * 60 * 60 * 1000 = 21600000 毫秒 - private static final String DEFAULT_SECRET = "tiny-engine-backend-secret-key-at-jwt-login"; // 避免启动时环境变量未加载的问题 private static String getSecretString() { - return Optional.ofNullable(System.getenv("SECRET_STRING")) - .orElse(DEFAULT_SECRET); + return System.getenv("SECRET_STRING"); } public static SecretKey getSecretKey() { diff --git a/base/src/test/java/com/tinyengine/it/dynamic/service/DynamicModelServiceTest.java b/base/src/test/java/com/tinyengine/it/dynamic/service/DynamicModelServiceTest.java new file mode 100644 index 00000000..11f1d0ea --- /dev/null +++ b/base/src/test/java/com/tinyengine/it/dynamic/service/DynamicModelServiceTest.java @@ -0,0 +1,357 @@ +package com.tinyengine.it.dynamic.service; + +import cn.hutool.core.util.ReflectUtil; +import com.tinyengine.it.common.context.LoginUserContext; +import com.tinyengine.it.dynamic.dto.DynamicDelete; +import com.tinyengine.it.dynamic.dto.DynamicInsert; +import com.tinyengine.it.dynamic.dto.DynamicQuery; +import com.tinyengine.it.dynamic.dto.DynamicUpdate; +import com.tinyengine.it.model.dto.ParametersDto; +import com.tinyengine.it.model.entity.Model; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.support.KeyHolder; + +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + + +class DynamicModelServiceTest { + + @Mock + private JdbcTemplate jdbcTemplate; + + @Mock + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; + + @Mock + private LoginUserContext loginUserContext; + + @InjectMocks + private DynamicModelService dynamicModelService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + MockitoAnnotations.openMocks(this); + ReflectUtil.setFieldValue(dynamicModelService, "jdbcTemplate", jdbcTemplate); + ReflectUtil.setFieldValue(dynamicModelService, "loginUserContext", loginUserContext); + ReflectUtil.setFieldValue(dynamicModelService, "namedParameterJdbcTemplate", namedParameterJdbcTemplate); + + } + + + @Test + void createDynamicTable() { + // Arrange + Model model = new Model(); + model.setNameEn("test_table"); + ParametersDto parametersDto = new ParametersDto(); + parametersDto.setProp("name"); + parametersDto.setType("String"); + parametersDto.setRequired(true); + parametersDto.setDefaultValue("1"); + parametersDto.setDescription("1"); + model.setParameters(Collections.singletonList(parametersDto)); + + // Mock JdbcTemplate behavior + doNothing().when(jdbcTemplate).execute(anyString()); + + // Act & Assert + assertDoesNotThrow(() -> dynamicModelService.createDynamicTable(model)); + verify(jdbcTemplate, times(1)).execute(anyString()); + } + + @Test + void dropDynamicTable() { + // Arrange + Model model = new Model(); + model.setNameEn("test_table"); + + // Mock JdbcTemplate behavior + doNothing().when(jdbcTemplate).execute(anyString()); + + // Act & Assert + assertDoesNotThrow(() -> dynamicModelService.dropDynamicTable(model)); + verify(jdbcTemplate, times(1)).execute("DROP TABLE IF EXISTS dynamic_test_table;"); + } + + @Test + void initializeDynamicTable() { + // Arrange + Model model = new Model(); + model.setNameEn("test_table"); + ParametersDto param1 = new ParametersDto(); + param1.setProp("name"); + param1.setType("String"); + param1.setDefaultValue("default_name"); + param1.setRequired(true); + model.setParameters(Collections.singletonList(param1)); + + Long userId = 1L; + + // Mock JdbcTemplate behavior + when(jdbcTemplate.update(anyString(), any(Object[].class))).thenReturn(1); + + // Act & Assert + assertDoesNotThrow(() -> dynamicModelService.initializeDynamicTable(model, userId)); + verify(jdbcTemplate, times(1)).update(anyString(), any(Object[].class)); + } + + @Test + void dynamicQuery() { + // Arrange + String tableName = "test_table"; + List fields = Arrays.asList("id", "name"); + Map conditions = Map.of("id", 1); + String orderBy = "id DESC"; + Integer limit = 10; + + List> mockResult = new ArrayList<>(); + mockResult.add(Map.of("id", 1, "name", "test_name")); + + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(mockResult); + + // Act + List> result = dynamicModelService.dynamicQuery(tableName, fields, conditions, orderBy, limit); + + // Assert + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals("test_name", result.get(0).get("name")); + verify(namedParameterJdbcTemplate, times(1)).queryForList(anyString(), anyMap()); + } + + @Test + void dynamicCount() { + // Arrange + String tableName = "test_table"; + Map conditions = Map.of("id", 1); + + List> mockResult = new ArrayList<>(); + mockResult.add(Map.of("count", 5L)); + + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(mockResult); + + // Act + List> result = dynamicModelService.dynamicCount(tableName, conditions); + + // Assert + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(5L, result.get(0).get("count")); + verify(namedParameterJdbcTemplate, times(1)).queryForList(anyString(), anyMap()); + } + + @Test + void count() { + // Arrange + String tableName = "test_table"; + Map conditions = Map.of("id", 1); + + List> mockResult = new ArrayList<>(); + mockResult.add(Map.of("count", 10L)); + + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(mockResult); + + // Act + Long result = dynamicModelService.count(tableName, conditions); + + // Assert + assertNotNull(result); + assertEquals(10L, result); + verify(namedParameterJdbcTemplate, times(1)).queryForList(anyString(), anyMap()); + } + + @Test + void queryWithPage() { + // Arrange + DynamicQuery dto = new DynamicQuery(); + dto.setNameEn("test_table"); + dto.setFields(Arrays.asList("id", "name")); + dto.setParams(Map.of("id", 1)); + dto.setOrderBy("id DESC"); + dto.setCurrentPage(1); + dto.setPageSize(10); + + List> mockData = new ArrayList<>(); + mockData.add(Map.of("id", 1, "name", "test_name")); + + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(mockData); + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(List.of(Map.of("count", 1L))); + + // Act + Map result = dynamicModelService.queryWithPage(dto); + + // Assert + assertNotNull(result); + assertTrue((Boolean) result.get("success")); + assertEquals(1L, result.get("total")); + assertEquals(1, ((List) result.get("data")).size()); + verify(namedParameterJdbcTemplate, times(2)).queryForList(anyString(), anyMap()); + } + + + + @Test + void createData() { + // Arrange + DynamicInsert dataDto = new DynamicInsert(); + dataDto.setNameEn("test_table"); + dataDto.setParams(Map.of("name", "test")); + + when(loginUserContext.getLoginUserId()).thenReturn("1"); + when(jdbcTemplate.update(any(PreparedStatementCreator.class), any(KeyHolder.class))).thenAnswer(invocation -> { + KeyHolder keyHolder = invocation.getArgument(1); + keyHolder.getKeyList().add(Map.of("GENERATED_KEY", 1L)); + return 1; + }); + + // Act + Map result = dynamicModelService.createData(dataDto); + + // Assert + assertNotNull(result); + assertEquals(1L, result.get("id")); + verify(jdbcTemplate, times(1)).update(any(PreparedStatementCreator.class), any(KeyHolder.class)); + } + + @Test + void getDataById() { + // Arrange + String modelId = "test_table"; + Long id = 1L; + + List> mockResult = new ArrayList<>(); + mockResult.add(Map.of("id", 1, "name", "test_name")); + + when(jdbcTemplate.queryForList(anyString(), Optional.ofNullable(any()))).thenReturn(mockResult); + + // Act + Map result = dynamicModelService.getDataById(modelId, id); + + // Assert + assertNotNull(result); + assertEquals("test_name", result.get("name")); + verify(jdbcTemplate, times(1)).queryForList(anyString(), Optional.ofNullable(any())); + } + + @Test + void updateDateById() { + // Arrange + DynamicUpdate dto = new DynamicUpdate(); + dto.setNameEn("test_table"); + dto.setParams(Map.of("id", 1)); + dto.setData(Map.of("name", "updated_name")); + + when(jdbcTemplate.update(anyString(), any(Object[].class))).thenReturn(1); + + // Act + Map result = dynamicModelService.updateDateById(dto); + + // Assert + assertNotNull(result); + assertEquals(1, result.get("rowsAffected")); + verify(jdbcTemplate, times(1)).update(anyString(), any(Object[].class)); + } + + @Test + void deleteDataById() { + // Arrange + DynamicDelete dto = new DynamicDelete(); + dto.setNameEn("test_table"); + dto.setId(1); + + when(jdbcTemplate.update(anyString(), Optional.ofNullable(any()))).thenReturn(1); + + // Act + Map result = dynamicModelService.deleteDataById(dto); + + // Assert + assertNotNull(result); + assertEquals(1, result.get("rowsAffected")); + verify(jdbcTemplate, times(1)).update(anyString(), Optional.ofNullable(any())); + } + + + @Test + void testCreateDynamicTable() { + Model model = new Model(); + model.setNameEn("test_table"); + ParametersDto parametersDto = new ParametersDto(); + parametersDto.setProp("name"); + parametersDto.setType("String"); + parametersDto.setRequired(true); + parametersDto.setDefaultValue("1"); + parametersDto.setDescription("1"); + model.setParameters(Collections.singletonList(parametersDto)); + + doNothing().when(jdbcTemplate).execute(anyString()); + + assertDoesNotThrow(() -> dynamicModelService.createDynamicTable(model)); + verify(jdbcTemplate, times(1)).execute(anyString()); + } + + @Test + void testDropDynamicTable() { + Model model = new Model(); + model.setNameEn("test_table"); + + doNothing().when(jdbcTemplate).execute(anyString()); + + assertDoesNotThrow(() -> dynamicModelService.dropDynamicTable(model)); + verify(jdbcTemplate, times(1)).execute(anyString()); + } + + @Test + void testDynamicQuery() { + String tableName = "test_table"; + List fields = Arrays.asList("id", "name"); + Map conditions = Map.of("id", 1); + + when(namedParameterJdbcTemplate.queryForList(anyString(), anyMap())).thenReturn(new ArrayList<>()); + + List> result = dynamicModelService.dynamicQuery(tableName, fields, conditions, null, null); + assertNotNull(result); + verify(namedParameterJdbcTemplate, times(1)).queryForList(anyString(), anyMap()); + } + + @Test + void testCreateData() { + DynamicInsert dataDto = new DynamicInsert(); + dataDto.setNameEn("test_table"); + dataDto.setParams(Map.of("name", "test")); + + when(loginUserContext.getLoginUserId()).thenReturn("1"); + when(jdbcTemplate.update(any(), any(PreparedStatementCreator.class), any())).thenReturn(1); + + Map result = dynamicModelService.createData(dataDto); + assertNotNull(result); + verify(jdbcTemplate, times(1)).update(any(PreparedStatementCreator.class), any()); + } + + @Test + void testDeleteDataById() { + DynamicDelete dto = new DynamicDelete(); + dto.setNameEn("test_table"); + dto.setId(1); + + when(jdbcTemplate.update(anyString(), Optional.ofNullable(any()))).thenReturn(1); + + Map result = dynamicModelService.deleteDataById(dto); + assertEquals(1, result.get("rowsAffected")); + verify(jdbcTemplate, times(1)).update(anyString(), Optional.ofNullable(any())); + } + + +} \ No newline at end of file