From 5c0681e37e582590b8a25cc87dac40930155b8e4 Mon Sep 17 00:00:00 2001 From: jade Date: Thu, 9 Jul 2026 13:40:12 +0900 Subject: [PATCH] feat(common, other): add GlowMciFieldInfo and List parsing support --- .../io/shinhanlife/glow/GlowMciFieldInfo.java | 29 +++++ .../shinhanlife/glow/util/GlowMciParser.java | 105 ++++++++++++++++++ .../biz/mcp/tool/dto/MciSampleStringRes.java | 32 ++++++ .../biz/mcp/tool/dto/MciSampleTargetDto.java | 20 ++++ .../tool/service/SampleStringToolService.java | 7 +- 5 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 axhub-common/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java create mode 100644 axhub-common/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java create mode 100644 axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleStringRes.java create mode 100644 axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleTargetDto.java diff --git a/axhub-common/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java b/axhub-common/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java new file mode 100644 index 0000000..4755766 --- /dev/null +++ b/axhub-common/src/main/java/io/shinhanlife/glow/GlowMciFieldInfo.java @@ -0,0 +1,29 @@ +package io.shinhanlife.glow; + +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface GlowMciFieldInfo { + + /** + * 필드 순서 + */ + int order(); + + /** + * 필드 길이 (List인 경우 전체 길이로 활용될 수 있음) + */ + int length(); + + /** + * 필드 설명 + */ + String description() default ""; + + /** + * List 매핑 시 대상 DTO 클래스 + */ + Class target() default void.class; +} diff --git a/axhub-common/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java b/axhub-common/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java new file mode 100644 index 0000000..6955a38 --- /dev/null +++ b/axhub-common/src/main/java/io/shinhanlife/glow/util/GlowMciParser.java @@ -0,0 +1,105 @@ +package io.shinhanlife.glow.util; + +import io.shinhanlife.glow.GlowMciFieldInfo; +import lombok.extern.slf4j.Slf4j; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * @package io.shinhanlife.glow.util + * @className GlowMciParser + * @description MCI 고정 길이 전문 파싱 유틸리티 (GlowMciFieldInfo 기반) + * @author 김형식 + * @create 2026.09.01 + */ +@Slf4j +public class GlowMciParser { + + public static T parse(String mciString, Class clazz) { + if (mciString == null || mciString.isEmpty()) { + return null; + } + + try { + T instance = clazz.getDeclaredConstructor().newInstance(); + + List fields = new ArrayList<>(); + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(GlowMciFieldInfo.class)) { + fields.add(field); + } + } + + fields.sort(Comparator.comparingInt(f -> f.getAnnotation(GlowMciFieldInfo.class).order())); + + int currentIndex = 0; + for (Field field : fields) { + GlowMciFieldInfo annotation = field.getAnnotation(GlowMciFieldInfo.class); + int length = annotation.length(); + + if (currentIndex >= mciString.length()) { + break; + } + + int endIndex = Math.min(currentIndex + length, mciString.length()); + String value = mciString.substring(currentIndex, endIndex); + + field.setAccessible(true); + setFieldValue(instance, field, value, annotation); + + currentIndex += length; + } + + return instance; + } catch (Exception e) { + log.error("GlowMciFieldInfo 파싱 중 오류 발생: {}", e.getMessage(), e); + throw new RuntimeException("MCI 전문 파싱 오류", e); + } + } + + private static void setFieldValue(Object instance, Field field, String value, GlowMciFieldInfo annotation) throws IllegalAccessException { + Class fieldType = field.getType(); + String trimmedValue = value.trim(); + + if (fieldType == String.class) { + field.set(instance, trimmedValue); + } else if (fieldType == int.class || fieldType == Integer.class) { + field.set(instance, trimmedValue.isEmpty() ? 0 : Integer.parseInt(trimmedValue)); + } else if (fieldType == long.class || fieldType == Long.class) { + field.set(instance, trimmedValue.isEmpty() ? 0L : Long.parseLong(trimmedValue)); + } else if (fieldType == boolean.class || fieldType == Boolean.class) { + field.set(instance, Boolean.parseBoolean(trimmedValue)); + } else if (fieldType == double.class || fieldType == Double.class) { + field.set(instance, trimmedValue.isEmpty() ? 0.0 : Double.parseDouble(trimmedValue)); + } else if (List.class.isAssignableFrom(fieldType)) { + Class targetClass = annotation.target(); + if (targetClass != void.class) { + List list = new ArrayList<>(); + int itemLength = calculateTotalLength(targetClass); + if (itemLength > 0) { + for (int i = 0; i < value.length(); i += itemLength) { + int end = Math.min(i + itemLength, value.length()); + String itemStr = value.substring(i, end); + if (itemStr.trim().isEmpty()) continue; + list.add(parse(itemStr, targetClass)); + } + } + field.set(instance, list); + } + } else { + field.set(instance, trimmedValue); + } + } + + private static int calculateTotalLength(Class clazz) { + int total = 0; + for (Field field : clazz.getDeclaredFields()) { + if (field.isAnnotationPresent(GlowMciFieldInfo.class)) { + total += field.getAnnotation(GlowMciFieldInfo.class).length(); + } + } + return total; + } +} diff --git a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleStringRes.java b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleStringRes.java new file mode 100644 index 0000000..d0deb71 --- /dev/null +++ b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleStringRes.java @@ -0,0 +1,32 @@ +package io.shinhanlife.axhub.biz.mcp.tool.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.shinhanlife.glow.GlowMciFieldInfo; +import lombok.Data; +import java.util.List; + +/** + * @package io.shinhanlife.axhub.biz.mcp.tool.dto + * @className MciSampleStringRes + * @description AX HUB 시스템 처리 클래스 + * @author 김형식 + * @create 2026.09.01 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class MciSampleStringRes { + @GlowMciFieldInfo(order = 1, length = 10, description = "이름") + private String name; + + @GlowMciFieldInfo(order = 2, length = 3, description = "나이") + private int age; + + @GlowMciFieldInfo(order = 3, length = 8, description = "가입일자(YYYYMMDD)") + private String joinDate; + + @GlowMciFieldInfo(order = 4, length = 2, description = "상태코드") + private String statusCode; + + @GlowMciFieldInfo(order = 5, length = 30, description = "타겟 리스트", target = MciSampleTargetDto.class) + private List targetList; +} diff --git a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleTargetDto.java b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleTargetDto.java new file mode 100644 index 0000000..7bf0b97 --- /dev/null +++ b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/dto/MciSampleTargetDto.java @@ -0,0 +1,20 @@ +package io.shinhanlife.axhub.biz.mcp.tool.dto; + +import io.shinhanlife.glow.GlowMciFieldInfo; +import lombok.Data; + +/** + * @package io.shinhanlife.axhub.biz.mcp.tool.dto + * @className MciSampleTargetDto + * @description AX HUB 시스템 처리 클래스 + * @author 김형식 + * @create 2026.09.01 + */ +@Data +public class MciSampleTargetDto { + @GlowMciFieldInfo(order = 1, length = 5, description = "항목 코드") + private String itemCode; + + @GlowMciFieldInfo(order = 2, length = 5, description = "항목 값") + private String itemValue; +} diff --git a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/SampleStringToolService.java b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/SampleStringToolService.java index 26c42d9..ccad09e 100644 --- a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/SampleStringToolService.java +++ b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/SampleStringToolService.java @@ -2,9 +2,8 @@ package io.shinhanlife.axhub.biz.mcp.tool.service; import io.shinhanlife.axhub.biz.mcp.tool.annotation.McpFunction; import io.shinhanlife.axhub.biz.mcp.tool.annotation.McpTool; +import io.shinhanlife.axhub.biz.mcp.tool.dto.MciSampleStringRes; import io.shinhanlife.axhub.biz.mcp.tool.dto.SampleStringReq; -import io.shinhanlife.axhub.biz.mcp.tool.dto.SampleStringRes; -import io.shinhanlife.glow.util.GlowTrgmParser; import org.springframework.stereotype.Service; /** @@ -44,7 +43,7 @@ public class SampleStringToolService extends AbstractMcpToolService { String rawStringResponse = (String) result.get("legacy_response"); - // 2. 받아온 고정 길이 전문(String)을 GlowTrgmParser를 이용해 DTO로 파싱합니다. - return GlowTrgmParser.parse(rawStringResponse, SampleStringRes.class); + // 2. 받아온 고정 길이 전문(String)을 GlowMciParser를 이용해 DTO로 파싱합니다. + return io.shinhanlife.glow.util.GlowMciParser.parse(rawStringResponse, MciSampleStringRes.class); } }