Merge pull request 'feat(ui): 배치 작업 목록 한글 표시명 추가 (#40)' (#43) from feature/ISSUE-40-job-display-name into develop

This commit is contained in:
HYOJIN 2026-03-13 11:24:26 +09:00
커밋 a9e8df7e89
5개의 변경된 파일66개의 추가작업 그리고 8개의 파일을 삭제

파일 보기

@ -28,6 +28,7 @@
- 배치 작업 목록 UX 개선: 상태 필터, 카드/테이블 뷰, 정렬, 실행 중 강조 (#33)
- 재시도 초과 레코드 초기화 API/UI 추가
- IMO 기반 Risk 상세 조회 bypass API 추가 (#39)
- 배치 작업 목록 한글 표시명 추가 (#40)
### 수정
- 자동 재수집 JobParameter 오버플로우 수정 (VARCHAR 2500 제한 해결)

파일 보기

@ -235,6 +235,7 @@ export interface LastExecution {
export interface JobDetailDto {
jobName: string;
displayName: string | null;
lastExecution: LastExecution | null;
scheduleCron: string | null;
}

파일 보기

@ -75,9 +75,16 @@ export default function Jobs() {
usePoller(loadJobs, POLLING_INTERVAL);
/** displayName 우선, 없으면 jobName */
const getJobLabel = useCallback((job: JobDetailDto) => job.displayName || job.jobName, []);
const statusCounts = useMemo(() => {
const searchFiltered = searchTerm.trim()
? jobs.filter((job) => job.jobName.toLowerCase().includes(searchTerm.toLowerCase()))
? jobs.filter((job) => {
const term = searchTerm.toLowerCase();
return job.jobName.toLowerCase().includes(term)
|| (job.displayName?.toLowerCase().includes(term) ?? false);
})
: jobs;
return STATUS_TABS.reduce<Record<StatusFilterKey, number>>(
@ -94,14 +101,17 @@ export default function Jobs() {
if (searchTerm.trim()) {
const term = searchTerm.toLowerCase();
result = result.filter((job) => job.jobName.toLowerCase().includes(term));
result = result.filter((job) =>
job.jobName.toLowerCase().includes(term)
|| (job.displayName?.toLowerCase().includes(term) ?? false),
);
}
result = result.filter((job) => matchesStatusFilter(job, statusFilter));
result = [...result].sort((a, b) => {
if (sortKey === 'name') {
return a.jobName.localeCompare(b.jobName);
return getJobLabel(a).localeCompare(getJobLabel(b));
}
if (sortKey === 'recent') {
const aTime = a.lastExecution?.startTime ? new Date(a.lastExecution.startTime).getTime() : 0;
@ -302,9 +312,14 @@ export default function Jobs() {
${isRunning ? 'border-l-4 border-emerald-500' : ''}`}
>
<div className="flex items-start justify-between mb-3">
<h3 className="text-sm font-semibold text-wing-text break-all leading-tight">
{job.jobName}
</h3>
<div>
<h3 className="text-sm font-semibold text-wing-text break-all leading-tight">
{getJobLabel(job)}
</h3>
{job.displayName && (
<p className="text-xs text-wing-muted mt-0.5">{job.jobName}</p>
)}
</div>
<div className="flex items-center gap-2 ml-2 shrink-0">
{isRunning && (
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
@ -421,7 +436,12 @@ export default function Jobs() {
{isRunning && (
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse shrink-0" />
)}
{job.jobName}
<div>
<span>{getJobLabel(job)}</span>
{job.displayName && (
<p className="text-xs text-wing-muted font-normal">{job.jobName}</p>
)}
</div>
</div>
</td>
<td className="px-4 py-3">
@ -489,7 +509,7 @@ export default function Jobs() {
>
<h3 className="text-lg font-semibold text-wing-text mb-2"> </h3>
<p className="text-wing-muted text-sm mb-4">
&quot;{targetJob}&quot; ?
&quot;{jobs.find((j) => j.jobName === targetJob)?.displayName || targetJob}&quot; ?
</p>
<div className="flex justify-end gap-3 mt-6">

파일 보기

@ -14,6 +14,7 @@ import java.time.LocalDateTime;
public class JobDetailDto {
private String jobName;
private String displayName;
private LastExecution lastExecution;
private String scheduleCron;

파일 보기

@ -37,6 +37,40 @@ import java.util.stream.Collectors;
@Service
public class BatchService {
/** Job Bean 이름 → 한글 표시명 매핑 */
private static final Map<String, String> JOB_DISPLAY_NAMES = Map.ofEntries(
// AIS
Map.entry("aisTargetImportJob", "AIS 선박위치 수집(1분 주기)"),
Map.entry("aisTargetDbSyncJob", "AIS 선박위치 DB 적재(15분 주기)"),
// 공통코드
Map.entry("FlagCodeImportJob", "선박 국가코드 수집"),
Map.entry("Stat5CodeImportJob", "선박 유형코드 수집"),
// Compliance
Map.entry("ComplianceImportRangeJob", "선박 제재 정보 수집"),
Map.entry("CompanyComplianceImportRangeJob", "회사 제재 정보 수집"),
// Event
Map.entry("EventImportJob", "해양 사건/사고 수집"),
// Port
Map.entry("PortImportJob", "항구 시설 수집"),
// Movement
Map.entry("AnchorageCallsRangeImportJob", "정박지 기항 이력 수집"),
Map.entry("BerthCallsRangeImportJob", "선석 기항 이력 수집"),
Map.entry("CurrentlyAtRangeImportJob", "현재 위치 이력 수집"),
Map.entry("DestinationsRangeImportJob", "목적지 이력 수집"),
Map.entry("PortCallsRangeImportJob", "항구 기항 이력 수집"),
Map.entry("STSOperationRangeImportJob", "STS 작업 이력 수집"),
Map.entry("TerminalCallsRangeImportJob", "터미널 기항 이력 수집"),
Map.entry("TransitsRangeImportJob", "항해 이력 수집"),
// PSC
Map.entry("PSCDetailImportJob", "PSC 선박 검사 수집"),
// Risk
Map.entry("RiskRangeImportJob", "선박 위험지표 수집"),
// Ship
Map.entry("ShipDetailUpdateJob", "선박 제원정보 수집"),
// Infra
Map.entry("partitionManagerJob", "AIS 파티션 테이블 생성/관리")
);
private final JobLauncher jobLauncher;
private final JobExplorer jobExplorer;
private final JobOperator jobOperator;
@ -852,6 +886,7 @@ public class BatchService {
return JobDetailDto.builder()
.jobName(jobName)
.displayName(JOB_DISPLAY_NAMES.get(jobName))
.lastExecution(lastExec)
.scheduleCron(cronMap.get(jobName))
.build();