공지사항/Q&A 개선 - 장애 점검 정보 노출 및 상단고정 정렬 - Q&A 이벤트 정리
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IncidentAffectedApiDTO {
|
||||
private String apiId;
|
||||
private String apiName;
|
||||
}
|
||||
@@ -8,11 +8,18 @@ import org.hibernate.validator.constraints.Length;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PortalNoticeDTO {
|
||||
|
||||
public static final String NOTICE_TYPE_NORMAL = "1";
|
||||
public static final String NOTICE_TYPE_MAINTENANCE = "2";
|
||||
public static final String NOTICE_TYPE_INCIDENT = "3";
|
||||
|
||||
private String id;
|
||||
|
||||
@NotEmpty(message = "제목을 입력해주세요.")
|
||||
@@ -29,9 +36,33 @@ public class PortalNoticeDTO {
|
||||
|
||||
private boolean useYn;
|
||||
|
||||
private String fixYn;
|
||||
|
||||
private String noticeType;
|
||||
|
||||
private String inquirerName;
|
||||
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
private LocalDateTime lastModifiedDate;
|
||||
|
||||
// ───── 장애/점검 연동 필드 ─────
|
||||
private String summary;
|
||||
private LocalDateTime startedAt;
|
||||
private LocalDateTime endAt;
|
||||
private String state;
|
||||
private String previousState;
|
||||
private List<IncidentAffectedApiDTO> affectedApis = Collections.emptyList();
|
||||
|
||||
public boolean isIncidentType() {
|
||||
return NOTICE_TYPE_INCIDENT.equals(noticeType);
|
||||
}
|
||||
|
||||
public boolean isMaintenanceType() {
|
||||
return NOTICE_TYPE_MAINTENANCE.equals(noticeType);
|
||||
}
|
||||
|
||||
public boolean isIncidentOrMaintenance() {
|
||||
return isIncidentType() || isMaintenanceType();
|
||||
}
|
||||
}
|
||||
+45
-3
@@ -1,8 +1,12 @@
|
||||
package com.eactive.apim.portal.apps.community.notice.service;
|
||||
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.IncidentAffectedApiDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeDTO;
|
||||
import com.eactive.apim.portal.apps.community.notice.dto.PortalNoticeSearch;
|
||||
import com.eactive.apim.portal.apps.community.notice.mapper.PortalNoticeMapper;
|
||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncident;
|
||||
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentApiRepository;
|
||||
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentRepository;
|
||||
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -12,7 +16,10 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service("portalNoticeFacade")
|
||||
@RequiredArgsConstructor
|
||||
@@ -20,12 +27,13 @@ public class PortalNoticeFacadeImpl implements PortalNoticeFacade {
|
||||
|
||||
private final PortalNoticeService portalNoticeService;
|
||||
private final PortalNoticeMapper portalNoticeMapper;
|
||||
private final DjbApistatusIncidentRepository incidentRepository;
|
||||
private final DjbApistatusIncidentApiRepository incidentApiRepository;
|
||||
|
||||
@Override
|
||||
public List<PortalNoticeDTO> getLatestNotices() {
|
||||
PortalNoticeSearch search = new PortalNoticeSearch();
|
||||
Pageable pageable = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "createdDate"));
|
||||
// Pageable pageable = Pageable.ofSize(3);
|
||||
Page<PortalNoticeDTO> notices = getNotices(search, pageable);
|
||||
return notices.getContent();
|
||||
}
|
||||
@@ -36,13 +44,47 @@ public class PortalNoticeFacadeImpl implements PortalNoticeFacade {
|
||||
Specification<PortalNotice> spec = Specification.where(search.buildSpecification())
|
||||
.and((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("useYn"),"Y"));
|
||||
|
||||
Page<PortalNotice> noticesPage = portalNoticeService.getNotices(spec, pageable);
|
||||
// 상단고정(fixYn='Y') 우선 정렬 — 호출자의 sort 앞에 결합 (admin Service 와 일관)
|
||||
Sort fixYnFirst = Sort.by(Sort.Direction.DESC, "fixYn");
|
||||
Pageable fixedPageable = PageRequest.of(
|
||||
pageable.getPageNumber(),
|
||||
pageable.getPageSize(),
|
||||
fixYnFirst.and(pageable.getSort())
|
||||
);
|
||||
|
||||
Page<PortalNotice> noticesPage = portalNoticeService.getNotices(spec, fixedPageable);
|
||||
return noticesPage.map(portalNoticeMapper::map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PortalNoticeDTO getNotice(String id) {
|
||||
PortalNotice portalNotice = portalNoticeService.getNotice(id);
|
||||
return portalNoticeMapper.map(portalNotice);
|
||||
PortalNoticeDTO dto = portalNoticeMapper.map(portalNotice);
|
||||
populateIncident(dto);
|
||||
return dto;
|
||||
}
|
||||
|
||||
private void populateIncident(PortalNoticeDTO dto) {
|
||||
if (!dto.isIncidentOrMaintenance()) {
|
||||
dto.setAffectedApis(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
Optional<DjbApistatusIncident> incidentOpt = incidentRepository.findByNoticeId(dto.getId());
|
||||
if (!incidentOpt.isPresent()) {
|
||||
dto.setAffectedApis(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
DjbApistatusIncident incident = incidentOpt.get();
|
||||
dto.setSummary(incident.getSummary());
|
||||
dto.setStartedAt(incident.getStartedAt());
|
||||
dto.setEndAt(incident.getEndAt());
|
||||
dto.setState(incident.getState() == null ? null : incident.getState().name());
|
||||
dto.setPreviousState(incident.getPreviousState() == null ? null : incident.getPreviousState().name());
|
||||
|
||||
List<IncidentAffectedApiDTO> apis = incidentApiRepository
|
||||
.findByIncidentIdOrderByApiId(incident.getIncidentId()).stream()
|
||||
.map(api -> new IncidentAffectedApiDTO(api.getApiId(), api.getApiName()))
|
||||
.collect(Collectors.toList());
|
||||
dto.setAffectedApis(apis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,50 @@
|
||||
|
||||
<!-- Notice Header: Title and Date -->
|
||||
<div class="notice-detail-header">
|
||||
<h2 class="notice-detail-title" th:text="${portalNotice.noticeSubject}">개인정보처리 방침 정정 공지</h2>
|
||||
<h2 class="notice-detail-title">
|
||||
<span th:if="${portalNotice.noticeType == '3'}"
|
||||
style="display:inline-block;padding:2px 10px;margin-right:8px;border-radius:12px;font-size:14px;vertical-align:middle;background:#fde7e9;color:#c0152a;">장애</span>
|
||||
<span th:if="${portalNotice.noticeType == '2'}"
|
||||
style="display:inline-block;padding:2px 10px;margin-right:8px;border-radius:12px;font-size:14px;vertical-align:middle;background:#fff4d6;color:#9a6700;">점검</span>
|
||||
<span th:text="${portalNotice.noticeSubject}">개인정보처리 방침 정정 공지</span>
|
||||
</h2>
|
||||
<span class="notice-detail-date" th:text="${#temporals.format(portalNotice.createdDate, 'yyyy.MM.dd')}">2025.11.01</span>
|
||||
</div>
|
||||
|
||||
<!-- 장애/점검 정보 영역 -->
|
||||
<div class="notice-detail-incident-info" th:if="${portalNotice.incidentOrMaintenance}"
|
||||
style="margin:16px 0;padding:16px;background:#f7f8fa;border:1px solid #e3e6eb;border-radius:8px;">
|
||||
<table style="width:100%;border-collapse:collapse;">
|
||||
<colgroup>
|
||||
<col style="width:120px;"><col><col style="width:120px;"><col>
|
||||
</colgroup>
|
||||
<tr>
|
||||
<th style="text-align:left;padding:6px 8px;">시작</th>
|
||||
<td style="padding:6px 8px;"
|
||||
th:text="${portalNotice.startedAt != null ? #temporals.format(portalNotice.startedAt, 'yyyy.MM.dd HH:mm') : '-'}">-</td>
|
||||
<th style="text-align:left;padding:6px 8px;">종료</th>
|
||||
<td style="padding:6px 8px;"
|
||||
th:text="${portalNotice.endAt != null ? #temporals.format(portalNotice.endAt, 'yyyy.MM.dd HH:mm') : '진행중'}">-</td>
|
||||
</tr>
|
||||
<tr th:if="${portalNotice.incidentType}">
|
||||
<th style="text-align:left;padding:6px 8px;">상태</th>
|
||||
<td colspan="3" style="padding:6px 8px;" th:text="${portalNotice.state != null ? portalNotice.state : '-'}">-</td>
|
||||
</tr>
|
||||
<tr th:if="${portalNotice.affectedApis != null and !portalNotice.affectedApis.isEmpty()}">
|
||||
<th style="text-align:left;padding:6px 8px;vertical-align:top;">영향 API</th>
|
||||
<td colspan="3" style="padding:6px 8px;">
|
||||
<ul style="margin:0;padding-left:18px;">
|
||||
<li th:each="api : ${portalNotice.affectedApis}">
|
||||
<strong th:text="${api.apiId}">API_ID</strong>
|
||||
<span th:if="${api.apiName != null and !api.apiName.isEmpty()}"
|
||||
th:text="| - ${api.apiName}|"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Attachment Section -->
|
||||
<div class="notice-detail-attachment" th:if="${!#strings.isEmpty(portalNotice.fileId)}">
|
||||
<div class="attachment-list" th:with="fileInfo=${@fileService.findById(portalNotice.fileId)}">
|
||||
|
||||
@@ -58,6 +58,15 @@
|
||||
<div class="row-cell row-cell--title" style="flex: 1; min-width: 200px;" data-label="제목">
|
||||
<a th:href="@{/portalnotice/detail(id=${notice.id})}" class="notice-title-link">
|
||||
<span class="notice-number" th:text="|[${page.totalElements - (page.number * page.size) - status.index}]|">[1]</span>
|
||||
<span class="notice-fix-badge"
|
||||
th:if="${notice.fixYn == 'Y'}"
|
||||
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#e5f0ff;color:#0a4ea3;font-weight:600;">고정</span>
|
||||
<span class="notice-type-badge notice-type-badge--incident"
|
||||
th:if="${notice.noticeType == '3'}"
|
||||
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#fde7e9;color:#c0152a;">장애</span>
|
||||
<span class="notice-type-badge notice-type-badge--maintenance"
|
||||
th:if="${notice.noticeType == '2'}"
|
||||
style="display:inline-block;padding:2px 8px;margin-right:6px;border-radius:10px;font-size:12px;background:#fff4d6;color:#9a6700;">점검</span>
|
||||
<span th:text="${notice.noticeSubject}">공지사항 제목</span>
|
||||
<span class="file-icon" th:if="${notice.fileId != null and !notice.fileId.isEmpty()}">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
Reference in New Issue
Block a user