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:
|
||||
base-url: http://acc-rest-api:8084/acc-rest-api
|
||||
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:
|
||||
base-url: https://api.samsara.com
|
||||
api-token: ${SAMSARA_API_TOKEN} # 반드시 env 로
|
||||
|
|
|
|||
Loading…
Reference in New Issue