File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package kr.itn.itnhub.contact;
import kr.itn.itnhub.seed.SeedParseException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 회원정보 xlsx(Sheet1)를 읽는다.
*
* 시트 배치 (1행 헤더, 2행부터 데이터):
* A=순번 B=구분 C=기관명 D=부서명 E=담당자명 F=직급/직함 G=유선전화 H=이메일
*
* 담당자명(E)이 빈 첫 행에서 데이터가 끝난 것으로 보고 읽기를 멈춘다. 구분(B)이
* 알려진 값(주관기관/변호사/신청기관/수행기관)이 아니면 그 행은 건너뛰고 skipped로
* 센다 - 시트 끝은 아니므로 이어서 다음 행을 계속 읽는다.
*/
@Component
public class MemberDirectoryParser {
public static final String SHEET_NAME = "Sheet1";
private static final int DATA_START_ROW_INDEX = 1; // 2행
private static final int COL_CATEGORY = 1; // B
private static final int COL_ORG_NAME = 2; // C
private static final int COL_DEPT = 3; // D
private static final int COL_NAME = 4; // E
private static final int COL_TITLE = 5; // F
private static final int COL_PHONE = 6; // G
private static final int COL_EMAIL = 7; // H
private static final Map<String, String> CATEGORY_LABELS = Map.of(
"주관기관", "MJ",
"변호사", "LAWYER",
"신청기관", "APPLICANT",
"수행기관", "OPERATOR",
"아이티앤 담당자", "ITN");
public MemberParseResult parse(InputStream xlsx) {
try (Workbook wb = WorkbookFactory.create(xlsx)) {
Sheet sheet = wb.getSheet(SHEET_NAME);
if (sheet == null) {
throw new SeedParseException(
"통합문서에 '" + SHEET_NAME + "' 시트가 없습니다.");
}
return readRows(sheet);
} catch (IOException e) {
throw new SeedParseException("엑셀 파일을 읽지 못했습니다.", e);
}
}
private MemberParseResult readRows(Sheet sheet) {
List<MemberRow> rows = new ArrayList<>();
int skipped = 0;
for (int i = DATA_START_ROW_INDEX; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
String name = row == null ? "" : string(row, COL_NAME);
if (name.isBlank()) {
break; // 담당자명이 빈 첫 행에서 데이터 끝
}
String category = CATEGORY_LABELS.get(string(row, COL_CATEGORY));
if (category == null) {
skipped++;
continue;
}
rows.add(new MemberRow(
category,
nullIfBlank(string(row, COL_ORG_NAME)),
normalizeDept(string(row, COL_DEPT)),
name,
nullIfBlank(string(row, COL_TITLE)),
nullIfBlank(string(row, COL_PHONE)),
nullIfBlank(string(row, COL_EMAIL))));
}
return new MemberParseResult(rows, skipped);
}
/** 부서명은 "-"와 공백 모두 값 없음으로 취급한다. */
private String normalizeDept(String value) {
String v = nullIfBlank(value);
return "-".equals(v) ? null : v;
}
private String string(Row row, int columnIndex) {
Cell cell = row.getCell(columnIndex);
if (cell == null) {
return "";
}
return cellValue(cell, cell.getCellType());
}
/** SeedParser와 동일한 셀 값 변환 규칙(FORMULA는 캐시된 결과 타입으로 위임). */
private String cellValue(Cell cell, CellType cellType) {
return switch (cellType) {
case STRING -> cell.getStringCellValue().trim();
case NUMERIC -> String.valueOf(Math.round(cell.getNumericCellValue()));
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
case FORMULA -> cellValue(cell, cell.getCachedFormulaResultType());
default -> "";
};
}
private String nullIfBlank(String value) {
return (value == null || value.isBlank()) ? null : value;
}
}