From 43579d67b557a4faebc2f7ea0324ed3e3dab34ee Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 5 Apr 2026 21:37:37 +0500 Subject: [PATCH] . --- .../FacebookLeadCallCenterProperties.java | 109 +++++++++++++++++- .../FacebookLeadCallCenterSyncService.java | 99 +++++++++++++--- src/main/resources/application.properties | 3 + 3 files changed, 193 insertions(+), 18 deletions(-) diff --git a/src/main/java/kz/konturai/parser/config/FacebookLeadCallCenterProperties.java b/src/main/java/kz/konturai/parser/config/FacebookLeadCallCenterProperties.java index c567165..6441bfc 100644 --- a/src/main/java/kz/konturai/parser/config/FacebookLeadCallCenterProperties.java +++ b/src/main/java/kz/konturai/parser/config/FacebookLeadCallCenterProperties.java @@ -1,22 +1,127 @@ package kz.konturai.parser.config; -import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "facebook.leads.call-center") -@Data public class FacebookLeadCallCenterProperties { private boolean enabled = false; private String baseUrl = "http://localhost:8080"; private String customerPath = "/proxy/customer/customers"; private String interactionPath = "/proxy/interaction/interactions"; + private String authLoginPath = "/proxy/auth/auth/login"; + private String authUsername = "admin"; + private String authPassword = "admin123"; private String actorUser = "facebook-lead-bot"; private String actorRole = "admin"; private String interactionChannel = "webchat"; private String interactionQueueId = "q_main"; private int timeoutMs = 30000; private String subjectPrefix = "[Facebook]"; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } + + public String getCustomerPath() { + return customerPath; + } + + public void setCustomerPath(String customerPath) { + this.customerPath = customerPath; + } + + public String getInteractionPath() { + return interactionPath; + } + + public void setInteractionPath(String interactionPath) { + this.interactionPath = interactionPath; + } + + public String getAuthLoginPath() { + return authLoginPath; + } + + public void setAuthLoginPath(String authLoginPath) { + this.authLoginPath = authLoginPath; + } + + public String getAuthUsername() { + return authUsername; + } + + public void setAuthUsername(String authUsername) { + this.authUsername = authUsername; + } + + public String getAuthPassword() { + return authPassword; + } + + public void setAuthPassword(String authPassword) { + this.authPassword = authPassword; + } + + public String getActorUser() { + return actorUser; + } + + public void setActorUser(String actorUser) { + this.actorUser = actorUser; + } + + public String getActorRole() { + return actorRole; + } + + public void setActorRole(String actorRole) { + this.actorRole = actorRole; + } + + public String getInteractionChannel() { + return interactionChannel; + } + + public void setInteractionChannel(String interactionChannel) { + this.interactionChannel = interactionChannel; + } + + public String getInteractionQueueId() { + return interactionQueueId; + } + + public void setInteractionQueueId(String interactionQueueId) { + this.interactionQueueId = interactionQueueId; + } + + public int getTimeoutMs() { + return timeoutMs; + } + + public void setTimeoutMs(int timeoutMs) { + this.timeoutMs = timeoutMs; + } + + public String getSubjectPrefix() { + return subjectPrefix; + } + + public void setSubjectPrefix(String subjectPrefix) { + this.subjectPrefix = subjectPrefix; + } } diff --git a/src/main/java/kz/konturai/parser/service/FacebookLeadCallCenterSyncService.java b/src/main/java/kz/konturai/parser/service/FacebookLeadCallCenterSyncService.java index 879f33a..020090b 100644 --- a/src/main/java/kz/konturai/parser/service/FacebookLeadCallCenterSyncService.java +++ b/src/main/java/kz/konturai/parser/service/FacebookLeadCallCenterSyncService.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.JsonNode; import kz.konturai.parser.config.FacebookLeadCallCenterProperties; import kz.konturai.parser.model.FacebookLead; import kz.konturai.parser.repository.FacebookLeadRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.client.WebClient; @@ -18,16 +18,24 @@ import java.util.List; import java.util.Map; @Service -@RequiredArgsConstructor -@Slf4j public class FacebookLeadCallCenterSyncService { private static final List RETRYABLE_SYNC_STATUSES = List.of("PENDING", "FAILED"); + private static final Logger log = LoggerFactory.getLogger(FacebookLeadCallCenterSyncService.class); private final FacebookLeadCallCenterProperties properties; private final FacebookLeadRepository facebookLeadRepository; private final WebClient.Builder webClientBuilder; + public FacebookLeadCallCenterSyncService( + FacebookLeadCallCenterProperties properties, + FacebookLeadRepository facebookLeadRepository, + WebClient.Builder webClientBuilder) { + this.properties = properties; + this.facebookLeadRepository = facebookLeadRepository; + this.webClientBuilder = webClientBuilder; + } + public void syncPendingLeads() { if (!properties.isEnabled()) { return; @@ -64,8 +72,10 @@ public class FacebookLeadCallCenterSyncService { FacebookLead managedLead = loadManagedLead(lead); try { + Map authHeaders = resolveAuthHeaders(); + if (!StringUtils.hasText(managedLead.getCallCenterCustomerId())) { - String customerId = createCustomer(managedLead); + String customerId = createCustomer(managedLead, authHeaders); managedLead.setCallCenterCustomerId(customerId); managedLead.setCallCenterSyncStatus("CUSTOMER_CREATED"); managedLead.setCallCenterSyncError(null); @@ -75,7 +85,7 @@ public class FacebookLeadCallCenterSyncService { } if (!StringUtils.hasText(managedLead.getCallCenterInteractionId())) { - String interactionId = createInteraction(managedLead); + String interactionId = createInteraction(managedLead, authHeaders); managedLead.setCallCenterInteractionId(interactionId); log.info("Facebook lead {} linked to call-center interaction {}", managedLead.getExternalCommentId(), interactionId); @@ -107,7 +117,50 @@ public class FacebookLeadCallCenterSyncService { return lead; } - private String createCustomer(FacebookLead lead) { + private Map resolveAuthHeaders() { + if (StringUtils.hasText(properties.getAuthUsername()) && StringUtils.hasText(properties.getAuthPassword())) { + try { + String accessToken = loginAndGetAccessToken(); + return Map.of("Authorization", "Bearer " + accessToken); + } catch (Exception e) { + log.warn("Call-center bearer auth failed. Falling back to legacy headers. message={}", + shorten(e.getMessage())); + } + } + + Map headers = new LinkedHashMap<>(); + headers.put("X-User", properties.getActorUser()); + headers.put("X-Role", properties.getActorRole()); + return headers; + } + + private String loginAndGetAccessToken() { + Map payload = Map.of( + "username", properties.getAuthUsername(), + "password", properties.getAuthPassword() + ); + + try { + JsonNode response = callCenterClient().post() + .uri(properties.getAuthLoginPath()) + .bodyValue(payload) + .retrieve() + .bodyToMono(JsonNode.class) + .block(Duration.ofMillis(properties.getTimeoutMs())); + + String accessToken = response != null ? response.path("access_token").asText(null) : null; + if (!StringUtils.hasText(accessToken)) { + throw new IllegalStateException("Call-center auth response does not contain access_token"); + } + return accessToken; + } catch (WebClientResponseException e) { + log.error("Call-center auth login failed. status={}, body={}", + e.getStatusCode().value(), e.getResponseBodyAsString()); + throw e; + } + } + + private String createCustomer(FacebookLead lead, Map authHeaders) { Map payload = new LinkedHashMap<>(); payload.put("display_name", customerDisplayName(lead)); payload.put("phones", List.of()); @@ -115,10 +168,11 @@ public class FacebookLeadCallCenterSyncService { payload.put("tags", List.of("facebook", "hot-lead")); try { - JsonNode response = callCenterClient().post() - .uri(properties.getCustomerPath()) - .header("X-User", properties.getActorUser()) - .header("X-Role", properties.getActorRole()) + WebClient.RequestBodySpec request = callCenterClient().post() + .uri(properties.getCustomerPath()); + request = applyHeaders(request, authHeaders); + + JsonNode response = request .bodyValue(payload) .retrieve() .bodyToMono(JsonNode.class) @@ -136,7 +190,7 @@ public class FacebookLeadCallCenterSyncService { } } - private String createInteraction(FacebookLead lead) { + private String createInteraction(FacebookLead lead, Map authHeaders) { if (!StringUtils.hasText(lead.getCallCenterCustomerId())) { throw new IllegalStateException("Call-center customer must be created before interaction"); } @@ -150,10 +204,11 @@ public class FacebookLeadCallCenterSyncService { ); try { - JsonNode response = callCenterClient().post() - .uri(properties.getInteractionPath()) - .header("X-User", properties.getActorUser()) - .header("X-Role", properties.getActorRole()) + WebClient.RequestBodySpec request = callCenterClient().post() + .uri(properties.getInteractionPath()); + request = applyHeaders(request, authHeaders); + + JsonNode response = request .bodyValue(payload) .retrieve() .bodyToMono(JsonNode.class) @@ -177,6 +232,18 @@ public class FacebookLeadCallCenterSyncService { .build(); } + private WebClient.RequestBodySpec applyHeaders( + WebClient.RequestBodySpec request, + Map headers) { + WebClient.RequestBodySpec current = request; + for (Map.Entry header : headers.entrySet()) { + if (StringUtils.hasText(header.getValue())) { + current = current.header(header.getKey(), header.getValue()); + } + } + return current; + } + private String customerDisplayName(FacebookLead lead) { String authorName = StringUtils.hasText(lead.getAuthorName()) ? lead.getAuthorName().trim() diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 44495af..72bc87d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -155,6 +155,9 @@ facebook.leads.call-center.enabled=${FACEBOOK_LEADS_CALL_CENTER_ENABLED:true} facebook.leads.call-center.base-url=${FACEBOOK_LEADS_CALL_CENTER_BASE_URL:http://localhost:8080} facebook.leads.call-center.customer-path=${FACEBOOK_LEADS_CALL_CENTER_CUSTOMER_PATH:/proxy/customer/customers} facebook.leads.call-center.interaction-path=${FACEBOOK_LEADS_CALL_CENTER_INTERACTION_PATH:/proxy/interaction/interactions} +facebook.leads.call-center.auth-login-path=${FACEBOOK_LEADS_CALL_CENTER_AUTH_LOGIN_PATH:/proxy/auth/auth/login} +facebook.leads.call-center.auth-username=${FACEBOOK_LEADS_CALL_CENTER_AUTH_USERNAME:admin} +facebook.leads.call-center.auth-password=${FACEBOOK_LEADS_CALL_CENTER_AUTH_PASSWORD:admin123} facebook.leads.call-center.actor-user=${FACEBOOK_LEADS_CALL_CENTER_ACTOR_USER:facebook-lead-bot} facebook.leads.call-center.actor-role=${FACEBOOK_LEADS_CALL_CENTER_ACTOR_ROLE:admin} facebook.leads.call-center.interaction-channel=${FACEBOOK_LEADS_CALL_CENTER_INTERACTION_CHANNEL:webchat}