From 0d7d006f92e6a81a52247ce8bb0ca7d7b16c99db Mon Sep 17 00:00:00 2001 From: eastargh Date: Mon, 1 Jun 2026 17:37:06 +0900 Subject: [PATCH] =?UTF-8?q?api=20=EC=83=81=ED=83=9C=20=EB=B3=80=ED=99=94?= =?UTF-8?q?=20=EA=B0=90=EC=A7=80=20&=20=EC=9B=B9=ED=9B=85=20=EB=B0=9C?= =?UTF-8?q?=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/WEB-INF/applicationContext-jpa.xml | 1 + .../data/entity/man/user/UserInfoService.java | 11 ++ .../onl}/djb/apistatus/ApiStatus.java | 2 +- .../onl}/djb/apistatus/ApiStatusEvent.java | 3 +- .../djb/inflow/InflowTokenInsufficient.java | 2 +- .../inflow/InflowTokenInsufficientLog.java | 2 +- .../inflow/InflowTokenInsufficientLogId.java | 2 +- .../entity/onl/djb/webhook/WebhookReq.java | 33 ++++ .../entity/onl/djb/webhook/WebhookReqApi.java | 19 +++ .../onl/djb/webhook/WebhookReqApiId.java | 25 ++++ .../onl/djb/webhook/WebhookReqEvent.java | 19 +++ .../onl/djb/webhook/WebhookReqEventId.java | 25 ++++ .../onl}/djb/webhook/WebhookSendLog.java | 2 +- .../ext/djb/apistatus/ApiStatusService.java | 69 --------- .../djb/apistatus/ApiStatusRepository.java | 20 ++- .../ext/djb/apistatus/ApiStatusService.java | 141 ++++++++++++++++++ .../eai/rms/ext/djb/async/AsyncConfig.java | 25 ---- .../rms/ext/djb/async/AsyncEventListener.java | 40 ----- .../ext/djb/event/ApiStatusChangedEvent.java | 18 --- .../InflowTokenInsufficientEvent.java | 4 +- .../ext/djb/inflow/InflowTokenRepository.java | 5 +- .../ext/djb/inflow/InflowTokenService.java | 13 +- .../rms/ext/djb/job/ApiStatusMonitorJob.java | 4 +- .../ext/djb/job/InflowTokenMonitorJob.java | 2 +- .../djb/swing/service/SwingSendManager.java | 17 --- .../eai/rms/ext/djb/util/UmsManager.java | 71 +++++++++ .../controller/WebhookSendController.java | 14 +- .../djb/webhook/dto/WebhookSendRequest.java | 3 +- .../repository/WebhookReqRepository.java | 11 ++ .../repository/WebhookSendLogRepository.java | 2 +- .../webhook/service/WebhookSendManager.java | 17 --- .../djb/webhook/service/WebhookService.java | 74 +++++++-- .../service/ums/UmsDispatchService.java | 86 +++++------ 33 files changed, 505 insertions(+), 277 deletions(-) rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/apistatus/ApiStatus.java (95%) rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/apistatus/ApiStatusEvent.java (54%) rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/inflow/InflowTokenInsufficient.java (66%) rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/inflow/InflowTokenInsufficientLog.java (95%) rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/inflow/InflowTokenInsufficientLogId.java (90%) create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReq.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApi.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApiId.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEvent.java create mode 100644 src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEventId.java rename src/main/java/com/eactive/eai/rms/data/{ext => entity/onl}/djb/webhook/WebhookSendLog.java (97%) delete mode 100644 src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusService.java rename src/main/java/com/eactive/eai/rms/{data => }/ext/djb/apistatus/ApiStatusRepository.java (80%) create mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusService.java delete mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncConfig.java delete mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncEventListener.java delete mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/event/ApiStatusChangedEvent.java rename src/main/java/com/eactive/eai/rms/ext/djb/{event => inflow}/InflowTokenInsufficientEvent.java (80%) rename src/main/java/com/eactive/eai/rms/{data => }/ext/djb/inflow/InflowTokenRepository.java (73%) rename src/main/java/com/eactive/eai/rms/{data => }/ext/djb/inflow/InflowTokenService.java (67%) delete mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/swing/service/SwingSendManager.java create mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/util/UmsManager.java create mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookReqRepository.java delete mode 100644 src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookSendManager.java diff --git a/WebContent/WEB-INF/applicationContext-jpa.xml b/WebContent/WEB-INF/applicationContext-jpa.xml index b1da502..95e9929 100644 --- a/WebContent/WEB-INF/applicationContext-jpa.xml +++ b/WebContent/WEB-INF/applicationContext-jpa.xml @@ -69,6 +69,7 @@ + findAllByRoleId(String roleId) { + QUserInfo qUserInfo = QUserInfo.userInfo; + QUserRole qUserRole = QUserRole.userRole; + + return getJPAQueryFactory() + .selectFrom(qUserInfo) + .join(qUserRole).on(qUserInfo.userid.eq(qUserRole.id.userId)) + .where(qUserRole.id.roleId.eq(roleId)) + .fetch(); + } + public Page findByUsername(Pageable pageable, String username) { QUserInfo qUserInfo = QUserInfo.userInfo; BooleanExpression predicate = qUserInfo.isNotNull(); diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatus.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatus.java similarity index 95% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatus.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatus.java index c7f1e17..8c46dd5 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatus.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatus.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.apistatus; +package com.eactive.eai.rms.data.entity.onl.djb.apistatus; import java.time.LocalDateTime; diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusEvent.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatusEvent.java similarity index 54% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusEvent.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatusEvent.java index 5f2049e..aba632a 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusEvent.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/apistatus/ApiStatusEvent.java @@ -1,7 +1,8 @@ -package com.eactive.eai.rms.data.ext.djb.apistatus; +package com.eactive.eai.rms.data.entity.onl.djb.apistatus; public interface ApiStatusEvent { String getEaisvcname(); String getEaisvcdesc(); + String getPrevStatusCode(); String getEvent(); } \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficient.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficient.java similarity index 66% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficient.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficient.java index c3c6ffc..64108f8 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficient.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficient.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.inflow; +package com.eactive.eai.rms.data.entity.onl.djb.inflow; public interface InflowTokenInsufficient { String getEaisvcname(); diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLog.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLog.java similarity index 95% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLog.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLog.java index ae0ba77..e3cd9eb 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLog.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLog.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.inflow; +package com.eactive.eai.rms.data.entity.onl.djb.inflow; import java.io.Serializable; diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLogId.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLogId.java similarity index 90% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLogId.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLogId.java index 0a2bae6..1568a34 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenInsufficientLogId.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/inflow/InflowTokenInsufficientLogId.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.inflow; +package com.eactive.eai.rms.data.entity.onl.djb.inflow; import java.io.Serializable; import javax.persistence.Column; diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReq.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReq.java new file mode 100644 index 0000000..02aa430 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReq.java @@ -0,0 +1,33 @@ +package com.eactive.eai.rms.data.entity.onl.djb.webhook; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "PTL_WEBHOOK_REQ") +@Getter +@NoArgsConstructor +public class WebhookReq { + + @Id + @Column(name = "ID", length = 36, nullable = false) + private String id; + + @Column(name = "ORG_ID", length = 100) + @Comment("법인 ID") + private String orgId; + + @Column(name = "TARGET_URL", length = 500, nullable = false) + @Comment("웹훅 수신 URL") + private String targetUrl; + + @Column(name = "SECRET", length = 200) + @Comment("HMAC 서명용 Secret Key") + private String secret; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApi.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApi.java new file mode 100644 index 0000000..df94810 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApi.java @@ -0,0 +1,19 @@ +package com.eactive.eai.rms.data.entity.onl.djb.webhook; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.io.Serializable; + +@Entity +@Table(name = "PTL_WEBHOOK_REQ_API") +@Getter +@NoArgsConstructor +public class WebhookReqApi implements Serializable { + + @EmbeddedId + private WebhookReqApiId id; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApiId.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApiId.java new file mode 100644 index 0000000..784ea4a --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqApiId.java @@ -0,0 +1,25 @@ +package com.eactive.eai.rms.data.entity.onl.djb.webhook; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Embeddable +public class WebhookReqApiId implements Serializable { + + @Column(name = "WEBHOOK_REQ_ID", nullable = false, length = 36) + @Comment("웹훅 요청 ID") + private String webhookReqId; + + @Column(name = "API_ID", nullable = false, length = 100) + @Comment("API ID") + private String apiId; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEvent.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEvent.java new file mode 100644 index 0000000..5cc9b4b --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEvent.java @@ -0,0 +1,19 @@ +package com.eactive.eai.rms.data.entity.onl.djb.webhook; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.io.Serializable; + +@Entity +@Table(name = "PTL_WEBHOOK_REQ_EVENT") +@Getter +@NoArgsConstructor +public class WebhookReqEvent implements Serializable { + + @EmbeddedId + private WebhookReqEventId id; +} diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEventId.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEventId.java new file mode 100644 index 0000000..1af090e --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookReqEventId.java @@ -0,0 +1,25 @@ +package com.eactive.eai.rms.data.entity.onl.djb.webhook; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Embeddable +public class WebhookReqEventId implements Serializable { + + @Column(name = "WEBHOOK_REQ_ID", nullable = false, length = 36) + @Comment("웹훅 요청 ID") + private String webhookReqId; + + @Column(name = "EVENT_TYPE", nullable = false, length = 50) + @Comment("이벤트 유형 (CONTROL_START, ERROR_START 등)") + private String eventType; +} diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/webhook/WebhookSendLog.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookSendLog.java similarity index 97% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/webhook/WebhookSendLog.java rename to src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookSendLog.java index f2bcdf2..a1fa55d 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/webhook/WebhookSendLog.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/djb/webhook/WebhookSendLog.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.webhook; +package com.eactive.eai.rms.data.entity.onl.djb.webhook; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusService.java b/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusService.java deleted file mode 100644 index ac35442..0000000 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusService.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.eactive.eai.rms.data.ext.djb.apistatus; - -import java.util.HashMap; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import com.eactive.eai.rms.ext.djb.event.ApiStatusChangedEvent; - -@Service -@Transactional -public class ApiStatusService { - - private static final Logger log = LoggerFactory.getLogger(ApiStatusService.class); - - @Autowired - private ApiStatusRepository apiStatusRepository; - - @Autowired - private ApplicationEventPublisher eventPublisher; - - - - public void updateApiStatus(HashMap param) { - - List list = apiStatusRepository.findApiStatusEvents( - Integer.parseInt(param.get("errorRate")), - Integer.parseInt(param.get("errorRangeMinute")), - Integer.parseInt(param.get("delayRangeMinute")), - Integer.parseInt(param.get("delayAvgRespTime")) - ); - - for (ApiStatusEvent event : list) { - String newStatusCode = resolveStatusCode(event.getEvent()); - if (newStatusCode == null) continue; - - ApiStatus apiStatus = apiStatusRepository.findById(event.getEaisvcname()).orElse(new ApiStatus()); - - apiStatus.setEaisvcname(event.getEaisvcname()); - apiStatus.setStatusCode(newStatusCode); - - apiStatusRepository.save(apiStatus); // PK 있으면 UPDATE, 없으면 INSERT - log.debug("API 상태 변경: {}-{} {} → {}", event.getEaisvcname(), event.getEaisvcdesc(), event.getEvent(), newStatusCode); - - eventPublisher.publishEvent(ApiStatusChangedEvent.from(event)); // 트랜잭션 커밋 후 리스너 실행 - log.debug("이벤트 전파"); - } - } - - - private String resolveStatusCode(String event) { - switch (event) { - case "CONTROL_START": return "C"; //점검 - case "CONTROL_END": return "N"; //정상 - case "ERROR_START": return "E"; //장애 - case "ERROR_END": return "N"; //정상 - case "DELAY_START": return "D"; //지연 - case "DELAY_END": return "N"; //정상 - default: - log.warn("알 수 없는 이벤트: {}", event); - return null; - } - } -} diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusRepository.java b/src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusRepository.java similarity index 80% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusRepository.java rename to src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusRepository.java index f716bb0..ec80426 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/apistatus/ApiStatusRepository.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusRepository.java @@ -1,10 +1,12 @@ -package com.eactive.eai.rms.data.ext.djb.apistatus; +package com.eactive.eai.rms.ext.djb.apistatus; import java.util.List; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import com.eactive.eai.data.jpa.BaseRepository; +import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatus; +import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatusEvent; public interface ApiStatusRepository extends BaseRepository { @@ -22,9 +24,11 @@ public interface ApiStatusRepository extends BaseRepository { @Query(nativeQuery = true, value = " SELECT EAISVCNAME" + " , (SELECT EAISVCDESC FROM TSEAIHE01 WHERE A.EAISVCNAME = EAISVCNAME) AS EAISVCDESC" + + " , PREV_STATUS_CODE " + " , EVENT" + " FROM (" + " SELECT EAISVCNAME" + + " , STATUS_CODE AS PREV_STATUS_CODE " + " , CASE WHEN STATUS_CODE != 'C' AND CTRL_YN = 'Y' THEN 'CONTROL_START'" + " WHEN STATUS_CODE = 'C' AND CTRL_YN = 'N' THEN 'CONTROL_END'" + " WHEN STATUS_CODE IN ('N','D') AND ERR_YN = 'Y' THEN 'ERROR_START'" @@ -34,14 +38,14 @@ public interface ApiStatusRepository extends BaseRepository { + " ELSE 'STAY' END AS EVENT" + " FROM (" + " SELECT A.EAISVCNAME" - + " , NVL(B.STATUS_CODE, '1') AS STATUS_CODE" - + " , NVL((SELECT 'Y' FROM TSEAITI01" - + " WHERE (TO_CHAR(SYSDATE,'HH24MI') BETWEEN SUBSTR(EAICTRLDSTICCTNT,16,4) AND SUBSTR(EAICTRLDSTICCTNT,21,4)" - + " OR SUBSTR(EAICTRLDSTICCTNT,16,9) = '0000|0000')" - + " AND A.EAISVCNAME = EAICTRLNAME),'N') AS CTRL_YN" + + " , NVL(B.STATUS_CODE, 'N') AS STATUS_CODE" + + " , NVL(( SELECT 'Y' FROM EMSAPP.PTL_NOTICE_API X " + + " WHERE EXISTS (SELECT 1 FROM EMSAPP.PTL_NOTICE Y WHERE X.NOTICE_ID = Y.ID " + + " AND TO_CHAR(SYSDATE, 'YYYYMMDDHH24MI') BETWEEN START_TIME AND END_TIME) " + + " AND A.EAISVCNAME = X.API_NAME),'N') AS CTRL_YN " + " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'" - + " WHEN (TOTAL - SUCCESS) * 100 / TOTAL > :errorRate THEN 'Y'" - + " ELSE 'N' END" + + " WHEN (TOTAL - SUCCESS) * 100 / TOTAL > :errorRate THEN 'Y'" + + " ELSE 'N' END" + " FROM (SELECT SUM(SUCCESS_CNT + TIMEOUT_CNT + SYSTEM_ERR_CNT + BIZ_ERR_CNT) AS TOTAL" + " , SUM(SUCCESS_CNT) AS SUCCESS" + " FROM API_STATS_MINUTE" diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusService.java b/src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusService.java new file mode 100644 index 0000000..08e6bce --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/ext/djb/apistatus/ApiStatusService.java @@ -0,0 +1,141 @@ +package com.eactive.eai.rms.ext.djb.apistatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.eactive.apim.portal.template.entity.MessageCode; +import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatus; +import com.eactive.eai.rms.data.entity.onl.djb.apistatus.ApiStatusEvent; +import com.eactive.eai.rms.ext.djb.util.UmsManager; +import com.eactive.ext.djb.apistatus.DjbIncidentDetectionService; + + +@Service +@Transactional +public class ApiStatusService { + + private static final Logger log = LoggerFactory.getLogger(ApiStatusService.class); + + @Autowired + private ApiStatusRepository apiStatusRepository; + + @Autowired + private UmsManager ums; + + @Autowired + private DjbIncidentDetectionService djbService; + + + public void updateApiStatus(HashMap param) { + + List list = apiStatusRepository.findApiStatusEvents( + Integer.parseInt(param.get("errorRate")), + Integer.parseInt(param.get("errorRangeMinute")), + Integer.parseInt(param.get("delayRangeMinute")), + Integer.parseInt(param.get("delayAvgRespTime")) + ); + + HashMap> eventMap = new HashMap<>(); + + + for (ApiStatusEvent event : list) { + String newStatusCode = resolveStatusCode(event.getEvent()); + if (newStatusCode == null) continue; + + ApiStatus apiStatus = apiStatusRepository.findById(event.getEaisvcname()).orElse(new ApiStatus()); + + apiStatus.setEaisvcname(event.getEaisvcname()); + apiStatus.setStatusCode(newStatusCode); + + apiStatusRepository.save(apiStatus); // PK 있으면 UPDATE, 없으면 INSERT + log.debug("API 상태 변경: {}-{} {} → {}", event.getEaisvcname(), event.getEaisvcdesc(), event.getEvent(), newStatusCode); + + + //이벤트별로 api분리 저장 + List apis = (List)eventMap.get(event.getEvent()); + if (apis == null) { + apis = new ArrayList(); + apis.add(event.getEaisvcname()); + eventMap.put(event.getEvent(), apis); + } else { + apis.add(event.getEaisvcname()); + } + } + + + //이벤트별로 ums, webhook을 발송한다 + for (Map.Entry> entry : eventMap.entrySet()) { + String event = entry.getKey(); + List apiIds = entry.getValue(); + + String message = getSwingChatMessage(event, apiIds); + ums.sendMessenger("api-monitor", MessageCode.API_STATUS_CHANGED, message); + + //제휴사 webhook 발송 + ums.sendWebhook(event, apiIds); + + //개발자포탈 API상태모니터링 화면을 위한 처리 + djbService.detect(event, apiIds); + + +// if (event.endsWith("_END")) { +// djbService.recovered(event, apiIds); +// } else { +// djbService.detect(event, apiIds); +// } + } + } + + + private String resolveStatusCode(String event) { + switch (event) { + case "CONTROL_START": return "C"; //점검 + case "CONTROL_END": return "N"; //정상 + case "ERROR_START": return "E"; //장애 + case "ERROR_END": return "N"; //정상 + case "DELAY_START": return "D"; //지연 + case "DELAY_END": return "N"; //정상 + default: + log.warn("알 수 없는 이벤트: {}", event); + return null; + } + } + + + private String getSwingChatMessage(String event, List apiIds) { + String message = ""; + switch (event) { + case "CONTROL_START": + message = "API 서비스 점검이 시작되었습니다"; //점검 + break; + case "CONTROL_END": + message = "API 서비스 점검이 완료되었습니다"; //정상 + break; + case "ERROR_START": + message = "API 서비스 장애가 발생하였습니다"; //장애 + break; + case "ERROR_END": + message = "API 서비스 장애가 복구되었습니다"; //정상 + break; + case "DELAY_START": + message = "API 서비스 지연이 발생하였습니다"; //지연 + break; + case "DELAY_END": + message = "API 서비스 지연이 복구되었습니다"; //정상 + break; + default: + message = ""; + } + + return message + "\n- " + String.join(",", apiIds); + } +} + diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncConfig.java b/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncConfig.java deleted file mode 100644 index b62f619..0000000 --- a/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.eactive.eai.rms.ext.djb.async; - -import java.util.concurrent.Executor; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -@Configuration -@EnableAsync -public class AsyncConfig { - - @Bean("asyncExecutor") - public Executor asyncExecutor() { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(2); - executor.setMaxPoolSize(5); - executor.setQueueCapacity(20); - executor.setThreadNamePrefix("async-"); - executor.initialize(); - return executor; - } - -} \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncEventListener.java b/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncEventListener.java deleted file mode 100644 index 63c5089..0000000 --- a/src/main/java/com/eactive/eai/rms/ext/djb/async/AsyncEventListener.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.eactive.eai.rms.ext.djb.async; - -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionPhase; -import org.springframework.transaction.event.TransactionalEventListener; - -import com.eactive.eai.rms.ext.djb.event.ApiStatusChangedEvent; -import com.eactive.eai.rms.ext.djb.event.InflowTokenInsufficientEvent; -import com.eactive.eai.rms.ext.djb.swing.service.SwingSendManager; -import com.eactive.eai.rms.ext.djb.webhook.service.WebhookSendManager; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Component -@RequiredArgsConstructor -public class AsyncEventListener { - - private final WebhookSendManager webhookSendManager; - - private final SwingSendManager swingSendManager; - - @Async("asyncExecutor") - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void onApiStatusChanged(ApiStatusChangedEvent event) { - log.debug("API 상태 변경 이벤트 수신: {} {}", event.getEaisvcname(), event.getEvent()); - webhookSendManager.send(event.getEaisvcname(), event.getEvent()); - swingSendManager.send(null, 0, 0); - } - - @Async("asyncExecutor") - @EventListener // 트랜잭션 write 없이 read만 하므로 TransactionalEventListener 불필요 - public void onInflowTokenFail(InflowTokenInsufficientEvent event) { - log.debug("유량제어 토큰획득 실패 이벤트 수신: {} {}건", event.getEaisvcname(), event.getCnt()); - swingSendManager.send(event.getEaisvcname(), event.getCnt(), event.getRangeMinute()); - } -} \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/event/ApiStatusChangedEvent.java b/src/main/java/com/eactive/eai/rms/ext/djb/event/ApiStatusChangedEvent.java deleted file mode 100644 index 6f6dc95..0000000 --- a/src/main/java/com/eactive/eai/rms/ext/djb/event/ApiStatusChangedEvent.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.eactive.eai.rms.ext.djb.event; - -import com.eactive.eai.rms.data.ext.djb.apistatus.ApiStatusEvent; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -public class ApiStatusChangedEvent { - private final String eaisvcname; - private final String eaisvcdesc; - private final String event; - - public static ApiStatusChangedEvent from(ApiStatusEvent source) { - return new ApiStatusChangedEvent(source.getEaisvcname(), source.getEaisvcdesc(), source.getEvent()); - } -} \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/event/InflowTokenInsufficientEvent.java b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenInsufficientEvent.java similarity index 80% rename from src/main/java/com/eactive/eai/rms/ext/djb/event/InflowTokenInsufficientEvent.java rename to src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenInsufficientEvent.java index b4b010e..dadcd80 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/event/InflowTokenInsufficientEvent.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenInsufficientEvent.java @@ -1,6 +1,6 @@ -package com.eactive.eai.rms.ext.djb.event; +package com.eactive.eai.rms.ext.djb.inflow; -import com.eactive.eai.rms.data.ext.djb.inflow.InflowTokenInsufficient; +import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficient; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenRepository.java b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenRepository.java similarity index 73% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenRepository.java rename to src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenRepository.java index 8dadc25..d98c99c 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenRepository.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenRepository.java @@ -1,9 +1,12 @@ -package com.eactive.eai.rms.data.ext.djb.inflow; +package com.eactive.eai.rms.ext.djb.inflow; import java.util.List; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import com.eactive.eai.data.jpa.BaseRepository; +import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficient; +import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficientLog; +import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficientLogId; public interface InflowTokenRepository extends BaseRepository { diff --git a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenService.java b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java similarity index 67% rename from src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenService.java rename to src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java index e4e91fa..fa7a12c 100644 --- a/src/main/java/com/eactive/eai/rms/data/ext/djb/inflow/InflowTokenService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/inflow/InflowTokenService.java @@ -1,4 +1,4 @@ -package com.eactive.eai.rms.data.ext.djb.inflow; +package com.eactive.eai.rms.ext.djb.inflow; import java.util.List; @@ -9,7 +9,9 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.eactive.eai.rms.ext.djb.event.InflowTokenInsufficientEvent; +import com.eactive.apim.portal.template.entity.MessageCode; +import com.eactive.eai.rms.data.entity.onl.djb.inflow.InflowTokenInsufficient; +import com.eactive.eai.rms.ext.djb.util.UmsManager; @Service @@ -22,16 +24,15 @@ public class InflowTokenService { private InflowTokenRepository inflowTokenRepository; @Autowired - private ApplicationEventPublisher eventPublisher; + private UmsManager ums; public void checkRecentFails(long rangeMinute) { List rows = inflowTokenRepository.countTokenInsufficient(rangeMinute); for (InflowTokenInsufficient info : rows) { log.debug("유량제어 토큰 획득 실패: {}-{} 최근 {}분 동안 {}건", info.getEaisvcname(), info.getEaisvcdesc(), rangeMinute, info.getCnt()); - - //내부직원 알림 발송 - eventPublisher.publishEvent(InflowTokenInsufficientEvent.from(info, rangeMinute)); + String message = "유량제어 토큰 획득 실패하였습니다 \n" + info.getEaisvcname(); + ums.sendMessenger("api-monitor", MessageCode.INFLOW_TOKEN_FAILED, message); } } } diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatusMonitorJob.java b/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatusMonitorJob.java index dbd3b66..6d0dd5c 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatusMonitorJob.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/job/ApiStatusMonitorJob.java @@ -17,7 +17,7 @@ import com.eactive.eai.rms.common.context.MonitoringContext; import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.util.CommonUtil; -import com.eactive.eai.rms.data.ext.djb.apistatus.ApiStatusService; +import com.eactive.eai.rms.ext.djb.apistatus.ApiStatusService; import com.eactive.eai.rms.onl.common.util.DateUtil; /** @@ -88,11 +88,11 @@ public class ApiStatusMonitorJob implements Job { monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext"); ApiStatusService apiStatusUpdateService = appContext.getBean(ApiStatusService.class); - DataSourceContextHolder.setDataSourceType( DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW)); try { apiStatusUpdateService.updateApiStatus(param); + } catch (Exception e) { log.error("ApiStatusUpdateJob execution failed", e); throw new JobExecutionException(e); diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java b/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java index f9dc9b7..bcb1a5f 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/job/InflowTokenMonitorJob.java @@ -18,7 +18,7 @@ import com.eactive.eai.rms.common.context.MonitoringContext; import com.eactive.eai.rms.common.datasource.DataSourceContextHolder; import com.eactive.eai.rms.common.datasource.DataSourceTypeManager; import com.eactive.eai.rms.common.util.CommonUtil; -import com.eactive.eai.rms.data.ext.djb.inflow.InflowTokenService; +import com.eactive.eai.rms.ext.djb.inflow.InflowTokenService; import com.eactive.eai.rms.onl.common.util.DateUtil; /** diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/swing/service/SwingSendManager.java b/src/main/java/com/eactive/eai/rms/ext/djb/swing/service/SwingSendManager.java deleted file mode 100644 index 34f61a2..0000000 --- a/src/main/java/com/eactive/eai/rms/ext/djb/swing/service/SwingSendManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.eactive.eai.rms.ext.djb.swing.service; - -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Service -@RequiredArgsConstructor -public class SwingSendManager { - - public void send(String eaisvcname, long cnt, long rangeMinute) { - log.info("[Swing] 발송 준비: {} 최근 {}분 {}건", eaisvcname, rangeMinute, cnt); - // TODO: 알림 발송 로직 - } -} \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/util/UmsManager.java b/src/main/java/com/eactive/eai/rms/ext/djb/util/UmsManager.java new file mode 100644 index 0000000..ac1479d --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/ext/djb/util/UmsManager.java @@ -0,0 +1,71 @@ +package com.eactive.eai.rms.ext.djb.util; + +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.eactive.apim.portal.template.entity.MessageCode; +import com.eactive.apim.portal.template.service.MessageHandlerService; +import com.eactive.apim.portal.template.service.MessageRecipient; +import com.eactive.apim.portal.user.entity.UserInfo; +import com.eactive.eai.rms.data.entity.man.user.UserInfoService; +import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest; +import com.eactive.eai.rms.ext.djb.webhook.service.WebhookService; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class UmsManager { + + private static final Logger log = LoggerFactory.getLogger(UmsManager.class); + + private final MessageHandlerService messageHandlerService; + private final UserInfoService userInfoService; + private final WebhookService webhookService; + + /** + * 내부직원 메신저(Swing chat) 발송 + * @param roleId + * @param messageCode + * @param message + * @return + */ + public void sendMessenger(String roleId, MessageCode messageCode, String message) { + + List users = userInfoService.findAllByRoleId(roleId); + if (users.isEmpty()) { + log.warn("sendMessenger: no users found for role '{}'", roleId); + return; + } + + for (UserInfo user : users) { + MessageRecipient recipient = MessageRecipient.builder() + .userId(user.getUserid()) + .username(user.getUsername()) + .build(); + + HashMap params = new HashMap<>(); + params.put("message", message); + + messageHandlerService.publishEvent(messageCode, recipient, params); + } + } + + /** + * event를 구둑중인 제휴사에게 웹훅 발송 + * @param event + * @param apiIds + */ + public void sendWebhook(String event, List apiIds) { + + List list = webhookService.findSendList(event, apiIds); + for (WebhookSendRequest req : list) { + webhookService.send(req); + } + } +} diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/controller/WebhookSendController.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/controller/WebhookSendController.java index 3f2a1a0..f338fbe 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/controller/WebhookSendController.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/controller/WebhookSendController.java @@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.eactive.eai.rms.data.ext.djb.webhook.WebhookSendLog; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog; import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest; import com.eactive.eai.rms.ext.djb.webhook.service.WebhookReceiveService; import com.eactive.eai.rms.ext.djb.webhook.service.WebhookService; @@ -38,16 +38,12 @@ public class WebhookSendController { public ResponseEntity> sendWebhook( @RequestBody WebhookSendRequest request) { - WebhookSendLog result = webhookService.send( - request.getTargetUrl(), - request.getEventType(), - request.getData() - ); + webhookService.send(request); Map response = new HashMap<>(); - response.put("logId", result.getId()); - response.put("success", result.getSuccess()); - response.put("statusCode", result.getStatusCode()); +// response.put("logId", result.getId()); +// response.put("success", result.getSuccess()); +// response.put("statusCode", result.getStatusCode()); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/dto/WebhookSendRequest.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/dto/WebhookSendRequest.java index 6250bc0..264338e 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/dto/WebhookSendRequest.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/dto/WebhookSendRequest.java @@ -6,8 +6,9 @@ import lombok.Setter; @Getter @Setter public class WebhookSendRequest { - + private String orgId; private String targetUrl; private String eventType; + private String secret; private Object data; } \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookReqRepository.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookReqRepository.java new file mode 100644 index 0000000..7abc722 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookReqRepository.java @@ -0,0 +1,11 @@ +package com.eactive.eai.rms.ext.djb.webhook.repository; + +import com.eactive.eai.rms.data.EMSDataSource; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookReq; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +@EMSDataSource +public interface WebhookReqRepository extends JpaRepository { +} diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookSendLogRepository.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookSendLogRepository.java index 08141d6..2233a9c 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookSendLogRepository.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/repository/WebhookSendLogRepository.java @@ -10,7 +10,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import com.eactive.eai.rms.data.EMSDataSource; -import com.eactive.eai.rms.data.ext.djb.webhook.WebhookSendLog; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog; @Repository @EMSDataSource diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookSendManager.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookSendManager.java deleted file mode 100644 index d2f5f59..0000000 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookSendManager.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.eactive.eai.rms.ext.djb.webhook.service; - -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j - @Service - @RequiredArgsConstructor - public class WebhookSendManager { - - public void send(String eaisvcname, String eventType) { - log.info("[Webhook] 발송 준비: {} {}", eaisvcname, eventType); - // TODO: PTL_WEBHOOK_REQ 조인 조회 후 WebhookService.send() 호출 - } - } \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java index a7f1f5d..afde0bf 100644 --- a/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java +++ b/src/main/java/com/eactive/eai/rms/ext/djb/webhook/service/WebhookService.java @@ -2,12 +2,16 @@ package com.eactive.eai.rms.ext.djb.webhook.service; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -18,10 +22,16 @@ import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; -import com.eactive.eai.rms.data.ext.djb.webhook.WebhookSendLog; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReq; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqApi; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.QWebhookReqEvent; +import com.eactive.eai.rms.data.entity.onl.djb.webhook.WebhookSendLog; import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookPayload; +import com.eactive.eai.rms.ext.djb.webhook.dto.WebhookSendRequest; import com.eactive.eai.rms.ext.djb.webhook.repository.WebhookSendLogRepository; import com.fasterxml.jackson.databind.ObjectMapper; +import com.querydsl.core.Tuple; +import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,17 +45,58 @@ public class WebhookService { private final ObjectMapper objectMapper; private final RestTemplate restTemplate; - - private String secretKey = "HW8JtFpkPmQqsVmr0Rb81P4qDypaNIVbGf3ZNMMPfyerhOHshoKABLaMBo1HA4BldTxhw1NjYmVnoPu9IIV7cyRXjecL3b2UctyR7DnVaJausltZLLr8Qm4Bzs4wRmgf"; + @PersistenceContext(unitName = "entityManagerFactoryForEMS") + private EntityManager entityManager; private static final String HMAC_ALGORITHM = "HmacSHA256"; private static final int MAX_RETRY = 3; + + + @SuppressWarnings("unchecked") + public List findSendList(String event, List apiIds) { + QWebhookReq req = QWebhookReq.webhookReq; + QWebhookReqEvent evt = QWebhookReqEvent.webhookReqEvent; + QWebhookReqApi api = QWebhookReqApi.webhookReqApi; + + List tuples = new JPAQueryFactory(entityManager) + .select(req.orgId, req.targetUrl, req.secret, api.id.apiId) + .from(req) + .join(evt).on(req.id.eq(evt.id.webhookReqId)) + .join(api).on(req.id.eq(api.id.webhookReqId)) + .where(evt.id.eventType.eq(event) + .and(api.id.apiId.in(apiIds))) + .fetch(); + + // orgId + targetUrl + secret 기준으로 그룹핑, apiId를 data(List)로 집계 + Map resultMap = new LinkedHashMap<>(); + for (Tuple t : tuples) { + String key = t.get(req.orgId) + "|" + t.get(req.targetUrl); + resultMap.computeIfAbsent(key, k -> { + WebhookSendRequest wr = new WebhookSendRequest(); + wr.setOrgId(t.get(req.orgId)); + wr.setTargetUrl(t.get(req.targetUrl)); + wr.setSecret(t.get(req.secret)); + wr.setEventType(event); + wr.setData(new ArrayList()); + return wr; + }); + ((List) resultMap.get(key).getData()).add(t.get(api.id.apiId)); + } + + return new ArrayList<>(resultMap.values()); + } /** * 웹훅 발송 메인 메서드 */ @Transactional - public WebhookSendLog send(String targetUrl, String eventType, Object data) { + public void send(WebhookSendRequest req) { + + String targetUrl = req.getTargetUrl(); + String secret = req.getSecret(); + String eventType = req.getEventType(); + Object data = req.getData(); + WebhookSendLog sendLog = new WebhookSendLog(); sendLog.setTargetUrl(targetUrl); sendLog.setEventType(eventType); @@ -57,7 +108,7 @@ public class WebhookService { sendLog.setPayload(payloadJson); // 2. Secret Key로 HMAC-SHA256 서명 생성 - String signature = generateSignature(payloadJson); + String signature = generateSignature(secret, payloadJson); sendLog.setSignature(signature); // 3. HTTP 요청 헤더 구성 @@ -95,12 +146,13 @@ public class WebhookService { eventType, targetUrl, e.getMessage()); } - return sendLogRepository.save(sendLog); + sendLogRepository.save(sendLog); + } /** * 실패 건 재시도 - */ + @Transactional public void retryFailedWebhooks() { List failedLogs = @@ -119,16 +171,16 @@ public class WebhookService { log.error("[Webhook] 재시도 실패 - id: {}", failedLog.getId(), e); } } - } + }*/ /** * HMAC-SHA256 서명 생성 * Java 17 미만은 HexFormat 미지원이므로 직접 변환 */ - private String generateSignature(String payload) throws Exception { + private String generateSignature(String secret, String payload) throws Exception { Mac mac = Mac.getInstance(HMAC_ALGORITHM); SecretKeySpec keySpec = new SecretKeySpec( - secretKey.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM); + secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM); mac.init(keySpec); byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8)); return bytesToHex(hash); diff --git a/src/main/java/com/eactive/eai/rms/onl/common/service/ums/UmsDispatchService.java b/src/main/java/com/eactive/eai/rms/onl/common/service/ums/UmsDispatchService.java index dc952fa..8d3e521 100644 --- a/src/main/java/com/eactive/eai/rms/onl/common/service/ums/UmsDispatchService.java +++ b/src/main/java/com/eactive/eai/rms/onl/common/service/ums/UmsDispatchService.java @@ -68,14 +68,14 @@ public class UmsDispatchService { log.info("UmsDispatchService initialized - KjbUmsService and KjbEmailSendModule ready via singleton"); } -// private KjbUmsService getUmsService() { -// return KjbUmsService.getInstance(); -// } -// -// private KjbEmailSendModule getEmailModule() { -// return KjbEmailSendModule.getInstance(); -// } -// + private KjbUmsService getUmsService() { + return KjbUmsService.getInstance(); + } + + private KjbEmailSendModule getEmailModule() { + return KjbEmailSendModule.getInstance(); + } + @SuppressWarnings("unchecked") @Transactional(transactionManager = "transactionManagerForEMS") public void processMessages() { @@ -112,7 +112,7 @@ public class UmsDispatchService { log.info("Starting to submit {} messages for processing", messagesToProcess.size()); for (MessageRequest message : messagesToProcess) { - //submitMessageTask(message); + submitMessageTask(message); } } else { log.info("No pending messages found"); @@ -123,40 +123,40 @@ public class UmsDispatchService { } -// private void submitMessageTask(MessageRequest message) { -// int queueSize = executorService.getQueue().size(); -// log.debug("Current queue size before submission: {}", queueSize); -// -// executorService.submit(() -> { -// try { -// transactionTemplate.execute(status -> { -// try { -// // 광주은행에서는 사용하지 않는 메소드 -//// processMessage(message); -// -// if (KjbUmsService.isAllowChannel(message)) { -// getUmsService().send(message); -// } -// -// if (KjbEmailSendModule.isAllowChannel(message)) { -// getEmailModule().sendEmail(message); -// } -// -// return null; -// } catch (Exception e) { -// log.error("Failed to process message: {} - Error: {}", -// message.getId(), e.getMessage(), e); -// updateMessageStatus(message, "FAILED"); -// status.setRollbackOnly(); -// return null; -// } -// }); -// } catch (Exception e) { -// log.error("Transaction execution failed for message: {} - Error: {}", -// message.getId(), e.getMessage(), e); -// } -// }); -// } + private void submitMessageTask(MessageRequest message) { + int queueSize = executorService.getQueue().size(); + log.debug("Current queue size before submission: {}", queueSize); + + executorService.submit(() -> { + try { + transactionTemplate.execute(status -> { + try { + // 광주은행에서는 사용하지 않는 메소드 +// processMessage(message); + + if (KjbUmsService.isAllowChannel(message)) { + getUmsService().send(message); + } + + if (KjbEmailSendModule.isAllowChannel(message)) { + getEmailModule().sendEmail(message); + } + + return null; + } catch (Exception e) { + log.error("Failed to process message: {} - Error: {}", + message.getId(), e.getMessage(), e); + updateMessageStatus(message, "FAILED"); + status.setRollbackOnly(); + return null; + } + }); + } catch (Exception e) { + log.error("Transaction execution failed for message: {} - Error: {}", + message.getId(), e.getMessage(), e); + } + }); + } /** * kbank 방식으로 광주은행에서는 사용하지 않음