feat: initialize loyalty agent service structure, security configuration, and campaign MCP service orchestration

This commit is contained in:
SonPhung
2026-07-21 23:54:13 +07:00
parent 5b128d8965
commit 64914c8b9a
5 changed files with 119 additions and 67 deletions

View File

@@ -2,7 +2,6 @@ package dev.sonpx.loyalty.agent.config;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -18,39 +17,10 @@ public class AgentConfig {
2. When a tool result contains an `agent_instruction`, you MUST strictly follow its formatting rules when presenting the relevant data to the user. Do not output raw JSON data unless explicitly asked.
""";
@Value("${loyalty.core.base-url}")
private String coreBaseUrl;
@Bean
public ChatClient loyaltyChatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultSystem(AGENT_SYSTEM_PROMPT)
.build();
}
// @Bean
// public RestClient coreRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
// return RestClient.builder()
// .baseUrl(coreBaseUrl)
// .requestInterceptor((request, body, execution) -> {
// org.springframework.security.core.Authentication principal =
// new org.springframework.security.authentication.AnonymousAuthenticationToken(
// "key", "loyalty-agent-service",
// org.springframework.security.core.authority.AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
// OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
// .withClientRegistrationId("keycloak")
// .principal(principal)
// .build();
// try {
// OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
// if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
// request.getHeaders().setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return execution.execute(request, body);
// })
// .build();
// }
}

View File

@@ -12,33 +12,13 @@ import org.springframework.security.web.SecurityFilterChain;
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/v1/agent/**", "/api/v1/conversations/**", "/ws/**", "/error").permitAll()
.anyRequest().authenticated()
);
// .oauth2Login(Customizer.withDefaults())
// .oauth2Client(Customizer.withDefaults());
return http.build();
}
// @Bean
// public OAuth2AuthorizedClientManager authorizedClientManager(
// ClientRegistrationRepository clientRegistrationRepository,
// OAuth2AuthorizedClientService authorizedClientService) {
//
// OAuth2AuthorizedClientProvider authorizedClientProvider =
// OAuth2AuthorizedClientProviderBuilder.builder()
// .clientCredentials()
// .build();
//
// AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
// new AuthorizedClientServiceOAuth2AuthorizedClientManager(
// clientRegistrationRepository, authorizedClientService);
// authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
//
// return authorizedClientManager;
// }
}

View File

@@ -23,21 +23,6 @@ spring:
connections:
loyalty:
url: ${MCP_SERVER_URL:http://localhost:9331}
# security:
# oauth2:
# client:
# registration:
# keycloak:
# client-id: ols-cli
# client-secret: ${KEYCLOAK_CLIENT_SECRET:1jV8NSAeybIqmUK0AWhI8hgdo1q5itj7}
# authorization-grant-type: client_credentials
# provider:
# keycloak:
# token-uri: ${KEYCLOAK_TOKEN_URI:http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token}
#loyalty:
# core:
# base-url: ${LOYALTY_CORE_BASE_URL:http://192.168.99.242:8081}
logging:
pattern:

View File

@@ -23,7 +23,23 @@ public class CampaignService {
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
List<Campaign> campaigns = campaignClient.getAll(criteria, Pageable.ofSize(5));
return Result.of(campaigns, "Format these campaigns as a markdown table with columns ID, Name, Start Date, End Date, Status.");
return Result.of(campaigns, """
Khi trả về danh sách chiến dịch, BẮT BUỘC KHÔNG dùng markdown table. Hãy định dạng theo cấu trúc sau:
1. **[Tên Chiến Dịch]** (ID: `[Mã ID]`)
- 👤 Owner: [Tên Owner] (Nếu trống, ghi là: Không có)
- 📝 Mô tả: [Mô tả] (Nếu trống, ghi là: Không có. Nếu dài hơn 100 ký tự, thêm "...")
Ví dụ đầu ra ĐÚNG:
1. **Happy Birthday!** (ID: `LACO`)
- 👤 Owner: Không có
- 📝 Mô tả: Không có
2. **SummerSale GOTIT Campaign** (ID: `YZFG`)
- 👤 Owner: Ngan Ha
- 📝 Mô tả: Không có
Luôn kết thúc bằng một câu hỏi gợi mở để hướng dẫn người dùng bước tiếp theo.
""");
}
public Result<Long> count(CampaignCriteria criteria) {

101
start-all.ps1 Normal file
View File

@@ -0,0 +1,101 @@
$ErrorActionPreference = "Stop"
$ProjectRoot = $PSScriptRoot
$LogDir = Join-Path $ProjectRoot "log"
if (-not (Test-Path $LogDir)) {
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
}
if (Test-Path (Join-Path $ProjectRoot ".env")) {
Write-Host "Loading environment variables from .env"
Get-Content (Join-Path $ProjectRoot ".env") | Where-Object { $_ -match "^[^#]" -and $_ -match "=" } | ForEach-Object {
$name, $value = $_.Split("=", 2)
[System.Environment]::SetEnvironmentVariable($name, $value)
}
}
Write-Host "=========================================="
Write-Host "Cleaning up existing processes..."
Write-Host "=========================================="
foreach ($port in 9331, 9332, 9333) {
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":$port"
if ($netstat) {
foreach ($line in $netstat) {
$parts = $line -split '\s+'
$pidToKill = $parts[-1]
if ($pidToKill -and $pidToKill -ne "0") {
Write-Host "Killing process on port $port (PID: $pidToKill)..."
Stop-Process -Id $pidToKill -Force -ErrorAction SilentlyContinue
}
}
}
}
$ProcessList = @()
try {
Write-Host "=========================================="
Write-Host "Starting Loyalty Agent Services"
Write-Host "=========================================="
# 1. Start MCP Server
Write-Host "Starting loyalty-mcp-server..."
$mcpLog = Join-Path $LogDir "loyalty-mcp-server.log"
$mcpProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$mcpLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-mcp-server") -PassThru -WindowStyle Hidden
$ProcessList += $mcpProcess
Write-Host "loyalty-mcp-server started with PID: $($mcpProcess.Id)"
Write-Host "Waiting for loyalty-mcp-server to initialize..."
while ($true) {
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9331"
if ($netstat) { break }
Start-Sleep -Seconds 2
}
Write-Host "loyalty-mcp-server is up!"
# 2. Start Agent Service
Write-Host "Starting loyalty-agent..."
$agentLog = Join-Path $LogDir "loyalty-agent.log"
$agentProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c ..\mvnw.cmd spring-boot:run -Dmaven.test.skip=true > `"$agentLog`" 2>&1" -WorkingDirectory (Join-Path $ProjectRoot "loyalty-agent") -PassThru -WindowStyle Hidden
$ProcessList += $agentProcess
Write-Host "loyalty-agent started with PID: $($agentProcess.Id)"
Write-Host "Waiting for loyalty-agent to initialize..."
while ($true) {
$netstat = netstat -ano | Select-String "LISTENING" | Select-String ":9332"
if ($netstat) { break }
Start-Sleep -Seconds 2
}
Write-Host "loyalty-agent is up!"
# 3. Start Frontend UI
Write-Host "Starting Frontend UI..."
$uiLog = Join-Path $LogDir "frontend.log"
$uiProcess = Start-Process -FilePath "cmd.exe" -ArgumentList "/c pnpm dev > `"$uiLog`" 2>&1" -WorkingDirectory $ProjectRoot -PassThru -WindowStyle Hidden
$ProcessList += $uiProcess
Write-Host "Frontend UI started with PID: $($uiProcess.Id)"
Write-Host "=========================================="
Write-Host "All services started successfully!"
Write-Host "Log files are stored in: $LogDir"
Write-Host "- $mcpLog"
Write-Host "- $agentLog"
Write-Host "- $uiLog"
Write-Host "=========================================="
Write-Host "Press Ctrl+C to stop all services..."
# Wait indefinitely
while ($true) {
Start-Sleep -Seconds 1
}
}
finally {
Write-Host "`nStopping services..."
foreach ($p in $ProcessList) {
if (-not $p.HasExited) {
taskkill /PID $p.Id /T /F 2>&1 | Out-Null
Write-Host "Stopped process tree for PID $($p.Id)"
}
}
Write-Host "All services stopped."
}