UCO 목표량 google chat webhook 보내는 로직 추가
This commit is contained in:
parent
eb71dd535c
commit
751880cc4a
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.goi.integration.google.api;
|
||||||
|
|
||||||
|
import com.goi.integration.google.dto.ChatBroadcastRequest;
|
||||||
|
import com.goi.integration.google.dto.ChatBroadcastResult;
|
||||||
|
import com.goi.integration.google.service.GoogleChatBroadcastService;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* crm-rest-api 가 호출하는 내부 엔드포인트.
|
||||||
|
* 전체 경로: POST {context-path}/google/chat/broadcast
|
||||||
|
* = /integration-service/google/chat/broadcast
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/google/chat")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GoogleChatController {
|
||||||
|
|
||||||
|
private final GoogleChatBroadcastService broadcastService;
|
||||||
|
|
||||||
|
@PostMapping("/broadcast")
|
||||||
|
public ChatBroadcastResult broadcast(@RequestBody ChatBroadcastRequest request) {
|
||||||
|
return broadcastService.broadcast(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.goi.integration.google.client;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Chat incoming webhook 전송 클라이언트.
|
||||||
|
* base-url 을 고정하지 않고, 호출 시 전달받은 webhook URL(절대주소) 로 POST 한다.
|
||||||
|
* payload 는 단순 text 메시지: {"text": "..."} (Google Chat 포맷: *굵게*, \n, <URL|라벨>).
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class GoogleChatClient {
|
||||||
|
|
||||||
|
private final WebClient webClient = WebClient.builder().build();
|
||||||
|
|
||||||
|
public void sendText(String webhookUrl, String text) {
|
||||||
|
webClient.post()
|
||||||
|
.uri(webhookUrl)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(Map.of("text", text))
|
||||||
|
.retrieve()
|
||||||
|
.onStatus(
|
||||||
|
status -> status.is4xxClientError() || status.is5xxServerError(),
|
||||||
|
resp -> resp.bodyToMono(String.class)
|
||||||
|
.map(body -> new RuntimeException(
|
||||||
|
"GCHAT_HTTP_ERROR: " + resp.statusCode() + " body=" + body))
|
||||||
|
)
|
||||||
|
.bodyToMono(String.class)
|
||||||
|
.block();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.goi.integration.google.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Chat 연동 설정.
|
||||||
|
* driverId(String) -> 해당 기사 전용 Google Chat incoming webhook URL.
|
||||||
|
* 실제 URL 은 env 로만 주입한다 (application.yaml 의 ext.gchat.driver-webhooks 참고).
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "ext.gchat")
|
||||||
|
public class GoogleChatProperties {
|
||||||
|
|
||||||
|
/** key = driverId(문자열), value = webhook URL */
|
||||||
|
private Map<String, String> driverWebhooks = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.goi.integration.google.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* crm-rest-api 가 보내는 일괄 전송 요청.
|
||||||
|
* date 는 로깅/추적용. messages 의 각 항목이 기사별 방으로 전송된다.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ChatBroadcastRequest {
|
||||||
|
private LocalDate date;
|
||||||
|
private List<DriverChatMessage> messages;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.goi.integration.google.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 일괄 전송 결과 요약.
|
||||||
|
* skipped = 해당 driverId 의 webhook 이 env 에 설정되지 않아 건너뜀.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class ChatBroadcastResult {
|
||||||
|
private int requested;
|
||||||
|
private int sent;
|
||||||
|
private int failed;
|
||||||
|
private int skipped;
|
||||||
|
private List<Detail> details;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public static class Detail {
|
||||||
|
private Long driverId;
|
||||||
|
private String status; // SENT / FAILED / SKIPPED
|
||||||
|
private String error; // FAILED 일 때만
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.goi.integration.google.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 기사 1명에게 보낼 메시지 1건.
|
||||||
|
* webhook URL 은 crm 이 넘기지 않는다 — integration 이 driverId 로 env 에서 해석.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class DriverChatMessage {
|
||||||
|
private Long driverId;
|
||||||
|
private String text;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.goi.integration.google.service;
|
||||||
|
|
||||||
|
import com.goi.integration.google.client.GoogleChatClient;
|
||||||
|
import com.goi.integration.google.config.GoogleChatProperties;
|
||||||
|
import com.goi.integration.google.dto.ChatBroadcastRequest;
|
||||||
|
import com.goi.integration.google.dto.ChatBroadcastResult;
|
||||||
|
import com.goi.integration.google.dto.ChatBroadcastResult.Detail;
|
||||||
|
import com.goi.integration.google.dto.DriverChatMessage;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* driverId -> env webhook 해석 후 기사별 방으로 메시지를 fan-out 한다.
|
||||||
|
* 한 기사 실패가 전체를 막지 않도록 건별 try/catch 로 격리하고 결과를 집계한다.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GoogleChatBroadcastService {
|
||||||
|
|
||||||
|
private final GoogleChatProperties props;
|
||||||
|
private final GoogleChatClient client;
|
||||||
|
|
||||||
|
public ChatBroadcastResult broadcast(ChatBroadcastRequest request) {
|
||||||
|
|
||||||
|
List<DriverChatMessage> messages =
|
||||||
|
request.getMessages() != null ? request.getMessages() : List.of();
|
||||||
|
|
||||||
|
log.info("[GCHAT][BROADCAST][REQUEST] date={}, drivers={}",
|
||||||
|
request.getDate(), messages.size());
|
||||||
|
|
||||||
|
int sent = 0, failed = 0, skipped = 0;
|
||||||
|
List<Detail> details = new ArrayList<>();
|
||||||
|
|
||||||
|
for (DriverChatMessage m : messages) {
|
||||||
|
|
||||||
|
String webhook = (m.getDriverId() == null)
|
||||||
|
? null
|
||||||
|
: props.getDriverWebhooks().get(String.valueOf(m.getDriverId()));
|
||||||
|
|
||||||
|
// 1) 웹훅 미설정 → 건너뜀
|
||||||
|
if (webhook == null || webhook.isBlank()) {
|
||||||
|
skipped++;
|
||||||
|
details.add(Detail.builder()
|
||||||
|
.driverId(m.getDriverId())
|
||||||
|
.status("SKIPPED")
|
||||||
|
.error("no webhook configured")
|
||||||
|
.build());
|
||||||
|
log.warn("[GCHAT][BROADCAST][SKIP] driverId={} (웹훅 미설정)", m.getDriverId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 전송
|
||||||
|
try {
|
||||||
|
client.sendText(webhook, m.getText());
|
||||||
|
sent++;
|
||||||
|
details.add(Detail.builder()
|
||||||
|
.driverId(m.getDriverId())
|
||||||
|
.status("SENT")
|
||||||
|
.build());
|
||||||
|
} catch (Exception e) {
|
||||||
|
failed++;
|
||||||
|
details.add(Detail.builder()
|
||||||
|
.driverId(m.getDriverId())
|
||||||
|
.status("FAILED")
|
||||||
|
.error(e.getMessage())
|
||||||
|
.build());
|
||||||
|
log.error("[GCHAT][BROADCAST][FAIL] driverId={}, error={}",
|
||||||
|
m.getDriverId(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("[GCHAT][BROADCAST][DONE] requested={}, sent={}, failed={}, skipped={}",
|
||||||
|
messages.size(), sent, failed, skipped);
|
||||||
|
|
||||||
|
return ChatBroadcastResult.builder()
|
||||||
|
.requested(messages.size())
|
||||||
|
.sent(sent)
|
||||||
|
.failed(failed)
|
||||||
|
.skipped(skipped)
|
||||||
|
.details(details)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,21 @@ ext:
|
||||||
acc:
|
acc:
|
||||||
base-url: http://acc-rest-api:8084/acc-rest-api
|
base-url: http://acc-rest-api:8084/acc-rest-api
|
||||||
internal-token: ${ACC_INTERNAL_TOKEN}
|
internal-token: ${ACC_INTERNAL_TOKEN}
|
||||||
|
gchat:
|
||||||
|
# driverId -> 해당 기사 전용 Google Chat incoming webhook URL
|
||||||
|
# 값은 env 로 주입
|
||||||
|
driver-webhooks:
|
||||||
|
"169": ${GCHAT_WEBHOOK_DRIVER_169:} # H.K
|
||||||
|
"195": ${GCHAT_WEBHOOK_DRIVER_195:} # J.S
|
||||||
|
"199": ${GCHAT_WEBHOOK_DRIVER_199:} # J.O
|
||||||
|
"178": ${GCHAT_WEBHOOK_DRIVER_178:} # T.K
|
||||||
|
"173": ${GCHAT_WEBHOOK_DRIVER_173:} # S.K
|
||||||
|
"194": ${GCHAT_WEBHOOK_DRIVER_194:} # Y.S
|
||||||
|
"192": ${GCHAT_WEBHOOK_DRIVER_192:} # S.S
|
||||||
|
"206": ${GCHAT_WEBHOOK_DRIVER_206:} # D.L
|
||||||
|
"171": ${GCHAT_WEBHOOK_DRIVER_171:} # M.K
|
||||||
|
"243": ${GCHAT_WEBHOOK_DRIVER_243:} # S.L
|
||||||
|
"240": ${GCHAT_WEBHOOK_DRIVER_240:} # T.Y
|
||||||
samsara:
|
samsara:
|
||||||
base-url: https://api.samsara.com
|
base-url: https://api.samsara.com
|
||||||
api-token: ${SAMSARA_API_TOKEN} # 반드시 env 로
|
api-token: ${SAMSARA_API_TOKEN} # 반드시 env 로
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue