diff --git a/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java b/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java index 3a939c0..9be63eb 100644 --- a/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java +++ b/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java @@ -1484,18 +1484,45 @@ public class FormatUtil { /** * 格式化单个键 + * + * 根据指定的格式类型对键名进行格式化转换,支持多种命名风格之间的转换。 + * + *

支持的格式类型:

+ * + * + *

转换规则说明:

+ * + * + * @param key 原始键名字符串(可为null或空字符串) + * @param format 格式类型字符串,指定目标命名风格 + * @return 格式化后的键名,若原始键名为null则返回null */ private static String formatKey(String key, String format) { switch (format.toLowerCase()) { case "uppercase": + // 转换为全大写(如"userName" → "USERNAME") return key.toUpperCase(); case "lowercase": + // 转换为全小写(如"UserName" → "username") return key.toLowerCase(); case "camelcase": + // 转换为小驼峰命名(如"user_name" → "userName") return toCamelCase(key); case "snakecase": + // 转换为蛇形命名(如"userName" → "user_name") return toSnakeCase(key); case "kebabcase": + // 转换为短横线命名(如"userName" → "user-name") return toKebabCase(key); default: return key; @@ -1526,8 +1553,11 @@ public class FormatUtil { */ private static String toSnakeCase(String str) { if (str == null || str.isEmpty()) return str; - - return str.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); + + String processResult = str.replaceAll("-+", "_"); + processResult = processResult.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); + processResult = processResult.replaceAll("([a-zA-Z0-9]+)(_+([a-zA-Z0-9]+))+", "$1_$3").toLowerCase(); + return processResult; } /** @@ -1535,8 +1565,9 @@ public class FormatUtil { */ private static String toKebabCase(String str) { if (str == null || str.isEmpty()) return str; - - return str.replaceAll("([a-z])([A-Z])", "$1-$2").toLowerCase(); + + String processResult = str.replaceAll("_+", "-"); + return processResult.replaceAll("([a-z])([A-Z])", "$1-$2").toLowerCase(); } /** diff --git a/src/test/java/com/example/demo/FormatUtilTest.java b/src/test/java/com/example/demo/FormatUtilTest.java index 0731bed..2fc6871 100644 --- a/src/test/java/com/example/demo/FormatUtilTest.java +++ b/src/test/java/com/example/demo/FormatUtilTest.java @@ -448,7 +448,7 @@ public class FormatUtilTest { *

预计结果:返回空字符串或错误信息

*/ @Test - @DisplayName("substring - 起始位置超界") + @DisplayName("substring - 起始位置超界 - 【验证中】") void testSubstring_StartOutOfBounds() { System.out.println(); log.info("========== 字符串截取模块测试 - 起始位置超界 =========="); @@ -713,16 +713,13 @@ public class FormatUtilTest { replacementMap.put("Hello", "Hi"); replacementMap.put("world", "universe"); boolean caseSensitive = true; - log.info("输入参数:"); log.info(" - 输入字符串: {}", inputString); log.info(" - 替换映射: {}", JSON.toJSONString(replacementMap)); log.info(" - 大小写敏感: {}", caseSensitive); log.info(" - 注意: 'Hello'与'hello'大小写不匹配"); - // 执行测试 String result = FormatUtil.stealBeamsAndReplacePillars(inputString, replacementMap, caseSensitive); - // 断言验证 String expectedResult = "hello universe"; log.info("预期结果:'{}'", expectedResult); @@ -792,24 +789,19 @@ public class FormatUtilTest { String coordinate = "116°23'45.67\""; params.put("fromType", "dms"); params.put("toType", "decimal"); - log.info("输入参数:"); log.info(" - 坐标值: {}", coordinate); log.info(" - 源格式: {}", params.get("fromType")); log.info(" - 目标格式: {}", params.get("toType")); log.info(" - 注意: 116°23'45.67\" ≈ 116.396019"); - - // 预期结果 - log.info("预期结果: 非空转换结果"); - // 执行测试 Object result = FormatUtil.coordinateConvert(coordinate, params); - - // 实际结果 - log.info("实际结果: {}", result); - // 断言验证 - assertNotNull(result); + String expectedResult = "116.396019"; + log.info("预期结果:'{}'", expectedResult); + log.info("实际结果:'{}'", result); + assertEquals(expectedResult, result); + log.info("✓ 测试通过 - 度分秒转十进制转换正确"); } @@ -974,7 +966,7 @@ public class FormatUtilTest { *

预计结果:返回最大值

*/ @Test - @DisplayName("boundaryCheck - 超出上限截断") + @DisplayName("boundaryCheck - 超出上限截断 - 【验证中】") void testBoundaryCheck_AboveMaxTruncate() { System.out.println(); log.info("========== 极值检查模块测试 - 超出上限截断处理 =========="); @@ -1100,34 +1092,128 @@ public class FormatUtilTest { *

预计结果:转换结果正确

*/ @Test - @DisplayName("kvMapping - 键名格式转换 - 【验证中】") + @DisplayName("kvMapping - 键名格式转换 - 【已验证】") void testKvMapping_KeyFormatConversion() { System.out.println(); log.info("========== 数据KV映射模块测试 - 键名格式转换 =========="); // 输入参数 Map inputMap = new HashMap<>(); - inputMap.put("user_name", "张三"); - inputMap.put("user_age", 25); - params.put("keyFormat", "snake_to_camel"); + + // =================== 全大写命名 =================== + inputMap.put("USER_ID", 1001); + inputMap.put("STATUS", "ACTIVE"); + // =================== 全小写命名 =================== + inputMap.put("name", "李四"); + inputMap.put("address", "北京市朝阳区"); + inputMap.put("phone", "13800138000"); + // =================== 小驼峰命名 (lowerCamelCase) =================== + inputMap.put("userName", "王五"); + // =================== 大驼峰命名 (UpperCamelCase/PascalCase) =================== + inputMap.put("UserRole", "ADMIN"); + // =================== 蛇形命名 (snake_case) =================== + inputMap.put("user_password", "encrypted123"); + // =================== 短横线命名 (kebab-case) =================== + inputMap.put("user-address", "上海市浦东新区"); + + // 1. 转成全大写 + System.out.println(); + log.info("========== 转成全大写 =========="); + params.put("keyFormat", "uppercase"); log.info("输入参数:"); log.info(" - 输入Map: {}", inputMap); log.info(" - 键名格式转换: {}", params.get("keyFormat")); - log.info(" - 注意: 蛇形命名(user_name)转驼峰命名(userName)"); - // 预期结果 - log.info("预期结果: 非空转换结果"); + log.info(" - 注意: 短横杠在全大写中,不做处理"); // 执行测试 Object result = FormatUtil.kvMapping(inputMap, params); - // 实际结果 - Map expectedResult = new HashMap<>(); - expectedResult.put("userName", "张三"); - expectedResult.put("userAge", 25); - String expectedResultString = JSON.toJSONString(expectedResult); + // 断言验证 + String expectedResultString = """ + {"USERROLE":"ADMIN","STATUS":"ACTIVE","USER-ADDRESS":"上海市浦东新区","USER_PASSWORD":"encrypted123","ADDRESS":"北京市朝阳区","PHONE":"13800138000","USER_ID":1001,"USERNAME":"王五","NAME":"李四"} + """.trim(); String resultString = JSON.toJSONString(result); log.info("预期结果: {}", expectedResultString); log.info("实际结果: {}", resultString); + assertEquals(expectedResultString, resultString, String.format("转换为全大写%s失败", params.get("keyFormat"))); + + // 2. 转成全小写 + System.out.println(); + log.info("========== 转成全小写 =========="); + params.put("keyFormat", "lowercase"); + log.info("输入参数:"); + log.info(" - 输入Map: {}", inputMap); + log.info(" - 键名格式转换: {}", params.get("keyFormat")); + log.info(" - 注意: 短横杠在全小写中,不做处理"); + // 执行测试 + Object result2 = FormatUtil.kvMapping(inputMap, params); // 断言验证 - assertEquals(expectedResultString, resultString, "蛇形命名(user_name)转驼峰命名(userName) 失败"); + String expectedResultString2 = """ + {"user_password":"encrypted123","address":"北京市朝阳区","phone":"13800138000","user_id":1001,"name":"李四","userrole":"ADMIN","user-address":"上海市浦东新区","status":"ACTIVE","username":"王五"} + """.trim(); + String resultString2 = JSON.toJSONString(result2); + log.info("预期结果: {}", expectedResultString2); + log.info("实际结果: {}", resultString2); + assertEquals(expectedResultString2, resultString2, String.format("转换为全小写%s失败", params.get("keyFormat"))); + + + // 3. 转换为小驼峰命名 + System.out.println(); + log.info("========== 转换为小驼峰命名 =========="); + params.put("keyFormat", "camelcase"); + log.info("输入参数:"); + log.info(" - 输入Map: {}", inputMap); + log.info(" - 键名格式转换: {}", params.get("keyFormat")); + log.info(" - 注意: 所有值都被转为驼峰,不区分当前是否大小写,只判断 [ _ | - | ' '] 下划线、短横杠、空串三种。"); + // 执行测试 + Object result3 = FormatUtil.kvMapping(inputMap, params); + // 断言验证 + String expectedResultString3 = """ + {"userAddress":"上海市浦东新区","userPassword":"encrypted123","address":"北京市朝阳区","phone":"13800138000","name":"李四","userrole":"ADMIN","userId":1001,"status":"ACTIVE","username":"王五"} + """.trim(); + String resultString3 = JSON.toJSONString(result3); + log.info("预期结果: {}", expectedResultString3); + log.info("实际结果: {}", resultString3); + assertEquals(expectedResultString3, resultString3, String.format("转换为小驼峰命名%s失败", params.get("keyFormat"))); + + + // 4. 转换为蛇形命名 + System.out.println(); + log.info("========== 转换为蛇形命名 =========="); + params.put("keyFormat", "snakecase"); + log.info("输入参数:"); + log.info(" - 输入Map: {}", inputMap); + log.info(" - 键名格式转换: {}", params.get("keyFormat")); + log.info(" - 注意: 短横杠在蛇形命名中,不做处理"); + // 执行测试 + Object result4 = FormatUtil.kvMapping(inputMap, params); + // 断言验证 + String expectedResultString4 = """ + {"user_role":"ADMIN","user_password":"encrypted123","address":"北京市朝阳区","phone":"13800138000","user_id":1001,"user_name":"王五","user_address":"上海市浦东新区","name":"李四","status":"ACTIVE"} + """.trim(); + String resultString4 = JSON.toJSONString(result4); + log.info("预期结果: {}", expectedResultString4); + log.info("实际结果: {}", resultString4); + assertEquals(expectedResultString4, resultString4, String.format("转换为蛇形命名%s失败", params.get("keyFormat"))); + + + // 5. 转换为短横线命名 + System.out.println(); + log.info("========== 转换为短横线命名 =========="); + params.put("keyFormat", "kebabcase"); + log.info("输入参数:"); + log.info(" - 输入Map: {}", inputMap); + log.info(" - 键名格式转换: {}", params.get("keyFormat")); + log.info(" - 注意: 短横杠在全大写中,不做处理"); + // 执行测试 + Object result5 = FormatUtil.kvMapping(inputMap, params); + // 断言验证 + String expectedResultString5 = """ + {"user-password":"encrypted123","address":"北京市朝阳区","user-role":"ADMIN","user-name":"王五","phone":"13800138000","user-id":1001,"name":"李四","user-address":"上海市浦东新区","status":"ACTIVE"} + """.trim(); + String resultString5 = JSON.toJSONString(result5); + log.info("预期结果: {}", expectedResultString5); + log.info("实际结果: {}", resultString5); + assertEquals(expectedResultString5, resultString5, String.format("转换为短横线命名%s失败", params.get("keyFormat"))); + log.info("✓ 测试通过 - 键名格式转换正确"); } @@ -1271,7 +1357,7 @@ public class FormatUtilTest { *

预计结果:组合处理结果正确

*/ @Test - @DisplayName("综合测试 - 多方法组合使用") + @DisplayName("boundaryCheck - 综合测试 - 多方法组合使用 - 【验证中】") void testMultipleMethods_CombinedUsage() { System.out.println(); log.info("========== 综合测试 - 多方法组合使用 =========="); @@ -1334,7 +1420,7 @@ public class FormatUtilTest { *

预计结果:所有转换正确完成

*/ @Test - @DisplayName("coordinateConvert - 性能测试 - 大量数据处理") + @DisplayName("coordinateConvert - 性能测试 - 大量数据处理 - 【验证中】") void testPerformance_LargeDataProcessing() { System.out.println(); log.info("========== 性能测试 - 大量数据处理 =========="); @@ -1347,11 +1433,9 @@ public class FormatUtilTest { log.info(" - 目标格式: decimal"); log.info(" - 坐标模式: 116.0°23'45.67\" ~ 116.999°23'45.67\""); log.info(" - 性能要求: 处理时间 < 5000 ms"); - // 开始性能测试 long startTime = System.currentTimeMillis(); log.info("开始性能测试..."); - for (int i = 0; i < testCount; i++) { Map coordParams = new HashMap<>(); coordParams.put("fromType", "dms"); @@ -1360,22 +1444,19 @@ public class FormatUtilTest { Object result = FormatUtil.coordinateConvert("116." + i + "°23'45.67\"", coordParams); assertNotNull(result); } - long endTime = System.currentTimeMillis(); long duration = endTime - startTime; - // 实际结果 log.info("性能测试结果:"); log.info(" - 开始时间: {} ms", startTime); log.info(" - 结束时间: {} ms", endTime); log.info(" - 总耗时: {} ms", duration); log.info(" - 平均每个转换耗时: {} ms", (double)duration / testCount); - // 预期结果 - log.info("预期结果: 处理时间 < 5000 ms"); - + log.info("预期结果: 处理时间 < 100 ms"); // 断言验证 - assertTrue(duration < 5000, "性能测试应在5秒内完成"); + assertTrue(duration < 100, "性能测试应在0.1秒内完成"); + log.info("✓ 测试通过 - 性能表现符合要求"); } @@ -1420,7 +1501,7 @@ public class FormatUtilTest { *

预计结果:正确处理或返回错误信息

*/ @Test - @DisplayName("边界值测试 - 极值输入") + @DisplayName("boundaryCheck - 边界值测试 - 极值输入 - 【验证中】") void testBoundaryValues_ExtremeInputs() { System.out.println(); log.info("========== 边界值测试 - 极值输入 ==========");