diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableApi.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableApi.java index 706a7ac0b..920a5f1f3 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableApi.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableApi.java @@ -3,8 +3,7 @@ import static in.koreatech.koin.admin.history.enums.DomainType.SHUTTLE_BUS; import static in.koreatech.koin.domain.user.model.UserType.ADMIN; import static in.koreatech.koin.global.code.ApiResponseCode.*; - -import java.util.List; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -36,8 +35,8 @@ public interface AdminShuttleBusTimetableApi { }) @Operation(summary = "엑셀 파일을 업로드하여 파싱된 데이터를 미리보기 한다.") @AdminActivityLogging(domain = SHUTTLE_BUS) - @PostMapping("/excel") - ResponseEntity> previewShuttleBusTimetable( + @PostMapping(value = "/excel", consumes = MULTIPART_FORM_DATA_VALUE) + ResponseEntity uploadTimetableExcelForPreview( @Auth(permit = {ADMIN}) Integer adminId, @RequestParam(name = "shuttle-bus-timetable") MultipartFile file ); diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableController.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableController.java index 01bdd1e1f..63987e06e 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableController.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/controller/AdminShuttleBusTimetableController.java @@ -2,8 +2,7 @@ import static in.koreatech.koin.admin.history.enums.DomainType.SHUTTLE_BUS; import static in.koreatech.koin.domain.user.model.UserType.ADMIN; - -import java.util.List; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -33,13 +32,12 @@ public class AdminShuttleBusTimetableController implements AdminShuttleBusTimeta private final AdminShuttleBusService adminShuttleBusService; @AdminActivityLogging(domain = SHUTTLE_BUS) - @PostMapping("/excel") - public ResponseEntity> previewShuttleBusTimetable( + @PostMapping(value = "/excel", consumes = MULTIPART_FORM_DATA_VALUE) + public ResponseEntity uploadTimetableExcelForPreview( @Auth(permit = {ADMIN}) Integer adminId, @RequestParam(name = "shuttle-bus-timetable") MultipartFile file ) { - List response = adminShuttleBusExcelService - .previewShuttleBusTimetable(file); + AdminShuttleBusTimetableResponse response = adminShuttleBusExcelService.getShuttleBusTimetablePreview(file); return ResponseEntity.ok(response); } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/request/AdminShuttleBusUpdateRequest.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/request/AdminShuttleBusUpdateRequest.java index f97d91e26..a264193af 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/request/AdminShuttleBusUpdateRequest.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/request/AdminShuttleBusUpdateRequest.java @@ -57,7 +57,9 @@ public List toRouteInfoEntity() { return routeInfo.stream() .map(innerRouteInfoRequest -> InnerRouteInfoRequest.toEntity( - innerRouteInfoRequest.name, innerRouteInfoRequest.detail, innerRouteInfoRequest.arrivalTime + innerRouteInfoRequest.name, + innerRouteInfoRequest.detail, + innerRouteInfoRequest.arrivalTime ) ).toList(); } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/response/AdminShuttleBusTimetableResponse.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/response/AdminShuttleBusTimetableResponse.java index 1acc96c9a..06e3a99b3 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/response/AdminShuttleBusTimetableResponse.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/dto/response/AdminShuttleBusTimetableResponse.java @@ -13,73 +13,84 @@ import lombok.Builder; @JsonNaming(SnakeCaseStrategy.class) -@Builder public record AdminShuttleBusTimetableResponse( - @Schema(description = "운행 지역", example = "CHEONAN_ASAN", requiredMode = REQUIRED) - String region, - - @Schema(description = "노선 타입", example = "SHUTTLE", requiredMode = REQUIRED) - String routeType, - - @Schema(description = "노선 이름", example = "천안 셔틀", requiredMode = REQUIRED) - String routeName, - - @Schema(description = "노선 부제목", example = "토요일, 일요일", requiredMode = NOT_REQUIRED) - String subName, - - @Schema(description = "정류소 정보 리스트") - List nodeInfo, - - @Schema(description = "회차별 도착 시간 및 운행 요일 정보 리스트") - List routeInfo + @Schema(description = "셔틀 버스 시간표 정보 리스트") + List shuttleBusTimetables ) { - - public static AdminShuttleBusTimetableResponse from(ShuttleBusTimetable table) { - List nodeInfos = table.getNodeInfos().stream() - .map(n -> new AdminShuttleBusTimetableResponse.NodeInfo( - n.getName(), - n.getDetail() - )) - .toList(); - - List routeInfos = table.getRouteInfos().stream() - .map(r -> new AdminShuttleBusTimetableResponse.RouteInfo( - r.getName(), - r.getDetail(), - r.getArrivalTime() - )) - .toList(); - - return AdminShuttleBusTimetableResponse.builder() - .nodeInfo(nodeInfos) - .region(table.getRegion()) - .routeInfo(routeInfos) - .routeName(table.getRouteName()) - .routeType(table.getRouteType()) - .subName(table.getSubName()) - .build(); - } - @JsonNaming(SnakeCaseStrategy.class) - public record NodeInfo( - @Schema(description = "정류소 이름", example = "한기대", requiredMode = REQUIRED) - String name, + @Builder + public record InnerAdminShuttleBusTimetableResponse( + @Schema(description = "운행 지역", example = "CHEONAN_ASAN", requiredMode = REQUIRED) + String region, - @Schema(description = "정류소 이름 추가 설명 (없으면 null)", example = "학화호두과자 앞", requiredMode = NOT_REQUIRED) - String detail - ) { - } + @Schema(description = "노선 타입", example = "SHUTTLE", requiredMode = REQUIRED) + String routeType, - @JsonNaming(SnakeCaseStrategy.class) - public record RouteInfo( - @Schema(description = "회차 이름", example = "1회", requiredMode = REQUIRED) - String name, + @Schema(description = "노선 이름", example = "천안 셔틀", requiredMode = REQUIRED) + String routeName, + + @Schema(description = "노선 부제목", example = "토요일, 일요일", requiredMode = NOT_REQUIRED) + String subName, - @Schema(description = "회차 세부 이름", example = "(청주역→본교)", requiredMode = NOT_REQUIRED) - String detail, + @Schema(description = "정류소 정보 리스트") + List nodeInfo, - @Schema(description = "각 정류소 별 도착 시간 (미정차인 경우 null)", requiredMode = REQUIRED) - List arrivalTime + @Schema(description = "회차별 도착 시간 및 운행 요일 정보 리스트") + List routeInfo ) { + public static InnerAdminShuttleBusTimetableResponse from(ShuttleBusTimetable table) { + List nodeInfo = table.getNodeInfos() + .stream() + .map(n -> new InnerNodeInfoResponse( + n.getName(), + n.getDetail() + )) + .toList(); + + List routeInfo = table.getRouteInfos() + .stream() + .map(r -> new InnerRouteInfoResponse( + r.getName(), + r.getDetail(), + r.getArrivalTime() + )) + .toList(); + + return InnerAdminShuttleBusTimetableResponse.builder() + .nodeInfo(nodeInfo) + .region(table.getRegion()) + .routeInfo(routeInfo) + .routeName(table.getRouteName()) + .routeType(table.getRouteType()) + .subName(table.getSubName()) + .build(); + } + + @JsonNaming(SnakeCaseStrategy.class) + public record InnerNodeInfoResponse( + @Schema(description = "정류소 이름", example = "한기대", requiredMode = REQUIRED) + String name, + + @Schema(description = "정류소 이름 추가 설명 (없으면 null)", example = "학화호두과자 앞", requiredMode = NOT_REQUIRED) + String detail + ) { + } + + @JsonNaming(SnakeCaseStrategy.class) + public record InnerRouteInfoResponse( + @Schema(description = "회차 이름", example = "1회", requiredMode = REQUIRED) + String name, + + @Schema(description = "회차 세부 이름", example = "(청주역→본교)", requiredMode = NOT_REQUIRED) + String detail, + + @Schema( + description = "각 정류소 별 도착 시간 (미정차인 경우 null)", + example = "[\"08:00\", \"09:00\"]", + requiredMode = REQUIRED + ) + List arrivalTime + ) { + } } } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/PoiCellExtractor.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/PoiCellExtractor.java new file mode 100644 index 000000000..2548ea477 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/PoiCellExtractor.java @@ -0,0 +1,15 @@ +package in.koreatech.koin.admin.bus.shuttle.extractor; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; + +public class PoiCellExtractor { + + private static final DataFormatter FORMATTER = new DataFormatter(); + + public static String extractStringValue(Cell cell) { + String value = FORMATTER.formatCellValue(cell); + + return value != null ? value.trim() : ""; + } +} diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusMetaDataExtractor.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusMetaDataExtractor.java new file mode 100644 index 000000000..05186e43f --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusMetaDataExtractor.java @@ -0,0 +1,49 @@ +package in.koreatech.koin.admin.bus.shuttle.extractor; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import in.koreatech.koin.admin.bus.shuttle.model.RouteName; +import in.koreatech.koin.admin.bus.shuttle.model.RouteType; +import in.koreatech.koin.admin.bus.shuttle.model.SubName; +import in.koreatech.koin.domain.bus.enums.ShuttleBusRegion; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ShuttleBusMetaDataExtractor { + + private final Sheet sheet; + + private static final int REGION_ROW = 0; + private static final int REGION_COL = 1; + + private static final int ROUTE_TYPE_ROW = 1; + private static final int ROUTE_TYPE_COL = 1; + + public ShuttleBusRegion extractRegion() { + Row row = sheet.getRow(REGION_ROW); + Cell cell = row.getCell(REGION_COL); + + return ShuttleBusRegion.of(PoiCellExtractor.extractStringValue(cell)); + } + + public RouteType extractRouteType() { + Row row = sheet.getRow(ROUTE_TYPE_ROW); + Cell cell = row.getCell(ROUTE_TYPE_COL); + + return RouteType.of(PoiCellExtractor.extractStringValue(cell)); + } + + public RouteName extractRouteName() { + String sheetName = sheet.getSheetName(); + + return RouteName.of(sheetName); + } + + public SubName extractSubName() { + String sheetName = sheet.getSheetName(); + + return SubName.of(sheetName); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusNodeInfoParser.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusNodeInfoExtractor.java similarity index 61% rename from src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusNodeInfoParser.java rename to src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusNodeInfoExtractor.java index 6b2abd0d7..9d40a7bff 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusNodeInfoParser.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusNodeInfoExtractor.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.bus.shuttle.util; +package in.koreatech.koin.admin.bus.shuttle.extractor; import static in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.NodeInfo; @@ -10,12 +10,17 @@ import org.apache.poi.ss.usermodel.Sheet; import org.springframework.util.StringUtils; -public class ShuttleBusNodeInfoParser { +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ShuttleBusNodeInfoExtractor { + + private final Sheet sheet; private static final int START_BUS_STOP_ROW = 5; private static final int START_BUS_STOP_COL = 0; - public static List getNodeInfos(Sheet sheet) { + public List extractNodeInfos() { List nodeInfos = new ArrayList<>(); for (int i = START_BUS_STOP_ROW; i <= sheet.getLastRowNum(); i++) { @@ -26,18 +31,15 @@ public static List getNodeInfos(Sheet sheet) { } Cell cell = row.getCell(START_BUS_STOP_COL); - String nameWithDetail = ExcelStringUtil.getCellValueToString(cell); + String nameWithDetail = (cell == null) ? "" : PoiCellExtractor.extractStringValue(cell); - if (cell == null || !StringUtils.hasText(nameWithDetail)) { + if (!StringUtils.hasText(nameWithDetail)) { break; } nameWithDetail = nameWithDetail.trim(); - String name = ExcelStringUtil.extractNameWithoutBrackets(nameWithDetail); - String detail = ExcelStringUtil.extractDetailFromBrackets(nameWithDetail); - - nodeInfos.add(NodeInfo.of(name, detail)); + nodeInfos.add(NodeInfo.of(nameWithDetail)); } return nodeInfos; diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusRouteInfoParser.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusRouteInfoExtractor.java similarity index 81% rename from src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusRouteInfoParser.java rename to src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusRouteInfoExtractor.java index 8c6f06b4c..e39f0baa9 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusRouteInfoParser.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/extractor/ShuttleBusRouteInfoExtractor.java @@ -1,4 +1,4 @@ -package in.koreatech.koin.admin.bus.shuttle.util; +package in.koreatech.koin.admin.bus.shuttle.extractor; import static in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.RouteInfo; import static in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.RouteInfo.InnerNameDetail; @@ -15,8 +15,13 @@ import in.koreatech.koin.admin.bus.shuttle.enums.RunningDays; import in.koreatech.koin.admin.bus.shuttle.model.ArrivalTime; +import in.koreatech.koin.admin.bus.shuttle.util.ExcelRangeUtil; +import lombok.RequiredArgsConstructor; -public class ShuttleBusRouteInfoParser { +@RequiredArgsConstructor +public class ShuttleBusRouteInfoExtractor { + + private final Sheet sheet; private static final int START_HEADER_ROW = 3; private static final int START_DETAIL_ROW = 4; @@ -24,14 +29,14 @@ public class ShuttleBusRouteInfoParser { private static final int START_COL = 1; - public static List getRouteInfos(Sheet sheet) { - List innerNameDetails = extractRouteNameDetails(sheet); + public List extractRouteInfos() { + List innerNameDetails = extractRouteNameDetails(); List runningDays = innerNameDetails.stream() .map(RunningDays::from) .toList(); - List arrivalTimes = extractArrivalTimes(sheet); + List arrivalTimes = extractArrivalTimes(); return IntStream.range(0, innerNameDetails.size()) .mapToObj(i -> from( @@ -42,7 +47,7 @@ public static List getRouteInfos(Sheet sheet) { .toList(); } - private static List extractRouteNameDetails(Sheet sheet) { + private List extractRouteNameDetails() { List innerNameDetails = new ArrayList<>(); Row headerRow = sheet.getRow(START_HEADER_ROW); @@ -59,12 +64,12 @@ private static List extractRouteNameDetails(Sheet sheet) { break; } - String name = nameCell.getStringCellValue().trim(); + String name = PoiCellExtractor.extractStringValue(nameCell); Cell detailCell = detailRow.getCell(col); String detail = (detailCell != null && StringUtils.hasText(detailCell.toString())) - ? detailCell.getStringCellValue().trim() + ? PoiCellExtractor.extractStringValue(detailCell) : null; innerNameDetails.add(InnerNameDetail.of(name, detail)); @@ -73,7 +78,7 @@ private static List extractRouteNameDetails(Sheet sheet) { return innerNameDetails; } - private static List extractArrivalTimes(Sheet sheet) { + private List extractArrivalTimes() { List arrivalTimes = new ArrayList<>(); for (int colNum = START_COL; @@ -93,7 +98,7 @@ private static List extractArrivalTimes(Sheet sheet) { } Cell cell = row.getCell(colNum); - String strTime = ExcelStringUtil.getCellValueToString(cell); + String strTime = PoiCellExtractor.extractStringValue(cell); if (cell == null || !StringUtils.hasText(strTime)) { times.add(null); diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/RouteName.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/RouteName.java index 298c78cc6..f4439247b 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/RouteName.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/RouteName.java @@ -1,5 +1,6 @@ package in.koreatech.koin.admin.bus.shuttle.model; +import in.koreatech.koin.admin.bus.shuttle.parser.NameParser; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,7 +11,9 @@ public class RouteName { private String name; - public static RouteName of(String name) { - return new RouteName(name); + public static RouteName of(String sheetName) { + NameParser.ParsedName parsedName = NameParser.parse(sheetName); + + return new RouteName(parsedName.name()); } } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/ShuttleBusTimetable.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/ShuttleBusTimetable.java index 8706ac6d3..293105db5 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/ShuttleBusTimetable.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/ShuttleBusTimetable.java @@ -3,6 +3,7 @@ import java.util.List; import in.koreatech.koin.admin.bus.shuttle.enums.RunningDays; +import in.koreatech.koin.admin.bus.shuttle.parser.NameParser; import in.koreatech.koin.domain.bus.enums.ShuttleBusRegion; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -30,10 +31,11 @@ public static class NodeInfo { private String name; private String detail; - public static NodeInfo of(String name, String detail) { - return new NodeInfo(name, detail); - } + public static NodeInfo of(String nameWithDetail) { + NameParser.ParsedName parsedName = NameParser.parse(nameWithDetail); + return new NodeInfo(parsedName.name(), parsedName.detail()); + } } @AllArgsConstructor(access = AccessLevel.PROTECTED) diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/SubName.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/SubName.java index d204569fc..af593df1e 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/SubName.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/model/SubName.java @@ -1,5 +1,6 @@ package in.koreatech.koin.admin.bus.shuttle.model; +import in.koreatech.koin.admin.bus.shuttle.parser.NameParser; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,7 +11,9 @@ public class SubName { private String name; - public static SubName of(String name) { - return new SubName(name); + public static SubName of(String sheetName) { + NameParser.ParsedName parsedName = NameParser.parse(sheetName); + + return new SubName(parsedName.detail()); } } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/parser/NameParser.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/parser/NameParser.java new file mode 100644 index 000000000..94764c111 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/parser/NameParser.java @@ -0,0 +1,28 @@ +package in.koreatech.koin.admin.bus.shuttle.parser; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NameParser { + + private static final Pattern PATTERN = Pattern.compile("^(.*?)\\s*(?:\\((.*?)\\))?$"); + + public static ParsedName parse(String raw) { + if (raw == null || raw.trim().isEmpty()) { + return new ParsedName("", null); + } + + Matcher m = PATTERN.matcher(raw.trim()); + + if (m.matches()) { + String name = m.group(1).trim(); + String detail = m.group(2) != null ? m.group(2).trim() : null; + return new ParsedName(name, detail); + } + + return new ParsedName(raw.trim(), null); + } + + public record ParsedName(String name, String detail) { + } +} diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/service/AdminShuttleBusExcelService.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/service/AdminShuttleBusExcelService.java index c68495302..8d4a54857 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/service/AdminShuttleBusExcelService.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/service/AdminShuttleBusExcelService.java @@ -1,7 +1,6 @@ package in.koreatech.koin.admin.bus.shuttle.service; -import static in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.NodeInfo; -import static in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.RouteInfo; +import static in.koreatech.koin.admin.bus.shuttle.dto.response.AdminShuttleBusTimetableResponse.InnerAdminShuttleBusTimetableResponse; import java.io.IOException; import java.io.InputStream; @@ -16,13 +15,15 @@ import org.springframework.web.multipart.MultipartFile; import in.koreatech.koin.admin.bus.shuttle.dto.response.AdminShuttleBusTimetableResponse; +import in.koreatech.koin.admin.bus.shuttle.extractor.ShuttleBusMetaDataExtractor; +import in.koreatech.koin.admin.bus.shuttle.extractor.ShuttleBusNodeInfoExtractor; +import in.koreatech.koin.admin.bus.shuttle.extractor.ShuttleBusRouteInfoExtractor; import in.koreatech.koin.admin.bus.shuttle.model.RouteName; import in.koreatech.koin.admin.bus.shuttle.model.RouteType; import in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable; +import in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.NodeInfo; +import in.koreatech.koin.admin.bus.shuttle.model.ShuttleBusTimetable.RouteInfo; import in.koreatech.koin.admin.bus.shuttle.model.SubName; -import in.koreatech.koin.admin.bus.shuttle.util.ShuttleBusMetaDataParser; -import in.koreatech.koin.admin.bus.shuttle.util.ShuttleBusNodeInfoParser; -import in.koreatech.koin.admin.bus.shuttle.util.ShuttleBusRouteInfoParser; import in.koreatech.koin.domain.bus.enums.ShuttleBusRegion; import in.koreatech.koin.global.code.ApiResponseCode; import in.koreatech.koin.global.exception.CustomException; @@ -31,7 +32,7 @@ @Transactional(readOnly = true) public class AdminShuttleBusExcelService { - public List previewShuttleBusTimetable(MultipartFile file) { + public AdminShuttleBusTimetableResponse getShuttleBusTimetablePreview(MultipartFile file) { try ( InputStream inputStream = file.getInputStream(); Workbook workbook = WorkbookFactory.create(inputStream) @@ -42,25 +43,31 @@ public List previewShuttleBusTimetable(Multipa } } - private List extractShuttleBusTimetableData(Workbook workBook) { + private AdminShuttleBusTimetableResponse extractShuttleBusTimetableData(Workbook workBook) { List shuttleBusTimetables = new ArrayList<>(); for (Sheet sheet : workBook) { - List nodeInfos = ShuttleBusNodeInfoParser.getNodeInfos(sheet); - List routeInfos = ShuttleBusRouteInfoParser.getRouteInfos(sheet); + ShuttleBusNodeInfoExtractor nodeInfoExtractor = new ShuttleBusNodeInfoExtractor(sheet); + ShuttleBusRouteInfoExtractor routeInfoExtractor = new ShuttleBusRouteInfoExtractor(sheet); + ShuttleBusMetaDataExtractor metaDataExtractor = new ShuttleBusMetaDataExtractor(sheet); - RouteName routeName = ShuttleBusMetaDataParser.getRouteNameFromSheet(sheet); - SubName subName = ShuttleBusMetaDataParser.getSubNameFromSheet(sheet); - ShuttleBusRegion region = ShuttleBusMetaDataParser.getRegionFromSheet(sheet); - RouteType routeType = ShuttleBusMetaDataParser.getRouteTypeFromSheet(sheet); + List nodeInfos = nodeInfoExtractor.extractNodeInfos(); + List routeInfos = routeInfoExtractor.extractRouteInfos(); + + RouteName routeName = metaDataExtractor.extractRouteName(); + SubName subName = metaDataExtractor.extractSubName(); + ShuttleBusRegion region = metaDataExtractor.extractRegion(); + RouteType routeType = metaDataExtractor.extractRouteType(); shuttleBusTimetables.add( ShuttleBusTimetable.from(nodeInfos, routeInfos, region, routeName, subName, routeType) ); } - return shuttleBusTimetables.stream() - .map(AdminShuttleBusTimetableResponse::from) + List innerResponses = shuttleBusTimetables.stream() + .map(InnerAdminShuttleBusTimetableResponse::from) .toList(); + + return new AdminShuttleBusTimetableResponse(innerResponses); } } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelRangeUtil.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelRangeUtil.java index 6fb78c491..cd1c6a761 100644 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelRangeUtil.java +++ b/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelRangeUtil.java @@ -1,12 +1,23 @@ package in.koreatech.koin.admin.bus.shuttle.util; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.springframework.util.StringUtils; public class ExcelRangeUtil { + private static final DataFormatter FORMATTER = new DataFormatter(); + + private static String getCellStringValue(Cell cell) { + if (cell == null) { + return ""; + } + + return FORMATTER.formatCellValue(cell).trim(); + } + public static int countUsedRowsInColumn(Sheet sheet, int startRow, int checkColumn) { int cnt = 0; @@ -19,7 +30,7 @@ public static int countUsedRowsInColumn(Sheet sheet, int startRow, int checkColu Cell cell = row.getCell(checkColumn); - if (cell == null || !StringUtils.hasText(cell.getStringCellValue())) { + if (!StringUtils.hasText(getCellStringValue(cell))) { break; } @@ -41,7 +52,7 @@ public static int countUsedColumnsInRow(Sheet sheet, int checkRow, int startCol) for (int col = startCol; col < row.getLastCellNum(); col++) { Cell cell = row.getCell(col); - if (cell != null && StringUtils.hasText(cell.getStringCellValue())) { + if (StringUtils.hasText(getCellStringValue(cell))) { lastCol = col; } } diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelStringUtil.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelStringUtil.java deleted file mode 100644 index 6d802af34..000000000 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ExcelStringUtil.java +++ /dev/null @@ -1,36 +0,0 @@ -package in.koreatech.koin.admin.bus.shuttle.util; - -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.DataFormatter; - -public class ExcelStringUtil { - - private static final DataFormatter FORMATTER = new DataFormatter(); - - public static String getCellValueToString(Cell cell) { - String value = FORMATTER.formatCellValue(cell); - - return value != null ? value.trim() : ""; - } - - public static String extractDetailFromBrackets(String str) { - int openIdx = str.indexOf('('); - int closeIdx = str.indexOf(')'); - - if (openIdx != -1 && closeIdx != -1 && closeIdx > openIdx) { - return str.substring(openIdx + 1, closeIdx).trim(); - } - - return null; - } - - public static String extractNameWithoutBrackets(String str) { - int openIdx = str.indexOf('('); - - if (openIdx != -1) { - return str.substring(0, openIdx).trim(); - } - - return str.trim(); - } -} diff --git a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusMetaDataParser.java b/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusMetaDataParser.java deleted file mode 100644 index d1b74d077..000000000 --- a/src/main/java/in/koreatech/koin/admin/bus/shuttle/util/ShuttleBusMetaDataParser.java +++ /dev/null @@ -1,61 +0,0 @@ -package in.koreatech.koin.admin.bus.shuttle.util; - -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; - -import in.koreatech.koin.admin.bus.shuttle.model.RouteName; -import in.koreatech.koin.admin.bus.shuttle.model.RouteType; -import in.koreatech.koin.admin.bus.shuttle.model.SubName; -import in.koreatech.koin.domain.bus.enums.ShuttleBusRegion; -import in.koreatech.koin.global.code.ApiResponseCode; -import in.koreatech.koin.global.exception.CustomException; - -public class ShuttleBusMetaDataParser { - - private static final int REGION_ROW = 0; - private static final int REGION_COL = 1; - - private static final int ROUTE_TYPE_ROW = 1; - private static final int ROUTE_TYPE_COL = 1; - - public static ShuttleBusRegion getRegionFromSheet(Sheet sheet) { - return ShuttleBusRegion.of(getCellValue(sheet, REGION_ROW, REGION_COL)); - } - - public static RouteType getRouteTypeFromSheet(Sheet sheet) { - return RouteType.of(getCellValue(sheet, ROUTE_TYPE_ROW, ROUTE_TYPE_COL)); - } - - public static RouteName getRouteNameFromSheet(Sheet sheet) { - String sheetName = sheet.getSheetName(); - - String routeName = ExcelStringUtil.extractNameWithoutBrackets(sheetName); - - return RouteName.of(routeName); - } - - public static SubName getSubNameFromSheet(Sheet sheet) { - String sheetName = sheet.getSheetName(); - - String subName = ExcelStringUtil.extractDetailFromBrackets(sheetName); - - return SubName.of(subName); - } - - private static String getCellValue(Sheet sheet, int rowIndex, int colIndex) { - Row row = sheet.getRow(rowIndex); - - if (row == null) { - throw CustomException.of(ApiResponseCode.INVALID_EXCEL_ROW); - } - - Cell cell = row.getCell(colIndex); - - if (cell == null) { - throw CustomException.of(ApiResponseCode.INVALID_EXCEL_COL); - } - - return cell.toString(); - } -}