diff --git a/.cursor/rules/codegraph.mdc b/.cursor/rules/codegraph.mdc new file mode 100644 index 0000000..3f23cf6 --- /dev/null +++ b/.cursor/rules/codegraph.mdc @@ -0,0 +1,38 @@ +--- +description: CodeGraph MCP usage guide — when to use which tool +alwaysApply: true +--- + +## CodeGraph + +This project has a CodeGraph MCP server (`codegraph_*` tools) configured. CodeGraph is a tree-sitter-parsed knowledge graph of every symbol, edge, and file. Reads are sub-millisecond and return structural information grep cannot. + +### When to prefer codegraph over native search + +Use codegraph for **structural** questions — what calls what, what would break, where is X defined, what is X's signature. Use native grep/read only for **literal text** queries (string contents, comments, log messages) or after you already have a specific file open. + +| Question | Tool | +|---|---| +| "Where is X defined?" / "Find symbol named X" | `codegraph_search` | +| "What calls function Y?" | `codegraph_callers` | +| "What does Y call?" | `codegraph_callees` | +| "What would break if I changed Z?" | `codegraph_impact` | +| "Show me Y's signature / source / docstring" | `codegraph_node` | +| "Give me focused context for a task/area" | `codegraph_context` | +| "See several related symbols' source at once" | `codegraph_explore` | +| "What files exist under path/" | `codegraph_files` | +| "Is the index healthy?" | `codegraph_status` | + +### Rules of thumb + +- **Answer directly — don't delegate exploration.** For "how does X work" / architecture / trace questions, answer with 2-3 codegraph calls: `codegraph_context` first, then ONE `codegraph_explore` for the source of the symbols it surfaces. Codegraph IS the pre-built index, so spawning a separate file-reading sub-task/agent — or running a grep + read loop — repeats work codegraph already did and costs more for the same answer. +- **Trust codegraph results.** They come from a full AST parse. Do NOT re-verify them with grep — that's slower, less accurate, and wastes context. +- **Don't grep first** when looking up a symbol by name. `codegraph_search` is faster and returns kind + location + signature in one call. +- **Don't chain `codegraph_search` + `codegraph_node`** when you just want context — `codegraph_context` is one call. +- **Don't loop `codegraph_node` over many symbols** — one `codegraph_explore` call returns several symbols' source grouped in a single capped call, while each separate node/Read call re-reads the whole context and costs far more. +- **Index lag**: the file watcher debounces ~500ms behind writes; don't re-query immediately after editing a file in the same turn. + +### If `.codegraph/` doesn't exist + +The MCP server returns "not initialized." Ask the user: *"I notice this project doesn't have CodeGraph initialized. Want me to run `codegraph init -i` to build the index?"* + diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java index 228e0ba..66609e8 100644 --- a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java +++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/config/WebSocketConfig.java @@ -1,5 +1,6 @@ package dev.sonpx.loyalty.agent.config; +import java.util.UUID; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; @@ -25,7 +26,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override protected java.security.Principal determineUser(org.springframework.http.server.ServerHttpRequest request, org.springframework.web.socket.WebSocketHandler wsHandler, java.util.Map attributes) { return new java.security.Principal() { - private final String id = java.util.UUID.randomUUID().toString(); + private final String id = UUID.randomUUID().toString(); @Override public String getName() { return id; diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java index af99af6..3b9af0b 100644 --- a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java +++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/controller/AgentController.java @@ -1,12 +1,12 @@ package dev.sonpx.loyalty.agent.controller; import dev.sonpx.loyalty.agent.service.LoyaltyAgentService; -import org.springframework.messaging.handler.annotation.Header; +import java.security.Principal; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin(origins = "*") @@ -24,7 +24,7 @@ public class AgentController { public record ChatRequest(String conversationId, String messageId, String prompt, String activeCampaignId) {} @MessageMapping("/chat") - public void chat(@Payload ChatRequest request, java.security.Principal principal) { + public void chat(@Payload ChatRequest request, Principal principal) { agentService.chat(request, principal.getName()); } diff --git a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java index 6ca63ef..2e0b16d 100644 --- a/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java +++ b/loyalty-agent/src/main/java/dev/sonpx/loyalty/agent/service/LoyaltyAgentService.java @@ -1,24 +1,27 @@ package dev.sonpx.loyalty.agent.service; -import org.springframework.ai.chat.client.ChatClient; -import org.springframework.ai.tool.ToolCallbackProvider; -import org.springframework.stereotype.Service; +import dev.sonpx.loyalty.agent.controller.AgentController; +import dev.sonpx.loyalty.agent.domain.AgentEvent; +import java.util.ArrayList; import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.tool.ToolCallback; +import org.springframework.ai.tool.ToolCallbackProvider; +import org.springframework.ai.tool.definition.ToolDefinition; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class LoyaltyAgentService { - private final org.springframework.ai.chat.model.ChatModel chatModel; + private final ChatModel chatModel; private final List toolProviders; - private final org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate; + private final SimpMessagingTemplate messagingTemplate; - public LoyaltyAgentService(org.springframework.ai.chat.model.ChatModel chatModel, List toolProviders, org.springframework.messaging.simp.SimpMessagingTemplate messagingTemplate) { - this.chatModel = chatModel; - this.toolProviders = toolProviders; - this.messagingTemplate = messagingTemplate; - } - - public void chat(dev.sonpx.loyalty.agent.controller.AgentController.ChatRequest request, String sessionId) { + public void chat(AgentController.ChatRequest request, String sessionId) { String prompt = request.prompt(); if (request.activeCampaignId() != null && !request.activeCampaignId().isEmpty()) { prompt = "Context: Active Campaign ID is " + request.activeCampaignId() + ".\n\n" + prompt; @@ -27,21 +30,21 @@ public class LoyaltyAgentService { prompt = "System Context: The current conversationId (session ID) is " + request.conversationId() + ". You MUST provide this conversationId as a parameter to any tool that requires it.\n\n" + prompt; } - List wrappedTools = new java.util.ArrayList<>(); + List wrappedTools = new ArrayList<>(); for (ToolCallbackProvider provider : toolProviders) { - for (org.springframework.ai.tool.ToolCallback tool : provider.getToolCallbacks()) { - wrappedTools.add(new org.springframework.ai.tool.ToolCallback() { + for (ToolCallback tool : provider.getToolCallbacks()) { + wrappedTools.add(new ToolCallback() { @Override - public org.springframework.ai.tool.definition.ToolDefinition getToolDefinition() { + public ToolDefinition getToolDefinition() { return tool.getToolDefinition(); } @Override public String call(String toolInput) { - messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "running")); + messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "running")); try { return tool.call(toolInput); } finally { - messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "done")); + messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.toolStatus(request.messageId(), tool.getToolDefinition().name(), "done")); } } }); @@ -54,14 +57,8 @@ public class LoyaltyAgentService { Your primary job is to orchestrate operations for our Loyalty core system. CRITICAL RULES: - 1. DO NOT create a model.client.dev.sonpx.loyalty.agent.Campaign unless you verify that Member Tiers and Point Pools exist first. - 2. Use the `listMemberTiers` tool to check for valid tier IDs. - 3. Use the `listPointPools` tool to check for valid pool IDs and ensure they have a sufficient balance. - 4. Use the `listStaticAttributes` tool when asked about configurations, properties, or static values. - 5. Only use the `createCampaign` tool once verification is complete. - 6. Always answer the user in the language they used (e.g., Vietnamese). - 7. If a tool response contains a `presentation.content` field, you MUST display its content exactly as provided to the user. Do not summarize or alter its formatting. Use the `data` field only for your internal reasoning. - 8. When asked for information, details, or status of an existing campaign, ALWAYS use the `listCampaigns` tool. DO NOT use `createCampaign` unless explicitly asked to create or add a new one. + 1. Always answer the user in the language they used (e.g., Vietnamese). + 2. If a tool response contains a `presentation.markdown` field, you MUST display its content exactly as provided to the user. Do not summarize or alter its formatting. Use the `data` field only for your internal reasoning. """) .defaultTools(wrappedTools) .build(); @@ -70,14 +67,14 @@ public class LoyaltyAgentService { .user(prompt) .stream() .content() - .doOnComplete(() -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.done(request.messageId()))) + .doOnComplete(() -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.done(request.messageId()))) .subscribe( content -> { if (content != null && !content.isEmpty()) { - messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.token(request.messageId(), content)); + messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.token(request.messageId(), content)); } }, - error -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", dev.sonpx.loyalty.agent.domain.AgentEvent.error(request.messageId(), error.getMessage())) + error -> messagingTemplate.convertAndSendToUser(sessionId, "/queue/chat-events", AgentEvent.error(request.messageId(), error.getMessage())) ); } } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java deleted file mode 100644 index 47aa5cb..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/CampaignApiClient.java +++ /dev/null @@ -1,16 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.api; - -import dev.sonpx.loyalty.mcp.client.model.Campaign; -import dev.sonpx.loyalty.mcp.client.model.OperationResult; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.service.annotation.GetExchange; -import org.springframework.web.service.annotation.PostExchange; - -public interface CampaignApiClient { - - @GetExchange("/api/campaign/csr/id/generate") - String generateCampaignId(); - - @PostExchange("/api/campaign/csr/create-draft") - OperationResult createCampaignDraft(@RequestBody Campaign request); -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java deleted file mode 100644 index 6375b6d..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/MemberTierApiClient.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.api; - -import dev.sonpx.loyalty.mcp.client.model.MemberTier; -import java.util.List; -import org.springframework.web.service.annotation.GetExchange; - -public interface MemberTierApiClient { - - @GetExchange("/svc/reward/api/decide-tier-view/list?page=0&size=100&status.in=A") - List listMemberTiers(); -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java deleted file mode 100644 index 6a0c41c..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/api/PointPoolApiClient.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.api; - -import dev.sonpx.loyalty.mcp.client.model.PointPool; -import java.util.List; -import org.springframework.web.service.annotation.GetExchange; - -public interface PointPoolApiClient { - - @GetExchange("/api/pool-definition/all") - List listPointPools(); -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java deleted file mode 100644 index 6c1c90f..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/Campaign.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record Campaign(String campaignId, String name, String targetTierId, String rewardPoolId, String status) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java deleted file mode 100644 index f481fb5..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/CreateCampaignRequest.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model; - -public record CreateCampaignRequest(String campaignId, String name, String targetTierId, String rewardPoolId) { - - public CreateCampaignRequest(String name, String targetTierId, String rewardPoolId) { - this(null, name, targetTierId, rewardPoolId); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java deleted file mode 100644 index 94d2b97..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/MemberTier.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model; - -import com.fasterxml.jackson.annotation.JsonAlias; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record MemberTier( - @JsonProperty("key") @JsonAlias("tierId") String tierId, - @JsonAlias("name") String tierName, - Integer requiredPoints) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java deleted file mode 100644 index e2a3904..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/OperationResult.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record OperationResult(String errorCode, String message, T entity) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java deleted file mode 100644 index 9a13883..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/PointPool.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.math.BigDecimal; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record PointPool(String poolId, String poolName, BigDecimal balance) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java deleted file mode 100644 index 0a4f1fc..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/attribute/StaticAttributeDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.attribute; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class StaticAttributeDto { - private String attributeId; - private String attributeType; - private String attributeValue; - private String status; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java deleted file mode 100644 index 684275d..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignDto.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.reward; - -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import java.util.LinkedHashMap; -import java.util.Map; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class CampaignDto { - private String campaignId; - private String name; - private String targetTierId; - private String rewardPoolId; - private String status; - private String description; - - private Map extraData = new LinkedHashMap<>(); - - @JsonAnySetter - public void addExtraData(String key, Object value) { - this.extraData.put(key, value); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java deleted file mode 100644 index 9194c2e..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignFormulaOneDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.reward; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class CampaignFormulaOneDto { - private Double awardPerTxnAmt; - private Double txnAmt; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java deleted file mode 100644 index cf20ce9..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/CampaignRuleDto.java +++ /dev/null @@ -1,56 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.reward; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonValue; -import java.time.OffsetDateTime; -import java.util.List; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class CampaignRuleDto { - private ReferenceData campaignId; - private String ruleId; - private String ruleName; - private RuleTypeEnum ruleType; - private String description; - private OffsetDateTime effectiveFrom; - private OffsetDateTime effectiveTo; - private ReferenceData poolId; - private CampaignFormulaOneDto campaignFormulaOne; - private List formulaSequence; - - public enum RuleTypeEnum { - AWD("AWD"), - RED("RED"), - ADJ("ADJ"), - REP("REP"), - MAWD("MAWD"), - CEP("CEP"); - - private String value; - - RuleTypeEnum(String value) { - this.value = value; - } - - @JsonValue - public String getValue() { - return value; - } - - @Override - public String toString() { - return String.valueOf(value); - } - - public static RuleTypeEnum fromValue(String value) { - for (RuleTypeEnum b : RuleTypeEnum.values()) { - if (b.value.equals(value)) { - return b; - } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java deleted file mode 100644 index 4ed1a43..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/OperationResultCampaignRuleDto.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.reward; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class OperationResultCampaignRuleDto { - private Boolean success; - private String errorCode; - private String errorMessage; - private CampaignRuleDto data; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java deleted file mode 100644 index ee060f6..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/client/model/reward/ReferenceData.java +++ /dev/null @@ -1,10 +0,0 @@ -package dev.sonpx.loyalty.mcp.client.model.reward; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class ReferenceData { - private String code; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java deleted file mode 100644 index 4cc4ade..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ApiClientConfig.java +++ /dev/null @@ -1,103 +0,0 @@ -package dev.sonpx.loyalty.mcp.config; - -import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient; -import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient; -import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.web.client.RestClient; -import org.springframework.web.client.support.RestClientAdapter; -import org.springframework.web.service.invoker.HttpServiceProxyFactory; - -/** - * Configures OAuth2-enabled RestClient instances and Spring HTTP interface clients. - */ -@Configuration -public class ApiClientConfig { - - @Value("${loyalty.core.base-url}") - private String coreBaseUrl; - - - @Bean - public RestClient loyaltyRestClient(OAuth2AuthorizedClientManager authorizedClientManager) { - return RestClient.builder() - .baseUrl(coreBaseUrl) - .requestInterceptor((request, body, execution) -> { - Authentication principal = - new AnonymousAuthenticationToken( - "key", "loyalty-agent-service", - 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) { - throw new IllegalStateException("Failed to authorize loyalty API request", e); - } - return execution.execute(request, body); - }) - .build(); - } - - @Bean - public RestClient rewardRestClient(OAuth2AuthorizedClientManager authorizedClientManager) { - return RestClient.builder() - .baseUrl(coreBaseUrl + "/svc/reward") - .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) { - throw new IllegalStateException("Failed to authorize reward API request", e); - } - return execution.execute(request, body); - }) - .build(); - } - - @Bean - public MemberTierApiClient memberTierApiClient(@Qualifier("loyaltyRestClient") RestClient loyaltyRestClient) { - return createClient(loyaltyRestClient, MemberTierApiClient.class); - } - - @Bean - public PointPoolApiClient pointPoolApiClient(@Qualifier("rewardRestClient") RestClient rewardRestClient) { - return createClient(rewardRestClient, PointPoolApiClient.class); - } - - @Bean - public CampaignApiClient campaignApiClient(@Qualifier("rewardRestClient") RestClient rewardRestClient) { - return createClient(rewardRestClient, CampaignApiClient.class); - } - - private T createClient(RestClient restClient, Class clientType) { - HttpServiceProxyFactory factory = HttpServiceProxyFactory - .builderFor(RestClientAdapter.create(restClient)) - .build(); - return factory.createClient(clientType); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ClientConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ClientConfig.java index a9d8743..bf7f44b 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ClientConfig.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/ClientConfig.java @@ -3,6 +3,7 @@ package dev.sonpx.loyalty.mcp.config; import static org.springframework.security.oauth2.client.web.client.RequestAttributeClientRegistrationIdResolver.clientRegistrationId; import static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal; +import org.springframework.http.HttpStatusCode; import dev.sonpx.loyalty.mcp.reward.client.RewardClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -38,6 +39,12 @@ public class ClientConfig { principal("loyalty-service").accept(request.getAttributes()); return oauth2Interceptor.intercept(request, body, execution); }) + .defaultStatusHandler( + HttpStatusCode::isError, + (request, response) -> { + throw new RuntimeException("API Error: HTTP Status " + response.getStatusCode()); + } + ) .build(); } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java deleted file mode 100644 index debee66..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/config/McpServerConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package dev.sonpx.loyalty.mcp.config; - -import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient; -import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient; -import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient; -import dev.sonpx.loyalty.mcp.tool.LoyaltyAgentTools; -import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleTools; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestClient; - -@Configuration -public class McpServerConfig { - - @Bean - public LoyaltyAgentTools loyaltyAgentTools( - MemberTierApiClient memberTierApiClient, - PointPoolApiClient pointPoolApiClient, - CampaignApiClient campaignApiClient, - @org.springframework.beans.factory.annotation.Qualifier("loyaltyRestClient") RestClient restClient) { - return new LoyaltyAgentTools(memberTierApiClient, pointPoolApiClient, campaignApiClient, restClient); - } - - @Bean - public org.springframework.ai.tool.ToolCallbackProvider loyaltyToolsProvider( - LoyaltyAgentTools loyaltyAgentTools, - CampaignRuleTools campaignRuleTools, - dev.sonpx.loyalty.mcp.tool.reward.CampaignTools campaignTools) { - return org.springframework.ai.tool.method.MethodToolCallbackProvider.builder() - .toolObjects(loyaltyAgentTools, campaignRuleTools, campaignTools) - .build(); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/criteria/AbstractFwCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/criteria/AbstractFwCriteria.java index 26bdb66..a1f8c38 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/criteria/AbstractFwCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/criteria/AbstractFwCriteria.java @@ -10,7 +10,7 @@ import java.time.Instant; import lombok.Data; /** - * @author AnhDT Created on 2025/05/23 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public abstract class AbstractFwCriteria implements FwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java deleted file mode 100644 index a7f9d6f..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DependencyValidator.java +++ /dev/null @@ -1,44 +0,0 @@ -package dev.sonpx.loyalty.mcp.draft; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto; -import dev.sonpx.loyalty.mcp.draft.model.DraftSession; -import org.springframework.stereotype.Component; - -@Component -public class DependencyValidator { - - public void validateCampaignRequiredFields(CampaignDto campaign) { - if (campaign == null) { - throw new IllegalArgumentException("Campaign draft is empty"); - } - if (campaign.getCampaignId() == null || campaign.getCampaignId().isBlank()) { - throw new IllegalArgumentException("Campaign ID is required"); - } - if (campaign.getName() == null || campaign.getName().isBlank()) { - throw new IllegalArgumentException("Campaign name is required"); - } - } - - public void validateRulePrerequisites(DraftSession session, String targetCampaignId, String transactionCode, boolean needsPoolId, String poolId) { - if (targetCampaignId == null || targetCampaignId.isBlank()) { - throw new IllegalArgumentException("Target Campaign ID is required to add a rule"); - } - - // In a real system, we might check if targetCampaignId matches the one in draft, - // or check the Core API if the campaign exists. - - if (transactionCode == null || transactionCode.isBlank()) { - throw new IllegalArgumentException("Transaction Code is required"); - } - - if (needsPoolId && (poolId == null || poolId.isBlank())) { - throw new IllegalArgumentException("Pool ID is required for the selected rule flow"); - } - } - - public void validateSopPrerequisites(String poolId) { - if (poolId == null || poolId.isBlank()) { - throw new IllegalArgumentException("Existing Pool ID is required to create a Statement Output Pool"); - } - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java deleted file mode 100644 index bc401e4..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/DraftSessionManager.java +++ /dev/null @@ -1,57 +0,0 @@ -package dev.sonpx.loyalty.mcp.draft; - -import dev.sonpx.loyalty.mcp.draft.model.DraftSession; -import java.time.Duration; -import java.time.Instant; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import org.springframework.stereotype.Component; - -@Component -public class DraftSessionManager { - - private final Map sessions = new ConcurrentHashMap<>(); - private static final Duration TTL = Duration.ofHours(1); - - public DraftSession getSession(String conversationId) { - if (conversationId == null) { - throw new IllegalArgumentException("conversationId cannot be null"); - } - cleanupExpiredSessions(); - return sessions.compute(conversationId, (id, session) -> { - if (session == null || isExpired(session)) { - return new DraftSession(id); - } - session.setLastUpdated(Instant.now()); - return session; - }); - } - - public void updateSession(String conversationId, Consumer updater) { - if (conversationId == null) { - throw new IllegalArgumentException("conversationId cannot be null"); - } - cleanupExpiredSessions(); - sessions.compute(conversationId, (id, session) -> { - if (session == null || isExpired(session)) { - session = new DraftSession(id); - } - updater.accept(session); - session.setLastUpdated(Instant.now()); - return session; - }); - } - - public void removeSession(String conversationId) { - sessions.remove(conversationId); - } - - private boolean isExpired(DraftSession session) { - return Instant.now().isAfter(session.getLastUpdated().plus(TTL)); - } - - private void cleanupExpiredSessions() { - sessions.entrySet().removeIf(entry -> isExpired(entry.getValue())); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java deleted file mode 100644 index dc8bd95..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/CampaignDraft.java +++ /dev/null @@ -1,13 +0,0 @@ -package dev.sonpx.loyalty.mcp.draft.model; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto; -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; -import java.util.ArrayList; -import java.util.List; -import lombok.Data; - -@Data -public class CampaignDraft { - private CampaignDto campaign = new CampaignDto(); - private List rules = new ArrayList<>(); -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java deleted file mode 100644 index 1802c18..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/DraftSession.java +++ /dev/null @@ -1,19 +0,0 @@ -package dev.sonpx.loyalty.mcp.draft.model; - -import java.time.Instant; -import lombok.Data; - -@Data -public class DraftSession { - private String conversationId; - private Instant lastUpdated; - private CampaignDraft campaignDraft; - private PoolDraft poolDraft; - - public DraftSession(String conversationId) { - this.conversationId = conversationId; - this.lastUpdated = Instant.now(); - this.campaignDraft = new CampaignDraft(); - this.poolDraft = new PoolDraft(); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java deleted file mode 100644 index 1082b3f..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/draft/model/PoolDraft.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sonpx.loyalty.mcp.draft.model; - -import java.util.HashMap; -import java.util.Map; -import lombok.Data; - -@Data -public class PoolDraft { - private Map poolDefinition = new HashMap<>(); - private Map statementOutputPool = new HashMap<>(); -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/AttributeEntityType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/AttributeEntityType.java index 0073626..ba74a2b 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/AttributeEntityType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/AttributeEntityType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author Phudao - * Created on 2024/01/12 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/DerivedType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/DerivedType.java index 46e2e0e..96cff46 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/DerivedType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/DerivedType.java @@ -6,7 +6,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT Created on 2025/11/17 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodAction.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodAction.java index 65645bc..fae8b05 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodAction.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodAction.java @@ -2,8 +2,7 @@ package dev.sonpx.loyalty.mcp.enums; /** - * @author AnhDT - * Created on 2024/06/26 + * Created by SonPhung on Monday, 20-Jul-2026 */ public enum PeriodAction { CURRENT, diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodType.java index ba5208d..9a75050 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/PeriodType.java @@ -2,8 +2,7 @@ package dev.sonpx.loyalty.mcp.enums; /** - * @author AnhDT - * Created on 2024/06/26 + * Created by SonPhung on Monday, 20-Jul-2026 */ public enum PeriodType { YEAR, diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/StatisticPeriod.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/StatisticPeriod.java index 349819b..58fa510 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/StatisticPeriod.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/enums/StatisticPeriod.java @@ -9,8 +9,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/06/26 + * Created by SonPhung on Monday, 20-Jul-2026 * {@link #LAST_7_DAYS}, * {@link #LAST_30_DAYS}, * {@link #LAST_3_MONTHS}, diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/DoubleFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/DoubleFilter.java index 52dd09e..fbf8db9 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/DoubleFilter.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/DoubleFilter.java @@ -2,6 +2,6 @@ package dev.sonpx.loyalty.mcp.filter; /** - * @author AnhDT Created on 2025/12/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ public class DoubleFilter extends RangeFilter {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/InstantFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/InstantFilter.java index 3df4fc0..f8c2129 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/InstantFilter.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/InstantFilter.java @@ -4,6 +4,6 @@ package dev.sonpx.loyalty.mcp.filter; import java.time.Instant; /** - * @author AnhDT Created on 2025/12/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ public class InstantFilter extends RangeFilter {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateFilter.java index 0849eb8..c32b244 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateFilter.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateFilter.java @@ -4,6 +4,6 @@ package dev.sonpx.loyalty.mcp.filter; import java.time.LocalDate; /** - * @author AnhDT Created on 2025/12/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ public class LocalDateFilter extends RangeFilter {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateTimeFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateTimeFilter.java index 2dea15c..1d1e769 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateTimeFilter.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LocalDateTimeFilter.java @@ -4,6 +4,6 @@ package dev.sonpx.loyalty.mcp.filter; import java.time.LocalDateTime; /** - * @author AnhDT Created on 2025/12/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ public class LocalDateTimeFilter extends RangeFilter {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LongFilter.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LongFilter.java index 0d724f0..8c8b842 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LongFilter.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/filter/LongFilter.java @@ -2,6 +2,6 @@ package dev.sonpx.loyalty.mcp.filter; /** - * @author AnhDT Created on 2025/12/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ public class LongFilter extends RangeFilter {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/AttributeData.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/AttributeData.java index a46a6b8..f2b1f39 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/AttributeData.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/AttributeData.java @@ -8,7 +8,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; /** - * Created by tuanngo on Wed, 10/01/2024 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java deleted file mode 100644 index bc9e5d0..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/McpResponseWrapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package dev.sonpx.loyalty.mcp.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class McpResponseWrapper { - private T data; - private Presentation presentation; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/OperationResult.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/OperationResult.java new file mode 100644 index 0000000..7e1a7ed --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/OperationResult.java @@ -0,0 +1,19 @@ +package dev.sonpx.loyalty.mcp.model; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import lombok.Builder; +import lombok.Data; + +/** + * Created by SonPhung on Wednesday, 25-Oct-2023 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@Data +@Builder +public class OperationResult { + + private String errorCode; + private String message; + private D entity; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java deleted file mode 100644 index 027c808..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Presentation.java +++ /dev/null @@ -1,15 +0,0 @@ -package dev.sonpx.loyalty.mcp.model; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Presentation { - private String type; // e.g. "markdown" - private String content; // The formatted string -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ProcessResult.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ProcessResult.java index f989dd2..614e899 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ProcessResult.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ProcessResult.java @@ -2,7 +2,7 @@ package dev.sonpx.loyalty.mcp.model; /** - * Created by tuanngo on Wed, 2025-09-10 + * Created by SonPhung on Monday, 20-Jul-2026 */ public record ProcessResult(String code, String message, String targetId) { public static ProcessResult of(String code, String message) { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ReferenceData.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ReferenceData.java index c83113b..e5994dd 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ReferenceData.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/ReferenceData.java @@ -10,7 +10,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; /** - * Created by tuanngo on Tue, 2025-04-16 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Result.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Result.java new file mode 100644 index 0000000..33d96cd --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/model/Result.java @@ -0,0 +1,29 @@ +package dev.sonpx.loyalty.mcp.model; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Getter +@RequiredArgsConstructor(staticName = "of") +public class Result { + + private final T data; + + private Map presentation; + + public static Result of(T data, String content) { + Result result = of(data); + result.markdown(content); + return result; + } + + public void markdown(String content) { + this.presentation = new HashMap<>(); + this.presentation.put("markdown", content); + } +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/client/RewardClient.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/client/RewardClient.java index 1c1d0e7..fb8c9df 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/client/RewardClient.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/client/RewardClient.java @@ -1,12 +1,18 @@ package dev.sonpx.loyalty.mcp.reward.client; +import dev.sonpx.loyalty.mcp.model.OperationResult; import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria; -import dev.sonpx.loyalty.mcp.reward.model.CampaignDto; +import dev.sonpx.loyalty.mcp.reward.model.Campaign; +import jakarta.validation.Valid; import java.util.List; +import java.util.Map; import org.springframework.data.domain.Pageable; -import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.service.annotation.GetExchange; import org.springframework.web.service.annotation.HttpExchange; +import org.springframework.web.service.annotation.PostExchange; /** * Created by SonPhung on Sunday, 19-Jul-2026 @@ -15,5 +21,23 @@ import org.springframework.web.service.annotation.HttpExchange; public interface RewardClient { @GetExchange("/api/campaign/csr/list") - ResponseEntity> getAll(CampaignCriteria criteria, Pageable pageable); + List getAll(CampaignCriteria criteria, Pageable pageable); + + @GetExchange("/csr/count") + Long count(CampaignCriteria criteria); + + @GetExchange("/csr/id/{id}") + Campaign findActiveById(@PathVariable String id); + + @GetExchange("/csr/id") + List findActiveByIds(@RequestParam Map params); + + @GetExchange("/csr/id/generate") + String generateId(); + + @GetExchange("/csr/id/check/{id}") + String checkId(@PathVariable String id); + + @PostExchange("/csr/create") + OperationResult create(@Valid @RequestBody Campaign dto); } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignCriteria.java index 6b22f1a..9685b39 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignCriteria.java @@ -5,18 +5,27 @@ package dev.sonpx.loyalty.mcp.reward.criteria; import dev.sonpx.loyalty.mcp.criteria.AbstractFwCriteria; import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; /** - * @author AnhDT - * Created on 2023/10/25 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class CampaignCriteria extends AbstractFwCriteria { + @JsonPropertyDescription("Bộ lọc theo Mã chiến dịch (bằng chính xác, chứa từ khóa, v.v.)") private StringFilter campaignId; + + @JsonPropertyDescription("Bộ lọc theo Tên người/phòng ban sở hữu chiến dịch") private StringFilter ownerName; + + @JsonPropertyDescription("Bộ lọc theo Tên chiến dịch") private StringFilter name; + + @JsonPropertyDescription("Bộ lọc theo Mô tả của chiến dịch") private StringFilter description; + + @JsonPropertyDescription("Bộ lọc theo Loại chiến dịch (ví dụ: POINT, VOUCHER, v.v.)") private StringFilter campaignType; // for search text and component diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignRuleCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignRuleCriteria.java index 547daf4..06475ae 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignRuleCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CampaignRuleCriteria.java @@ -8,8 +8,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class CampaignRuleCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/ComponentCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/ComponentCriteria.java index 34a8c23..70f11ae 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/ComponentCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/ComponentCriteria.java @@ -7,7 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT Created on 2025/05/22 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class ComponentCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CounterDefinitionCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CounterDefinitionCriteria.java index 8e5c084..867ff04 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CounterDefinitionCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CounterDefinitionCriteria.java @@ -9,8 +9,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class CounterDefinitionCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CurrencyRateCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CurrencyRateCriteria.java index cf05941..de77564 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CurrencyRateCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/CurrencyRateCriteria.java @@ -8,8 +8,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/12/12 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class CurrencyRateCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/EventMaintenanceCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/EventMaintenanceCriteria.java index 85a3a49..26f025e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/EventMaintenanceCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/EventMaintenanceCriteria.java @@ -7,8 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class EventMaintenanceCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolConversionRateCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolConversionRateCriteria.java index a1627a7..2873d57 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolConversionRateCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolConversionRateCriteria.java @@ -7,8 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/12/06 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class PoolConversionRateCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolDefinitionCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolDefinitionCriteria.java index 78ba836..15219b3 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolDefinitionCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/PoolDefinitionCriteria.java @@ -7,8 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/25 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class PoolDefinitionCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/StatementOutputPoolCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/StatementOutputPoolCriteria.java index bc93501..2b1e184 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/StatementOutputPoolCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/StatementOutputPoolCriteria.java @@ -7,8 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/25 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class StatementOutputPoolCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCategoryCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCategoryCriteria.java index 56285f3..cd4b782 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCategoryCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCategoryCriteria.java @@ -7,8 +7,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class TransactionCategoryCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCodeCriteria.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCodeCriteria.java index 52798d8..5d001bf 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCodeCriteria.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/criteria/TransactionCodeCriteria.java @@ -8,8 +8,7 @@ import dev.sonpx.loyalty.mcp.filter.StringFilter; import lombok.Data; /** - * @author AnhDT - * Created on 2023/10/25 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Data public class TransactionCodeCriteria extends AbstractFwCriteria { diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AlertChannel.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AlertChannel.java index 44b6e40..db60216 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AlertChannel.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AlertChannel.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/08 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseType.java index c4e0405..2ca4d29 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/05 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseViewType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseViewType.java index c5b10da..6577619 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseViewType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/AmountToUseViewType.java @@ -7,8 +7,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/03/11 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/BaseDateType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/BaseDateType.java index 3336380..13e50c9 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/BaseDateType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/BaseDateType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/05 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/CampaignType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/CampaignType.java index a841bb2..1c85ebd 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/CampaignType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/CampaignType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/EventFrequencyType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/EventFrequencyType.java index 34563c5..ab4b3ee 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/EventFrequencyType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/EventFrequencyType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/09 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/LateValuePosting.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/LateValuePosting.java index 60518df..31648f1 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/LateValuePosting.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/LateValuePosting.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/08 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/MerchantEntityLevel.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/MerchantEntityLevel.java index d983fa4..c8ab2ad 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/MerchantEntityLevel.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/MerchantEntityLevel.java @@ -9,8 +9,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/15 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PoolType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PoolType.java index 5259ea1..01fdb1e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PoolType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PoolType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PrecisionOfBalance.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PrecisionOfBalance.java index b1ba29e..227f53f 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PrecisionOfBalance.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/PrecisionOfBalance.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/RuleType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/RuleType.java index d94458f..319f02d 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/RuleType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/RuleType.java @@ -2,6 +2,7 @@ package dev.sonpx.loyalty.mcp.reward.enums; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.annotation.JsonValue; import java.util.Arrays; import lombok.Getter; @@ -13,13 +14,28 @@ import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor public enum RuleType { + @JsonPropertyDescription("Thưởng điểm") AWARD("AWD"), + + @JsonPropertyDescription("Đổi điểm lấy quà") REDEEM("RED"), + + @JsonPropertyDescription("Đổi quà đặc biệt") ITEM_REDEEM("IRED"), + + @JsonPropertyDescription("Điều chỉnh điểm (tăng/giảm)") ADJUSTMENT("ADJ"), + + @JsonPropertyDescription("Sự kiện trích xuất bộ đếm") COUNTER_EXTRACT("CEP"), + + @JsonPropertyDescription("Đổi điểm trích xuất từ hệ thống khác") REDEEM_EXTRACT("REP"), + + @JsonPropertyDescription("Giao dịch trích xuất (Tích lũy theo GD)") TRANSACTION_EXTRACT("TEP"), + + @JsonPropertyDescription("Thưởng Marketing qua sự kiện/app") MARKETING_AWARD("MAWD"); @JsonValue diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/UpdateCounterStateType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/UpdateCounterStateType.java index 50b4415..7c8c5e8 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/UpdateCounterStateType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/UpdateCounterStateType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/09 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityEntity.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityEntity.java index f94ee52..c7e1958 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityEntity.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityEntity.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityPeriod.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityPeriod.java index 95ec05f..bcd1e9e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityPeriod.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityPeriod.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityTransactionType.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityTransactionType.java index a78c766..919c20f 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityTransactionType.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityTransactionType.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityUnit.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityUnit.java index a168248..c5cb9e9 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityUnit.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/enums/VelocityUnit.java @@ -8,8 +8,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; /** - * @author AnhDT - * Created on 2024/01/02 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @RequiredArgsConstructor diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagementDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagement.java similarity index 82% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagementDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagement.java index 4f5b0b1..733c007 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagementDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AlertManagement.java @@ -7,12 +7,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/20 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class AlertManagementDto extends FwDto { +public class AlertManagement extends Fw { @NotBlank @Size(min = 1, max = 10) diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseViewDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseView.java similarity index 53% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseViewDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseView.java index 6bc6260..77bf58b 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseViewDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/AmountToUseView.java @@ -5,9 +5,8 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2024/01/18 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class AmountToUseViewDto extends ComponentDto {} +public class AmountToUseView extends Component {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Campaign.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Campaign.java new file mode 100644 index 0000000..98f9cdd --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Campaign.java @@ -0,0 +1,51 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import dev.sonpx.loyalty.mcp.reward.enums.CampaignType; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import lombok.Getter; +import lombok.Setter; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; + +@Getter +@Setter +public class Campaign extends Fw { + + @NotBlank + @Size(min = 1, max = 10) + @JsonPropertyDescription("Mã chiến dịch (tối đa 10 ký tự, ví dụ: 'CMP01')") + private String campaignId; + + @Size(max = 50) + @JsonPropertyDescription("Tên người hoặc phòng ban sở hữu chiến dịch") + private String ownerName; + + @NotBlank + @Size(min = 1, max = 50) + @JsonPropertyDescription("Tên hiển thị của chiến dịch") + private String name; + + @Size(max = 500) + @JsonPropertyDescription("Mô tả chi tiết về chiến dịch") + private String description; + + @JsonPropertyDescription("Loại chiến dịch, xác định chiến dịch thuộc loại nào (ví dụ: POINT, VOUCHER)") + private CampaignType campaignType; + + @JsonPropertyDescription("Số lượng khách hàng mục tiêu dự kiến của chiến dịch") + private Long noOfCustomerTarget; + + @JsonPropertyDescription("Mục tiêu giá trị giao dịch trung bình (Target ATV)") + private Double targetAtv; + + @JsonPropertyDescription("Ngày bắt đầu có hiệu lực của chiến dịch") + private LocalDate effectiveFrom; + + @JsonPropertyDescription("Ngày kết thúc hiệu lực của chiến dịch") + private LocalDate effectiveTo; + + @JsonPropertyDescription("Số lượng quy tắc (rules) cấu hình trong chiến dịch") + private Integer numOfRule; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimitDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimit.java similarity index 93% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimitDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimit.java index 09f83ca..1d10b99 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimitDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignAwardLimit.java @@ -12,7 +12,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignAwardLimitDto extends FwDto { +public class CampaignAwardLimit extends Fw { private AwardLimitType limitType; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributorDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributor.java similarity index 91% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributorDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributor.java index 9de9049..5fd8db3 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributorDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignContributor.java @@ -12,7 +12,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignContributorDto extends FwDto { +public class CampaignContributor extends Fw { private ReferenceData chainId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteriaDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteria.java similarity index 83% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteriaDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteria.java index 7419e02..b943e67 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteriaDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignCriteria.java @@ -10,7 +10,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignCriteriaDto extends FwDto { +public class CampaignCriteria extends Fw { @NotBlank private String criteria; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignDto.java deleted file mode 100644 index 2c91f24..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignDto.java +++ /dev/null @@ -1,40 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import dev.sonpx.loyalty.mcp.reward.enums.CampaignType; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Size; -import java.time.LocalDate; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class CampaignDto extends FwDto { - - @NotBlank - @Size(min = 1, max = 10) - private String campaignId; - - @Size(max = 50) - private String ownerName; - - @NotBlank - @Size(min = 1, max = 50) - private String name; - - @Size(max = 500) - private String description; - - private CampaignType campaignType; - - private Long noOfCustomerTarget; - - private Double targetAtv; - - private LocalDate effectiveFrom; - - private LocalDate effectiveTo; - - private Integer numOfRule; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEightDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEight.java similarity index 95% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEightDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEight.java index 625f528..60d828f 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEightDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEight.java @@ -14,7 +14,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaEightDto extends FwDto { +public class CampaignFormulaEight extends Fw { private ReferenceData attrGroup; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaElevenDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEleven.java similarity index 91% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaElevenDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEleven.java index b87be16..695be27 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaElevenDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaEleven.java @@ -13,7 +13,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaElevenDto extends FwDto { +public class CampaignFormulaEleven extends Fw { @NotNull private ReferenceData segmentId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFiveDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFive.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFiveDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFive.java index 1d27ffa..2dd8c35 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFiveDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFive.java @@ -10,7 +10,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaFiveDto extends FwDto { +public class CampaignFormulaFive extends Fw { @NotNull private Double multiply; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFour.java similarity index 82% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFour.java index c32a199..5ee9f05 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFour.java @@ -15,7 +15,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaFourDto extends FwDto { +public class CampaignFormulaFour extends Fw { private AwardFactorType factorType; @@ -23,5 +23,5 @@ public class CampaignFormulaFourDto extends FwDto { @Valid @NotEmpty - private List tiers; + private List tiers; } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTierDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTier.java similarity index 91% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTierDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTier.java index 8503b75..c128d58 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTierDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaFourTier.java @@ -8,14 +8,14 @@ import jakarta.validation.constraints.Positive; import lombok.*; /** - * Created by AnhNt on Friday, 24-Nov-2023 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor -public class CampaignFormulaFourTierDto { +public class CampaignFormulaFourTier { @Min(1) private int tier; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOneDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOne.java similarity index 92% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOneDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOne.java index 7d9be42..dd67e0b 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOneDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaOne.java @@ -11,7 +11,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaOneDto extends FwDto { +public class CampaignFormulaOne extends Fw { @Positive @DecimalMax(value = "99999999999999.99") diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSettingDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSetting.java similarity index 91% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSettingDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSetting.java index 83fa98e..2f82423 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSettingDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSetting.java @@ -12,7 +12,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaSettingDto extends FwDto { +public class CampaignFormulaSetting extends Fw { private String ruleId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSix.java similarity index 83% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSix.java index 627a8a1..b04cb29 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSix.java @@ -14,7 +14,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaSixDto extends FwDto { +public class CampaignFormulaSix extends Fw { @NotNull private AwardFactorType factorType; @@ -24,5 +24,5 @@ public class CampaignFormulaSixDto extends FwDto { @Valid @NotEmpty - private List tiers; + private List tiers; } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTierDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTier.java similarity index 88% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTierDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTier.java index 865e4dc..4323860 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTierDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaSixTier.java @@ -7,15 +7,14 @@ import jakarta.validation.constraints.NotNull; import lombok.*; /** - * @author AnhDT - * Created on 2024/01/09 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor -public class CampaignFormulaSixTierDto { +public class CampaignFormulaSixTier { @Min(1) private int tier; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTenDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTen.java similarity index 93% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTenDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTen.java index 91fc718..0f8e529 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTenDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTen.java @@ -14,7 +14,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaTenDto extends FwDto { +public class CampaignFormulaTen extends Fw { @NotNull private AttributeData attributeId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwoDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwo.java similarity index 83% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwoDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwo.java index e21ffff..cc8b594 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwoDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignFormulaTwo.java @@ -10,7 +10,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignFormulaTwoDto extends FwDto { +public class CampaignFormulaTwo extends Fw { @Positive private Double fixedAmt; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRule.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRule.java new file mode 100644 index 0000000..df7511f --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRule.java @@ -0,0 +1,207 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import dev.sonpx.loyalty.mcp.enums.ExpiryPolicy; +import dev.sonpx.loyalty.mcp.reward.enums.BaseDateType; +import dev.sonpx.loyalty.mcp.reward.enums.NotificationChannel; +import dev.sonpx.loyalty.mcp.reward.enums.RuleType; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +/** + * Created by SonPhung on Monday, 13-Mon-2023 + */ +@Getter +@Setter +public class CampaignRule extends Fw { + + /** + * General Rule Infomation + */ + @NotNull + @JsonPropertyDescription("Tham chiếu đến Campaign chứa rule này") + private ReferenceData campaignId; + + @NotBlank + @Size(min = 1, max = 10) + @JsonPropertyDescription("Mã định danh của rule (tối đa 10 ký tự)") + private String ruleId; + + @NotBlank + @Size(min = 1, max = 100) + @JsonPropertyDescription("Tên của rule") + private String ruleName; + + @JsonPropertyDescription("Loại rule: RED (Đổi điểm), REP (Đổi quà), AWD (Thưởng), ADJ (Điều chỉnh), MAWD (Thưởng Marketing), CEP (Sự kiện)") + private RuleType ruleType; + + @Size(max = 500) + @JsonPropertyDescription("Mô tả chi tiết về rule") + private String description; + + @JsonPropertyDescription("Thời gian bắt đầu có hiệu lực của rule") + private LocalDateTime effectiveFrom; + + @JsonPropertyDescription("Thời gian kết thúc hiệu lực của rule") + private LocalDateTime effectiveTo; + + @JsonPropertyDescription("Loại ngày làm cơ sở để tính toán hiệu lực (nếu có)") + private BaseDateType effectivePeriodIsBase; + + @JsonPropertyDescription("Mẫu tin nhắn áp dụng cho rule này") + private ReferenceData messageTemplateId; + + @JsonPropertyDescription("Có dừng xử lý các rule tiếp theo nếu criteria của rule này thỏa mãn không?") + private Boolean stopIfCriteriaMet; + + @JsonPropertyDescription("Không cập nhật số dư Pool?") + private Boolean notUpdatePool; + + @Valid + @JsonPropertyDescription("Danh sách liên kết điều khoản (T&C)") + private List campaignTcLinkages; + + /** + * Rule Criteria + */ + @Valid + @JsonPropertyDescription("Điều kiện cấu hình chi tiết cho rule") + private CampaignCriteria campaignCriteria; + + /** + * Reward Setting - AWD/RED + */ + @JsonPropertyDescription("Tham chiếu đến Pool chính") + private ReferenceData poolId; + + @JsonPropertyDescription("Tham chiếu đến Sub-Pool") + private ReferenceData subPoolId; + + @JsonPropertyDescription("Chính sách hết hạn của điểm/quà") + private ExpiryPolicy expiryPolicy; + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Tham số cho chính sách hết hạn (ví dụ: số tháng)") + private Integer expiryPolicyParam; + + @JsonPropertyDescription("Ngày hết hạn cố định (nếu có)") + private LocalDate fixedDate; + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Ngày trong tháng (dùng cho chính sách hết hạn)") + private Integer dayOfMonth; + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Tháng trong năm (dùng cho chính sách hết hạn)") + private Integer monthOfYear; + + @JsonPropertyDescription("Mã sản phẩm/quà tặng") + private ReferenceData itemCode; + + // Aplly the first criterion satified in Formula 10 + @JsonPropertyDescription("Chỉ áp dụng tiêu chí đầu tiên thỏa mãn trong Formula 10") + private Boolean attrApplyFirstFlag; + + // Aplly the first criterion satified in Formula 11 + @JsonPropertyDescription("Chỉ áp dụng tiêu chí đầu tiên thỏa mãn trong Formula 11") + private Boolean segApplyFirstFlag; + + @Valid + @JsonPropertyDescription("Cấu hình công thức tính toán chung") + private CampaignFormulaSetting campaignFormulaSetting; + + @Valid + @JsonPropertyDescription("Danh sách giới hạn phần thưởng") + private List campaignAwardLimits; + + @Valid + @JsonPropertyDescription("Cấu hình Formula 1") + private CampaignFormulaOne campaignFormulaOne; + + @Valid + @JsonPropertyDescription("Cấu hình Formula 2") + private CampaignFormulaTwo campaignFormulaTwo; + + @Valid + @JsonPropertyDescription("Cấu hình Formula 4") + private CampaignFormulaFour campaignFormulaFour; + + @Valid + @JsonPropertyDescription("Danh sách cấu hình Formula 5") + private List campaignFormulaFives; + + @Valid + @JsonPropertyDescription("Cấu hình Formula 6") + private CampaignFormulaSix campaignFormulaSix; + + @Valid + @JsonPropertyDescription("Danh sách cấu hình Formula 8") + private List campaignFormulaEights; + + @Valid + @JsonPropertyDescription("Danh sách cấu hình Formula 10") + private List campaignFormulaTens; + + @Valid + @JsonPropertyDescription("Danh sách cấu hình Formula 11") + private List campaignFormulaElevens; + + @Valid + @JsonPropertyDescription("Danh sách các Contributor cho Campaign") + private List campaignContributors; + + @JsonPropertyDescription("Thứ tự ưu tiên áp dụng các Formula") + private List formulaSequence; + + /** + * Redemption Extract Rule Type + */ + @JsonPropertyDescription("Cấu hình cho loại rule Redemption Extract") + private RedemptionExtractRequest redemptionExtract; + + /** + * Counter Extract Rule Type + */ + @Valid + @JsonPropertyDescription("Cấu hình cho loại rule Counter Extract") + private CounterExtractRequest counterExtractRequest; + + /** + * Marketing Reward Rule Type + */ + @Valid + @JsonPropertyDescription("Cấu hình cho loại rule Marketing Reward") + private MarketingRewardRequest marketingRewardRequest; + + @Valid + @JsonPropertyDescription("Cấu hình lịch trình chạy cho rule") + private CampaignRuleSchedule campaignRuleSchedule; + + /** + * Notification Info + */ + @JsonPropertyDescription("Kênh gửi thông báo (ví dụ: SMS, EMAIL, NOTIFICATION)") + private NotificationChannel channel; + + @Valid + @JsonPropertyDescription("Nội dung thông báo qua SMS") + private RewardContentSms rewardContentSms; + + @Valid + @JsonPropertyDescription("Nội dung thông báo qua Email") + private RewardContentEmail rewardContentEmail; + + @Valid + @JsonPropertyDescription("Nội dung thông báo In-App Notification") + private RewardContentNotification rewardContentNotification; + +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPoolDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPool.java similarity index 83% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPoolDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPool.java index 4233ff7..41ba205 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPoolDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByPool.java @@ -7,13 +7,12 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2024/03/15 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter @Builder -public class CampaignRuleByPoolDto { +public class CampaignRuleByPool { private String poolId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCodeDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCode.java similarity index 83% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCodeDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCode.java index 8fe1467..14aa735 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCodeDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleByTxnCode.java @@ -6,7 +6,7 @@ import lombok.Setter; @Getter @Setter -public class CampaignRuleByTxnCodeDto { +public class CampaignRuleByTxnCode { private String ruleId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleDto.java deleted file mode 100644 index 1d4cbb3..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleDto.java +++ /dev/null @@ -1,163 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import dev.sonpx.loyalty.mcp.enums.ExpiryPolicy; -import dev.sonpx.loyalty.mcp.reward.enums.BaseDateType; -import dev.sonpx.loyalty.mcp.reward.enums.NotificationChannel; -import dev.sonpx.loyalty.mcp.reward.enums.RuleType; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.List; -import lombok.Getter; -import lombok.Setter; - -/** - * Created by SonPhung on Monday, 13-Mon-2023 - */ -@Getter -@Setter -public class CampaignRuleDto extends FwDto { - - /** - * General Rule Infomation - */ - @NotNull - private ReferenceData campaignId; - - @NotBlank - @Size(min = 1, max = 10) - private String ruleId; - - @NotBlank - @Size(min = 1, max = 100) - private String ruleName; - - private RuleType ruleType; - - @Size(max = 500) - private String description; - - private LocalDateTime effectiveFrom; - - private LocalDateTime effectiveTo; - - private BaseDateType effectivePeriodIsBase; - - private ReferenceData messageTemplateId; - - private Boolean stopIfCriteriaMet; - - private Boolean notUpdatePool; - - @Valid - private List campaignTcLinkages; - - /** - * Rule Criteria - */ - @Valid - private CampaignCriteriaDto campaignCriteria; - - /** - * Reward Setting - AWD/RED - */ - private ReferenceData poolId; - - private ReferenceData subPoolId; - - private ExpiryPolicy expiryPolicy; - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Integer expiryPolicyParam; - - private LocalDate fixedDate; - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Integer dayOfMonth; - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Integer monthOfYear; - - private ReferenceData itemCode; - - // Aplly the first criterion satified in Formula 10 - private Boolean attrApplyFirstFlag; - - // Aplly the first criterion satified in Formula 11 - private Boolean segApplyFirstFlag; - - @Valid - private CampaignFormulaSettingDto campaignFormulaSetting; - - @Valid - private List campaignAwardLimits; - - @Valid - private CampaignFormulaOneDto campaignFormulaOne; - - @Valid - private CampaignFormulaTwoDto campaignFormulaTwo; - - @Valid - private CampaignFormulaFourDto campaignFormulaFour; - - @Valid - private List campaignFormulaFives; - - @Valid - private CampaignFormulaSixDto campaignFormulaSix; - - @Valid - private List campaignFormulaEights; - - @Valid - private List campaignFormulaTens; - - @Valid - private List campaignFormulaElevens; - - @Valid - private List campaignContributors; - - private List formulaSequence; - - /** - * Redemption Extract Rule Type - */ - private RedemptionExtractRequestDto redemptionExtract; - - /** - * Counter Extract Rule Type - */ - @Valid - private CounterExtractRequestDto counterExtractRequest; - - /** - * Marketing Reward Rule Type - */ - @Valid - private MarketingRewardRequestDto marketingRewardRequest; - - @Valid - private CampaignRuleScheduleDto campaignRuleSchedule; - - /** - * Notification Info - */ - private NotificationChannel channel; - - @Valid - private RewardContentSmsDto rewardContentSms; - - @Valid - private RewardContentEmailDto rewardContentEmail; - - @Valid - private RewardContentNotificationDto rewardContentNotification; - -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleScheduleDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleSchedule.java similarity index 94% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleScheduleDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleSchedule.java index a6a0927..09ca5c1 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleScheduleDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignRuleSchedule.java @@ -12,7 +12,7 @@ import lombok.Setter; @Getter @Setter -public class CampaignRuleScheduleDto extends FwDto { +public class CampaignRuleSchedule extends Fw { private String ruleId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkageDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkage.java similarity index 86% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkageDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkage.java index 64e07ee..fab9b8e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkageDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CampaignTcLinkage.java @@ -10,7 +10,7 @@ import lombok.Setter; */ @Getter @Setter -public class CampaignTcLinkageDto extends FwDto { +public class CampaignTcLinkage extends Fw { @NotNull private ReferenceData transactionCode; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ComponentDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Component.java similarity index 73% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ComponentDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Component.java index fe8ce5c..cfe7d0f 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ComponentDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Component.java @@ -5,12 +5,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2025/10/28 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class ComponentDto { +public class Component { protected String key; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteriaDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteria.java similarity index 84% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteriaDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteria.java index 6cd0ce7..264d87e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteriaDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterCriteria.java @@ -6,7 +6,7 @@ import lombok.Setter; @Getter @Setter -public class CounterCriteriaDto { +public class CounterCriteria { private String key; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinitionDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinition.java similarity index 94% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinitionDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinition.java index 1d1f3d6..940aa30 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinitionDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterDefinition.java @@ -18,12 +18,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class CounterDefinitionDto extends FwDto { +public class CounterDefinition extends Fw { @NotBlank @Size(max = 10) diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequestDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequest.java similarity index 95% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequestDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequest.java index 2957d32..7c70c44 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequestDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterExtractRequest.java @@ -15,7 +15,7 @@ import lombok.Setter; @Getter @Setter -public class CounterExtractRequestDto extends FwDto { +public class CounterExtractRequest extends Fw { @NotNull private ReferenceData store; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStockDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStock.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStockDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStock.java index 5fbf147..b99fc59 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStockDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CounterStock.java @@ -6,12 +6,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/20 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class CounterStockDto extends FwDto { +public class CounterStock extends Fw { private String counterId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRateDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRate.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRateDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRate.java index 89758b8..1390ccd 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRateDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/CurrencyRate.java @@ -8,12 +8,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/12 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class CurrencyRateDto extends FwDto { +public class CurrencyRate extends Fw { @NotNull private ReferenceData pcrCode; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierViewDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierView.java similarity index 66% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierViewDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierView.java index b186bcd..241063a 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierViewDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DecideTierView.java @@ -6,4 +6,4 @@ import lombok.Setter; @Getter @Setter -public class DecideTierViewDto extends ComponentDto {} +public class DecideTierView extends Component {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequencesDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequences.java similarity index 92% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequencesDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequences.java index 91a2c6d..7983aab 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequencesDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/DeductionSequences.java @@ -10,7 +10,7 @@ import lombok.Setter; @Getter @Setter -public class DeductionSequencesDto extends FwDto { +public class DeductionSequences extends Fw { @NotBlank @Size(min = 1, max = 50) private String sequenceId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenanceDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenance.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenanceDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenance.java index 13bcaa3..7bed14f 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenanceDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/EventMaintenance.java @@ -10,12 +10,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class EventMaintenanceDto extends FwDto { +public class EventMaintenance extends Fw { @NotBlank private String eventId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/FwDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Fw.java similarity index 95% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/FwDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Fw.java index e71c3d9..cd4f102 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/FwDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/Fw.java @@ -13,7 +13,7 @@ import lombok.Setter; */ @Getter @Setter -public abstract class FwDto implements Serializable { +public abstract class Fw implements Serializable { @JsonFormat(shape = JsonFormat.Shape.STRING) protected Long recordNo; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReferenceDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReference.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReferenceDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReference.java index 2313625..49dba34 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReferenceDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ImageReference.java @@ -6,7 +6,7 @@ import lombok.Setter; @Getter @Setter -public class ImageReferenceDto { +public class ImageReference { private String imageSource; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPoolDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPool.java similarity index 84% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPoolDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPool.java index 755fd0e..2847b71 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPoolDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/LinkedPool.java @@ -7,13 +7,12 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/28 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter @AllArgsConstructor -public class LinkedPoolDto { +public class LinkedPool { private String poolId; private String poolName; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequest.java new file mode 100644 index 0000000..07a9f80 --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequest.java @@ -0,0 +1,29 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Getter +@Setter +public class MarketingRewardRequest extends Fw { + + @JsonPropertyDescription("Mã định danh của rule") + private String ruleId; + + @JsonPropertyDescription("Tham chiếu đến Pool điểm thưởng") + private ReferenceData poolId; + + @NotNull + @JsonPropertyDescription("Mã giao dịch thưởng (Transaction Code)") + private ReferenceData awardTransactionCode; + + @NotNull + @JsonPropertyDescription("Mã cửa hàng áp dụng thưởng (Store ID)") + private ReferenceData storeId; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequestDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequestDto.java deleted file mode 100644 index 0b61785..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MarketingRewardRequestDto.java +++ /dev/null @@ -1,25 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -/** - * @author AnhDT - * Created on 2024/09/23 - */ -@Getter -@Setter -public class MarketingRewardRequestDto extends FwDto { - - private String ruleId; - - private ReferenceData poolId; - - @NotNull - private ReferenceData awardTransactionCode; - - @NotNull - private ReferenceData storeId; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplateDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplate.java similarity index 84% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplateDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplate.java index f84e0d9..70d3a00 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplateDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MessageTemplate.java @@ -9,12 +9,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2024/01/03 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class MessageTemplateDto extends FwDto { +public class MessageTemplate extends Fw { @NotBlank @Size(min = 1, max = 10) diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfigDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfig.java similarity index 79% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfigDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfig.java index 66f7d75..eb8dae4 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfigDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/MqPoolConfig.java @@ -6,12 +6,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class MqPoolConfigDto extends FwDto { +public class MqPoolConfig extends Fw { @NotBlank private String messageType; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiryDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiry.java similarity index 93% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiryDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiry.java index f18fe50..c350ab1 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiryDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PaTypeExpiry.java @@ -13,7 +13,7 @@ import lombok.Setter; */ @Getter @Setter -public class PaTypeExpiryDto extends FwDto { +public class PaTypeExpiry extends Fw { private Long poolRecordNo; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequestDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequest.java similarity index 79% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequestDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequest.java index d598e2c..3b2f2d4 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequestDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolByEntityLevelRequest.java @@ -9,15 +9,14 @@ import java.util.Set; import lombok.*; /** - * @author AnhDT - * Created on 2024/03/07 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor -public class PoolByEntityLevelRequestDto implements Serializable { +public class PoolByEntityLevelRequest implements Serializable { @NotEmpty private Set poolSet; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRateDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRate.java similarity index 78% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRateDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRate.java index 89961af..17b6d53 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRateDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolConversionRate.java @@ -7,12 +7,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/06 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class PoolConversionRateDto extends FwDto { +public class PoolConversionRate extends Fw { @NotBlank @Size(min = 1, max = 5) diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinitionDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinition.java similarity index 88% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinitionDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinition.java index 2a26379..1805c4b 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinitionDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/PoolDefinition.java @@ -14,12 +14,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/23 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class PoolDefinitionDto extends FwDto { +public class PoolDefinition extends Fw { @NotBlank private String poolId; @@ -63,8 +62,8 @@ public class PoolDefinitionDto extends FwDto { // submodules @Valid - private List paTypeExpiries; + private List paTypeExpiries; @Valid - private List velocityControls; + private List velocityControls; } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroupDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroup.java similarity index 81% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroupDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroup.java index 9db3a2a..6bd9d73 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroupDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RecipientGroup.java @@ -8,12 +8,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2024/01/08 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class RecipientGroupDto extends FwDto { +public class RecipientGroup extends Fw { @NotBlank private String recipientGroupId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequestDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequest.java similarity index 85% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequestDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequest.java index 376a413..a3242e8 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequestDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RedemptionExtractRequest.java @@ -7,12 +7,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/12/20 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Setter @Getter -public class RedemptionExtractRequestDto extends FwDto { +public class RedemptionExtractRequest extends Fw { private ReferenceData poolId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ReferenceData.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ReferenceData.java index 278541c..f5751b7 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ReferenceData.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/ReferenceData.java @@ -10,7 +10,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; /** - * Created by tuanngo on Tue, 2025-04-16 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmail.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmail.java new file mode 100644 index 0000000..b90dc42 --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmail.java @@ -0,0 +1,43 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import lombok.Getter; +import lombok.Setter; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Getter +@Setter +public class RewardContentEmail extends Fw { + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Số thứ tự bản ghi rule") + private Long ruleRecordNo; + + @JsonPropertyDescription("Mã định danh của rule") + private String ruleId; + + @JsonPropertyDescription("Người/Hệ thống gửi Email") + private String sender; + + @JsonPropertyDescription("Tham chiếu đến template tin nhắn/Email") + private ReferenceData messageId; + + @JsonPropertyDescription("Tiêu đề Email") + private String subject; + + @JsonPropertyDescription("Nội dung Email") + private String body; + + @JsonPropertyDescription("Thiết kế hoặc mẫu hiển thị Email") + private String design; + + @JsonPropertyDescription("Thông tin siêu dữ liệu bổ sung") + private String metadata; + + @JsonPropertyDescription("Danh sách file đính kèm (nếu có)") + private String attachments; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmailDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmailDto.java deleted file mode 100644 index dc51d51..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentEmailDto.java +++ /dev/null @@ -1,34 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Getter; -import lombok.Setter; - -/** - * @author AnhDT - * Created on 2024/10/21 - */ -@Getter -@Setter -public class RewardContentEmailDto extends FwDto { - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Long ruleRecordNo; - - private String ruleId; - - private String sender; - - private ReferenceData messageId; - - private String subject; - - private String body; - - private String design; - - private String metadata; - - private String attachments; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotification.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotification.java new file mode 100644 index 0000000..7b1ed8a --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotification.java @@ -0,0 +1,84 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Getter +@Setter +public class RewardContentNotification extends Fw { + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Số thứ tự bản ghi rule") + private Long ruleRecordNo; + + @JsonPropertyDescription("Mã định danh của rule") + private String ruleId; + + @JsonPropertyDescription("Tham chiếu đến template Notification") + private ReferenceData messageId; + + @JsonPropertyDescription("Loại tin nhắn Notification") + private String messageType; + + @JsonPropertyDescription("Mã nội dung Notification") + private String notificationContentId; + + @JsonPropertyDescription("Mã ngôn ngữ (ví dụ: vi, en)") + private String languageCode; + + @JsonPropertyDescription("Tiêu đề Notification") + private String title; + + @JsonPropertyDescription("Nội dung tóm tắt Notification") + private String shortContent; + + @JsonPropertyDescription("Nội dung chi tiết Notification") + private String content; + + @JsonPropertyDescription("Loại điều hướng khi click vào Notification") + private ReferenceData navigationType; + + @JsonPropertyDescription("Đường dẫn liên kết điều hướng") + private String link; + + @JsonPropertyDescription("Màn hình đích đến trên app") + private ReferenceData targetScreen; + + @JsonPropertyDescription("Tham chiếu Pool điểm/quà") + private ReferenceData poolId; + + @JsonPropertyDescription("Nhóm ưu đãi liên kết") + private ReferenceData offerGroup; + + @JsonPropertyDescription("Danh mục liên kết") + private ReferenceData categoryId; + + @JsonPropertyDescription("Số tham chiếu") + private ReferenceData referenceNo; + + @JsonPropertyDescription("Mã chiến dịch liên kết") + private ReferenceData campaignId; + + @JsonPropertyDescription("Mã sản phẩm/quà tặng") + private ReferenceData itemCode; + + @JsonPropertyDescription("Loại thông báo") + private ReferenceData notificationType; + + @JsonPropertyDescription("Có chứa nội dung đa phương tiện không") + private Boolean richMedia; + + @JsonPropertyDescription("Ảnh đại diện Notification") + private ImageReference avatar; + + @JsonPropertyDescription("Danh sách các nút bấm thao tác trên Notification") + private List relatedButtons; + +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotificationDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotificationDto.java deleted file mode 100644 index 455fdb3..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentNotificationDto.java +++ /dev/null @@ -1,62 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import java.util.List; -import lombok.Getter; -import lombok.Setter; - -/** - * @author AnhDT - * Created on 2024/10/21 - */ -@Getter -@Setter -public class RewardContentNotificationDto extends FwDto { - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Long ruleRecordNo; - - private String ruleId; - - private ReferenceData messageId; - - private String messageType; - - private String notificationContentId; - - private String languageCode; - - private String title; - - private String shortContent; - - private String content; - - private ReferenceData navigationType; - - private String link; - - private ReferenceData targetScreen; - - private ReferenceData poolId; - - private ReferenceData offerGroup; - - private ReferenceData categoryId; - - private ReferenceData referenceNo; - - private ReferenceData campaignId; - - private ReferenceData itemCode; - - private ReferenceData notificationType; - - private Boolean richMedia; - - private ImageReferenceDto avatar; - - private List relatedButtons; - -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSms.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSms.java new file mode 100644 index 0000000..553b01b --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSms.java @@ -0,0 +1,40 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import lombok.Getter; +import lombok.Setter; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Getter +@Setter +public class RewardContentSms extends Fw { + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Số thứ tự bản ghi rule") + private Long ruleRecordNo; + + @JsonPropertyDescription("Mã định danh của rule") + private String ruleId; + + @JsonPropertyDescription("Thương hiệu SMS (Brandname)") + private ReferenceData smsBrand; + + @JsonPropertyDescription("Tham chiếu đến template SMS") + private ReferenceData messageId; + + @JsonPropertyDescription("Nội dung tin nhắn SMS") + private String message; + + @JsonPropertyDescription("Cờ đánh dấu từ chối nhận khuyến mãi") + private boolean refusedPromotion; + + @JsonPropertyDescription("Mã tin nhắn hệ thống") + private String messageCode; + + @JsonPropertyDescription("Số điện thoại nhận SMS") + private String phone; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSmsDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSmsDto.java deleted file mode 100644 index 94a0438..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardContentSmsDto.java +++ /dev/null @@ -1,32 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Getter; -import lombok.Setter; - -/** - * @author AnhDT - * Created on 2024/10/21 - */ -@Getter -@Setter -public class RewardContentSmsDto extends FwDto { - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Long ruleRecordNo; - - private String ruleId; - - private ReferenceData smsBrand; - - private ReferenceData messageId; - - private String message; - - private boolean refusedPromotion; - - private String messageCode; - - private String phone; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButton.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButton.java new file mode 100644 index 0000000..f4e938b --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButton.java @@ -0,0 +1,64 @@ + +package dev.sonpx.loyalty.mcp.reward.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import lombok.*; + + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RewardNotificationButton extends Fw { + + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonPropertyDescription("Số thứ tự bản ghi rule") + private Long ruleRecordNo; + + @JsonPropertyDescription("Mã định danh của rule") + private String ruleId; + + @JsonPropertyDescription("Tham chiếu đến template Notification") + private String messageId; + + @JsonPropertyDescription("Mã nội dung Notification") + private String notificationContentId; + + @JsonPropertyDescription("Mã định danh của nút bấm") + private String buttonId; + + @JsonPropertyDescription("Tên hiển thị của nút bấm") + private String buttonName; + + @JsonPropertyDescription("Loại điều hướng của nút bấm") + private ReferenceData buttonNavigationType; + + @JsonPropertyDescription("Đường dẫn liên kết điều hướng của nút bấm") + private String buttonLink; + + @JsonPropertyDescription("Màn hình đích đến trên app của nút bấm") + private ReferenceData buttonTargetScreen; + + @JsonPropertyDescription("Tham chiếu Pool điểm/quà của nút bấm") + private ReferenceData buttonPoolId; + + @JsonPropertyDescription("Nhóm ưu đãi liên kết của nút bấm") + private ReferenceData buttonOfferGroup; + + @JsonPropertyDescription("Danh mục liên kết của nút bấm") + private ReferenceData buttonCategoryId; + + @JsonPropertyDescription("Mã chiến dịch liên kết của nút bấm") + private ReferenceData buttonCampaignId; + + @JsonPropertyDescription("Mã sản phẩm/quà tặng của nút bấm") + private ReferenceData buttonItemCode; + + @JsonPropertyDescription("Số tham chiếu của nút bấm") + private ReferenceData buttonReferenceNo; + + @JsonPropertyDescription("Loại thông báo của nút bấm") + private ReferenceData buttonNotificationType; +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButtonDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButtonDto.java deleted file mode 100644 index 331592d..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RewardNotificationButtonDto.java +++ /dev/null @@ -1,47 +0,0 @@ - -package dev.sonpx.loyalty.mcp.reward.model; - -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.*; - - -@Getter -@Setter -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class RewardNotificationButtonDto extends FwDto { - - @JsonFormat(shape = JsonFormat.Shape.STRING) - private Long ruleRecordNo; - - private String ruleId; - - private String messageId; - - private String notificationContentId; - - private String buttonId; - - private String buttonName; - - private ReferenceData buttonNavigationType; - - private String buttonLink; - - private ReferenceData buttonTargetScreen; - - private ReferenceData buttonPoolId; - - private ReferenceData buttonOfferGroup; - - private ReferenceData buttonCategoryId; - - private ReferenceData buttonCampaignId; - - private ReferenceData buttonItemCode; - - private ReferenceData buttonReferenceNo; - - private ReferenceData buttonNotificationType; -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteriaDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteria.java similarity index 89% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteriaDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteria.java index 672ecf0..5166d7e 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteriaDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/RuleCriteria.java @@ -12,7 +12,7 @@ import org.apache.commons.lang3.StringUtils; @ToString @Data @JsonIgnoreProperties(ignoreUnknown = true) -public class RuleCriteriaDto { +public class RuleCriteria { private String field; @@ -26,7 +26,7 @@ public class RuleCriteriaDto { private String combinator; - private List rules = new ArrayList<>(); + private List rules = new ArrayList<>(); public boolean isSimple() { return !isGroup(); @@ -40,7 +40,7 @@ public class RuleCriteriaDto { return StringUtils.isEmpty(field) && StringUtils.isEmpty(fieldType) && (rules == null || rules.isEmpty()); } - public void validateSimple(RuleCriteriaDto rule, List errors) { + public void validateSimple(RuleCriteria rule, List errors) { if (StringUtils.isEmpty(rule.getField())) { errors.add("Field is required. " + rule.devLog()); } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPoolDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPool.java similarity index 80% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPoolDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPool.java index 094205a..1eaec7c 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPoolDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/StatementOutputPool.java @@ -6,12 +6,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/24 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class StatementOutputPoolDto extends FwDto { +public class StatementOutputPool extends Fw { private String poolId; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategoryDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategory.java similarity index 76% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategoryDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategory.java index f27053a..945b9f9 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategoryDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCategory.java @@ -6,12 +6,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/31 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class TransactionCategoryDto extends FwDto { +public class TransactionCategory extends Fw { @NotBlank private String code; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCodeDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCode.java similarity index 82% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCodeDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCode.java index a33d844..21ad254 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCodeDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/TransactionCode.java @@ -7,12 +7,11 @@ import lombok.Getter; import lombok.Setter; /** - * @author AnhDT - * Created on 2023/10/19 + * Created by SonPhung on Monday, 20-Jul-2026 */ @Getter @Setter -public class TransactionCodeDto extends FwDto { +public class TransactionCode extends Fw { @NotBlank @Size(min = 1, max = 10) diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControlDto.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControl.java similarity index 95% rename from loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControlDto.java rename to loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControl.java index ffc895f..3ad80c4 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControlDto.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/reward/model/VelocityControl.java @@ -21,7 +21,7 @@ import lombok.Setter; */ @Getter @Setter -public class VelocityControlDto extends FwDto { +public class VelocityControl extends Fw { private Long poolRecordNo; diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java new file mode 100644 index 0000000..338d48e --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/service/CampaignService.java @@ -0,0 +1,97 @@ +package dev.sonpx.loyalty.mcp.service; + +import static dev.sonpx.loyalty.mcp.util.MarkdownGenerator.generateCampaignMD; + +import dev.sonpx.loyalty.mcp.model.OperationResult; +import dev.sonpx.loyalty.mcp.model.Result; +import dev.sonpx.loyalty.mcp.reward.client.RewardClient; +import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria; +import dev.sonpx.loyalty.mcp.reward.model.Campaign; +import dev.sonpx.loyalty.mcp.util.MarkdownGenerator; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class CampaignService { + + private final RewardClient rewardClient; + + public Result> getAll(CampaignCriteria criteria) { + try { + List campaigns = rewardClient.getAll(criteria, Pageable.ofSize(10)); + + return Result.of(campaigns, generateCampaignMD(campaigns)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result count(CampaignCriteria criteria) { + try { + Long count = rewardClient.count(criteria); + return Result.of(count, MarkdownGenerator.generateCampaignMD(count)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result findActiveById(String id) { + try { + Campaign campaign = rewardClient.findActiveById(id); + return Result.of(campaign, MarkdownGenerator.generateCampaignMD(campaign)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result> findActiveByIds(String ids) { + try { + List campaigns = rewardClient.findActiveByIds(java.util.Map.of("id", ids)); + return Result.of(campaigns, MarkdownGenerator.generateCampaignMD(campaigns)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result generateId() { + try { + String id = rewardClient.generateId(); + return Result.of(id, MarkdownGenerator.generateCampaignIdMD(id)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result checkId(String id) { + try { + String checkResult = rewardClient.checkId(id); + return Result.of(checkResult, MarkdownGenerator.generateCheckResultMD(checkResult)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } + + public Result> create(Campaign dto) { + try { + OperationResult opResult = rewardClient.create(dto); + return Result.of(opResult, MarkdownGenerator.generateOperationResultMD(opResult)); + } catch (Exception e) { + log.error(e.getMessage(), e); + return Result.of(null); + } + } +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/ApiMcpTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/ApiMcpTools.java new file mode 100644 index 0000000..74c3d2f --- /dev/null +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/ApiMcpTools.java @@ -0,0 +1,60 @@ +package dev.sonpx.loyalty.mcp.tool; + +import dev.sonpx.loyalty.mcp.model.OperationResult; +import dev.sonpx.loyalty.mcp.model.Result; +import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria; +import dev.sonpx.loyalty.mcp.reward.model.Campaign; +import dev.sonpx.loyalty.mcp.service.CampaignService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.ai.mcp.annotation.McpTool; +import org.springframework.stereotype.Component; + +/** + * Created by SonPhung on Monday, 20-Jul-2026 + */ +@Component +@RequiredArgsConstructor +public class ApiMcpTools { + + private final CampaignService campaignService; + + @McpTool(description = "Lấy danh sách chiến dịch, cho phép tìm kiếm với param search") + public Result> campaigns(String search) { + CampaignCriteria criteria = new CampaignCriteria(); + criteria.setSearch(search); + return campaignService.getAll(criteria); + } + + @McpTool(description = "Đếm số lượng chiến dịch, cho phép tìm kiếm với param search") + public Result countCampaigns(String search) { + CampaignCriteria criteria = new CampaignCriteria(); + criteria.setSearch(search); + return campaignService.count(criteria); + } + + @McpTool(description = "Lấy thông tin chi tiết một chiến dịch theo ID") + public Result getCampaignById(String id) { + return campaignService.findActiveById(id); + } + + @McpTool(description = "Lấy thông tin nhiều chiến dịch theo danh sách ID, phân tách bằng dấu phẩy") + public Result> getCampaignsByIds(String ids) { + return campaignService.findActiveByIds(ids); + } + + @McpTool(description = "Tạo một ID chiến dịch mới") + public Result generateCampaignId() { + return campaignService.generateId(); + } + + @McpTool(description = "Kiểm tra trạng thái hoặc tính hợp lệ của một ID chiến dịch") + public Result checkCampaignId(String id) { + return campaignService.checkId(id); + } + + @McpTool(description = "Tạo một chiến dịch mới") + public Result> createCampaign(Campaign dto) { + return campaignService.create(dto); + } +} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java deleted file mode 100644 index 0522068..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentTools.java +++ /dev/null @@ -1,71 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool; - -import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient; -import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient; -import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient; -import dev.sonpx.loyalty.mcp.client.model.*; -import dev.sonpx.loyalty.mcp.client.model.attribute.StaticAttributeDto; -import java.util.List; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.core.ParameterizedTypeReference; - -public class LoyaltyAgentTools { - - private final MemberTierApiClient memberTierApiClient; - private final PointPoolApiClient pointPoolApiClient; - private final CampaignApiClient campaignApiClient; - private final org.springframework.web.client.RestClient restClient; - - public LoyaltyAgentTools(MemberTierApiClient memberTierApiClient, PointPoolApiClient pointPoolApiClient, CampaignApiClient campaignApiClient, org.springframework.web.client.RestClient restClient) { - this.memberTierApiClient = memberTierApiClient; - this.pointPoolApiClient = pointPoolApiClient; - this.campaignApiClient = campaignApiClient; - this.restClient = restClient; - } - - @Tool(description = "List all available Member Tiers. Always use this to verify a Tier exists before creating a Campaign.") - public List listMemberTiers() { - return memberTierApiClient.listMemberTiers(); - } - - @Tool(description = "List all available Point Pools. Always use this to verify a Pool exists and has sufficient balance before creating a Campaign.") - public List listPointPools() { - return pointPoolApiClient.listPointPools(); - } - - @Tool(description = "Create a new Campaign. ONLY use this when explicitly asked to create or add a new campaign. Do not use this to fetch information about existing campaigns. This requires a valid targetTierId and rewardPoolId, which MUST be verified beforehand using listMemberTiers and listPointPools.") - public Campaign createCampaign(CreateCampaignRequest request) { - String campaignId = request.campaignId(); - if (campaignId == null || campaignId.isBlank()) { - campaignId = campaignApiClient.generateCampaignId(); - } - - Campaign draft = new Campaign( - campaignId, - request.name(), - request.targetTierId(), - request.rewardPoolId(), - null - ); - OperationResult result = campaignApiClient.createCampaignDraft(draft); - if (result == null || result.entity() == null) { - String message = result != null ? result.message() : "empty response"; - throw new IllegalStateException("Campaign create-draft did not return an entity: " + message); - } - return result.entity(); - } - - @Tool(description = "List all available Static Attributes. Use this tool when you need to find configurations, properties, or static values like tier types or currencies.") - public List listStaticAttributes() { - try { - String url = "/svc/attribute/api/static-attribute/csr/view/list?sort=lastApproveDate,desc&sort=lastUpdateDate,desc&page=0&size=10&status.in=A"; - return restClient.get() - .uri(url) - .retrieve() - .body(new ParameterizedTypeReference<>() { - }); - } catch (Exception e) { - throw new RuntimeException("Failed to fetch static attributes", e); - } - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java deleted file mode 100644 index c945e5b..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreatePoolDefinitionRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.pool; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; - -public record CreatePoolDefinitionRequest( - @JsonPropertyDescription("Conversation ID from the agent") - String conversationId, - - @JsonPropertyDescription("Pool ID (max 10 chars)") - String poolId, - - @JsonPropertyDescription("Pool Name") - String poolName, - - @JsonPropertyDescription("Pool Type (e.g. STANDARD, PROMOTIONAL)") - String poolType, - - @JsonPropertyDescription("Currency Code") - String currencyCode -) {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java deleted file mode 100644 index d4952b0..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/pool/CreateSopRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.pool; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; - -public record CreateSopRequest( - @JsonPropertyDescription("Conversation ID from the agent") - String conversationId, - - @JsonPropertyDescription("Statement Output Pool ID") - String sopId, - - @JsonPropertyDescription("Reference Pool ID (must exist)") - String poolId, - - @JsonPropertyDescription("SOP Name") - String name, - - @JsonPropertyDescription("Statement Type") - String statementType -) {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java deleted file mode 100644 index 75c8655..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/AddRuleToCampaignRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; - -public record AddRuleToCampaignRequest( - @JsonPropertyDescription("Conversation ID from the agent") - String conversationId, - - @JsonPropertyDescription("Target Campaign ID") - String targetCampaignId, - - @JsonPropertyDescription("Transaction Code to trigger the rule") - String transactionCode, - - @JsonPropertyDescription("Whether this rule flow requires a Pool ID") - boolean needsPoolId, - - @JsonPropertyDescription("Pool ID for the rule (if needed)") - String poolId, - - @JsonPropertyDescription("Rule details") - CreateCampaignRuleToolRequest ruleDetails -) {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java deleted file mode 100644 index 460597c..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/CreateCampaignRuleToolRequest.java +++ /dev/null @@ -1,41 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import java.time.OffsetDateTime; - -public record CreateCampaignRuleToolRequest( - @JsonPropertyDescription("ID of the campaign this rule belongs to") - String campaignId, - - @JsonPropertyDescription("Unique identifier for the rule (max 10 chars)") - String ruleId, - - @JsonPropertyDescription("Name of the rule") - String ruleName, - - @JsonPropertyDescription("Type of the rule (e.g., AWD for Award, RED for Redemption, ADJ for Adjustment)") - String ruleType, - - @JsonPropertyDescription("Description of the rule") - String description, - - @JsonPropertyDescription("Effective start date of the rule") - OffsetDateTime effectiveFrom, - - @JsonPropertyDescription("Effective end date of the rule") - OffsetDateTime effectiveTo, - - @JsonPropertyDescription("ID of the Point Pool to be updated by this rule") - String poolId, - - @JsonPropertyDescription("Formula One configuration for basic points awarding (points per txn amount)") - FormulaOneConfig formulaOne -) { - public record FormulaOneConfig( - @JsonPropertyDescription("Amount of points awarded per transaction amount") - Double awardPerTxnAmt, - - @JsonPropertyDescription("Required transaction amount to receive the award") - Double txnAmt - ) {} -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java deleted file mode 100644 index a9341df..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/ListCampaignRuleToolRequest.java +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ListCampaignRuleToolRequest( - String campaignId, - Integer page, - Integer size -) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java deleted file mode 100644 index 72de595..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/SubmitCampaignDraftRequest.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; - -public record SubmitCampaignDraftRequest( - @JsonPropertyDescription("Conversation ID from the agent") - String conversationId -) {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java deleted file mode 100644 index 6757e52..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignDraftRequest.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; - -public record UpdateCampaignDraftRequest( - @JsonPropertyDescription("Conversation ID from the agent") - String conversationId, - - @JsonPropertyDescription("Campaign ID") - String campaignId, - - @JsonPropertyDescription("Campaign Name") - String name, - - @JsonPropertyDescription("Target Tier ID") - String targetTierId -) {} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java deleted file mode 100644 index b67cec2..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/model/reward/UpdateCampaignRuleToolRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.model.reward; - -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import java.time.OffsetDateTime; - -public record UpdateCampaignRuleToolRequest( - @JsonPropertyDescription("ID of the campaign this rule belongs to") - String campaignId, - - @JsonPropertyDescription("Unique identifier for the rule (max 10 chars)") - String ruleId, - - @JsonPropertyDescription("Name of the rule") - String ruleName, - - @JsonPropertyDescription("Type of the rule (e.g., AWD for Award, RED for Redemption, ADJ for Adjustment)") - String ruleType, - - @JsonPropertyDescription("Description of the rule") - String description, - - @JsonPropertyDescription("Effective start date of the rule") - OffsetDateTime effectiveFrom, - - @JsonPropertyDescription("Effective end date of the rule") - OffsetDateTime effectiveTo, - - @JsonPropertyDescription("ID of the Point Pool to be updated by this rule") - String poolId, - - @JsonPropertyDescription("Formula One configuration for basic points awarding (points per txn amount)") - CreateCampaignRuleToolRequest.FormulaOneConfig formulaOne -) { -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java deleted file mode 100644 index ada67ab..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/pool/PointPoolTools.java +++ /dev/null @@ -1,85 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.pool; - -import dev.sonpx.loyalty.mcp.draft.DependencyValidator; -import dev.sonpx.loyalty.mcp.draft.DraftSessionManager; -import dev.sonpx.loyalty.mcp.tool.model.pool.CreatePoolDefinitionRequest; -import dev.sonpx.loyalty.mcp.tool.model.pool.CreateSopRequest; -import java.util.Map; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; - -@Component -public class PointPoolTools { - - private final DraftSessionManager draftSessionManager; - private final DependencyValidator dependencyValidator; - private final RestClient restClient; - - public PointPoolTools(DraftSessionManager draftSessionManager, DependencyValidator dependencyValidator, @Qualifier("rewardRestClient") RestClient restClient) { - this.draftSessionManager = draftSessionManager; - this.dependencyValidator = dependencyValidator; - this.restClient = restClient; - } - - @Tool(description = "Create a Pool Definition. This builds a PoolDefinitionDto and submits it via Core API.") - public Map createPoolDefinition(CreatePoolDefinitionRequest request) { - if (request.conversationId() == null || request.conversationId().isBlank()) { - throw new IllegalArgumentException("conversationId is required"); - } - - // Store in draft - draftSessionManager.updateSession(request.conversationId(), session -> { - Map poolDef = session.getPoolDraft().getPoolDefinition(); - poolDef.put("poolId", request.poolId()); - poolDef.put("poolName", request.poolName()); - poolDef.put("poolType", request.poolType()); - poolDef.put("currencyCode", request.currencyCode()); - }); - - // Submit via Core API - try { - return restClient.post() - .uri("/api/pool-definition/csr/create-draft") - .body(request) - .retrieve() - .body(new ParameterizedTypeReference<>() {}); - } catch (Exception e) { - e.printStackTrace(); - return Map.of("error", "Failed to create pool definition draft via Core API: " + e.getMessage()); - } - } - - @Tool(description = "Create a Statement Output Pool (SOP). This builds a StatementOutputPoolDto and requires an existing Pool ID.") - public Map createSop(CreateSopRequest request) { - if (request.conversationId() == null || request.conversationId().isBlank()) { - throw new IllegalArgumentException("conversationId is required"); - } - - // Validate prerequisite: existing Pool ID - dependencyValidator.validateSopPrerequisites(request.poolId()); - - // Store in draft - draftSessionManager.updateSession(request.conversationId(), session -> { - Map sop = session.getPoolDraft().getStatementOutputPool(); - sop.put("sopId", request.sopId()); - sop.put("poolId", request.poolId()); - sop.put("name", request.name()); - sop.put("statementType", request.statementType()); - }); - - // Submit via Core API - try { - return restClient.post() - .uri("/api/sop/csr/create-draft") - .body(request) - .retrieve() - .body(new ParameterizedTypeReference<>() {}); - } catch (Exception e) { - e.printStackTrace(); - return Map.of("error", "Failed to create Statement Output Pool draft via Core API: " + e.getMessage()); - } - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java deleted file mode 100644 index 14dad6b..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.reward; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignFormulaOneDto; -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; -import dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData; -import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest; -import java.util.ArrayList; -import org.springframework.stereotype.Component; - -@Component -public class CampaignRuleMapper { - - public CampaignRuleDto toDto(CreateCampaignRuleToolRequest request) { - CampaignRuleDto dto = new CampaignRuleDto(); - - dto.setCampaignId(toReferenceData(request.campaignId())); - dto.setRuleId(request.ruleId()); - dto.setRuleName(request.ruleName()); - if (request.ruleType() != null) { - dto.setRuleType(CampaignRuleDto.RuleTypeEnum.fromValue(request.ruleType())); - } - dto.setDescription(request.description()); - dto.setEffectiveFrom(request.effectiveFrom()); - dto.setEffectiveTo(request.effectiveTo()); - dto.setPoolId(toReferenceData(request.poolId())); - - if (request.formulaOne() != null) { - CampaignFormulaOneDto formulaOne = new CampaignFormulaOneDto(); - formulaOne.setAwardPerTxnAmt(request.formulaOne().awardPerTxnAmt()); - formulaOne.setTxnAmt(request.formulaOne().txnAmt()); - dto.setCampaignFormulaOne(formulaOne); - - dto.setFormulaSequence(new ArrayList<>()); - dto.getFormulaSequence().add("1"); - } - - return dto; - } - - public CampaignRuleDto toDto(UpdateCampaignRuleToolRequest request) { - CampaignRuleDto dto = new CampaignRuleDto(); - - dto.setCampaignId(toReferenceData(request.campaignId())); - dto.setRuleId(request.ruleId()); - dto.setRuleName(request.ruleName()); - if (request.ruleType() != null) { - dto.setRuleType(CampaignRuleDto.RuleTypeEnum.fromValue(request.ruleType())); - } - dto.setDescription(request.description()); - dto.setEffectiveFrom(request.effectiveFrom()); - dto.setEffectiveTo(request.effectiveTo()); - dto.setPoolId(toReferenceData(request.poolId())); - - if (request.formulaOne() != null) { - CampaignFormulaOneDto formulaOne = new CampaignFormulaOneDto(); - formulaOne.setAwardPerTxnAmt(request.formulaOne().awardPerTxnAmt()); - formulaOne.setTxnAmt(request.formulaOne().txnAmt()); - dto.setCampaignFormulaOne(formulaOne); - - dto.setFormulaSequence(new ArrayList<>()); - dto.getFormulaSequence().add("1"); - } - - return dto; - } - - private ReferenceData toReferenceData(String code) { - if (code == null) { - return null; - } - ReferenceData ref = new ReferenceData(); - ref.setCode(code); - return ref; - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java deleted file mode 100644 index 55e56a4..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleTools.java +++ /dev/null @@ -1,208 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.reward; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; -import dev.sonpx.loyalty.mcp.client.model.reward.OperationResultCampaignRuleDto; -import dev.sonpx.loyalty.mcp.draft.DependencyValidator; -import dev.sonpx.loyalty.mcp.draft.DraftSessionManager; -import dev.sonpx.loyalty.mcp.model.McpResponseWrapper; -import dev.sonpx.loyalty.mcp.model.Presentation; -import dev.sonpx.loyalty.mcp.tool.model.reward.AddRuleToCampaignRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest; -import dev.sonpx.loyalty.mcp.util.MarkdownGenerator; -import java.util.List; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; - -@Component -public class CampaignRuleTools { - - private static final Logger log = LoggerFactory.getLogger(CampaignRuleTools.class); - - private final CampaignRuleMapper campaignRuleMapper; - private final RestClient rewardRestClient; - private final DraftSessionManager draftSessionManager; - private final DependencyValidator dependencyValidator; - - public CampaignRuleTools( - CampaignRuleMapper campaignRuleMapper, - @Qualifier("rewardRestClient") RestClient rewardRestClient, - DraftSessionManager draftSessionManager, - DependencyValidator dependencyValidator) { - this.campaignRuleMapper = campaignRuleMapper; - this.rewardRestClient = rewardRestClient; - this.draftSessionManager = draftSessionManager; - this.dependencyValidator = dependencyValidator; - } - - @Tool(description = "Add Rule to local Campaign Draft. Validates prerequisites (Transaction Code, Pool ID) before adding.") - public Map addRuleToCampaign(AddRuleToCampaignRequest request) { - if (request.conversationId() == null || request.conversationId().isBlank()) { - throw new IllegalArgumentException("conversationId is required"); - } - - // Validate dependencies - dependencyValidator.validateRulePrerequisites( - draftSessionManager.getSession(request.conversationId()), - request.targetCampaignId(), - request.transactionCode(), - request.needsPoolId(), - request.poolId() - ); - - // Update local draft - draftSessionManager.updateSession(request.conversationId(), session -> { - CampaignRuleDto rule = campaignRuleMapper.toDto(request.ruleDetails()); - dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData ref = new dev.sonpx.loyalty.mcp.client.model.reward.ReferenceData(); - ref.setCode(request.targetCampaignId()); - rule.setCampaignId(ref); - session.getCampaignDraft().getRules().add(rule); - }); - - return Map.of("status", "Rule added to Campaign draft successfully", "ruleId", request.ruleDetails().ruleId()); - } - - @Tool(description = "Create a new Campaign Rule for an existing Campaign. Supports basic properties and Formula One configuration for points per transaction amount.") - public OperationResultCampaignRuleDto createCampaignRule(CreateCampaignRuleToolRequest request) { - CampaignRuleDto dto = campaignRuleMapper.toDto(request); - try { - return rewardRestClient.post() - .uri("/api/campaign-rule/csr/create") - .body(dto) - .retrieve() - .body(OperationResultCampaignRuleDto.class); - } catch (Exception e) { - log.error("Failed to create Campaign Rule", e); - throw new RuntimeException("Failed to create Campaign Rule: " + e.getMessage(), e); - } - } - - @Tool(description = "Edit an existing Campaign Rule. You must provide all required fields (campaignId, ruleId, ruleName, etc) as this will update the entire rule configuration.") - public OperationResultCampaignRuleDto editCampaignRule(UpdateCampaignRuleToolRequest request) { - CampaignRuleDto dto = campaignRuleMapper.toDto(request); - try { - return rewardRestClient.patch() - .uri("/api/campaign-rule/csr/edit") - .body(dto) - .retrieve() - .body(OperationResultCampaignRuleDto.class); - } catch (Exception e) { - log.error("Failed to edit Campaign Rule", e); - throw new RuntimeException("Failed to edit Campaign Rule: " + e.getMessage(), e); - } - } - - @Tool(description = "List Campaign Rules, optionally filtered by campaignId. Use this tool to get a list of campaign rules. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.") - public McpResponseWrapper> listCampaignRules( - @org.springframework.lang.Nullable String campaignId, - @org.springframework.lang.Nullable Integer page, - @org.springframework.lang.Nullable Integer size) { - int pageNum = page != null ? page : 0; - int sizeNum = size != null ? size : 20; - - try { - String jsonResponse = rewardRestClient.get() - .uri(uriBuilder -> { - uriBuilder.path("/api/campaign-rule/csr/list") - .queryParam("page", pageNum) - .queryParam("size", sizeNum); - - if (campaignId != null && !campaignId.isEmpty()) { - uriBuilder.queryParam("campaignId.equals", campaignId); - } - return uriBuilder.build(); - }) - .retrieve() - .body(String.class); - - // Fix missing timezone in date-time strings from backend (e.g. "2026-06-25T16:16:01" -> "2026-06-25T16:16:01Z") - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\""); - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\""); - - // Try to map it manually - com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); - mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); - mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - try { - CampaignRuleDto[] rules = mapper.readValue(jsonResponse, CampaignRuleDto[].class); - return wrapCampaignRules(rules != null ? List.of(rules) : List.of()); - } catch (Exception parseEx) { - System.out.println("Failed to parse as array. Trying to extract from pageable format..."); - com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse); - if (root.has("content")) { - CampaignRuleDto[] rules = mapper.treeToValue(root.get("content"), CampaignRuleDto[].class); - return wrapCampaignRules(rules != null ? List.of(rules) : List.of()); - } else if (root.has("data")) { - CampaignRuleDto[] rules = mapper.treeToValue(root.get("data"), CampaignRuleDto[].class); - return wrapCampaignRules(rules != null ? List.of(rules) : List.of()); - } - throw parseEx; - } - } catch (Exception e) { - log.error("Failed to list Campaign Rules", e); - throw new RuntimeException("Failed to list Campaign Rules: " + e.getMessage(), e); - } - } - @Tool(description = "List Campaign Rules by Campaign Name. It finds the campaign by name and returns its rules. Use this tool when the user provides a campaign name and asks for its campaign rules. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.") - public McpResponseWrapper> listCampaignRulesByCampaignName(String campaignName) { - try { - String jsonResponse = rewardRestClient.get() - .uri(uriBuilder -> uriBuilder - .path("/api/campaign/csr/list") - .queryParam("name.contains", campaignName) - .queryParam("page", 0) - .queryParam("size", 1) - .build() - ) - .retrieve() - .body(String.class); - - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\""); - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\""); - - com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); - mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); - mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[] campaigns = null; - try { - campaigns = mapper.readValue(jsonResponse, dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class); - } catch (Exception parseEx) { - com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse); - if (root.has("content")) { - campaigns = mapper.treeToValue(root.get("content"), dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class); - } else if (root.has("data")) { - campaigns = mapper.treeToValue(root.get("data"), dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto[].class); - } else { - throw parseEx; - } - } - - if (campaigns == null || campaigns.length == 0) { - return wrapCampaignRules(List.of()); - } - - String campaignId = campaigns[0].getCampaignId(); - return listCampaignRules(campaignId, 0, 100); - } catch (Exception e) { - log.error("Failed to list Campaign Rules by Campaign Name", e); - throw new RuntimeException("Failed to list Campaign Rules by Campaign Name: " + e.getMessage(), e); - } - } - - private McpResponseWrapper> wrapCampaignRules(List data) { - StringBuilder markdown = new StringBuilder(); - if (data.isEmpty()) { - markdown.append("No campaign rules found."); - } else { - for (CampaignRuleDto r : data) { - markdown.append(MarkdownGenerator.generateCampaignRuleMarkdown(r)).append("\n"); - } - } - return new McpResponseWrapper<>(data, new Presentation("markdown", markdown.toString())); - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java deleted file mode 100644 index 4cb6191..0000000 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignTools.java +++ /dev/null @@ -1,148 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.reward; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto; -import dev.sonpx.loyalty.mcp.draft.DependencyValidator; -import dev.sonpx.loyalty.mcp.draft.DraftSessionManager; -import dev.sonpx.loyalty.mcp.draft.model.DraftSession; -import dev.sonpx.loyalty.mcp.model.McpResponseWrapper; -import dev.sonpx.loyalty.mcp.model.Presentation; -import dev.sonpx.loyalty.mcp.tool.model.reward.SubmitCampaignDraftRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignDraftRequest; -import dev.sonpx.loyalty.mcp.util.MarkdownGenerator; -import java.util.List; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; - -@Component -public class CampaignTools { - - private static final Logger log = LoggerFactory.getLogger(CampaignTools.class); - - private final RestClient rewardRestClient; - private final DraftSessionManager draftSessionManager; - private final DependencyValidator dependencyValidator; - - public CampaignTools( - @Qualifier("rewardRestClient") RestClient rewardRestClient, - DraftSessionManager draftSessionManager, - DependencyValidator dependencyValidator) { - this.rewardRestClient = rewardRestClient; - this.draftSessionManager = draftSessionManager; - this.dependencyValidator = dependencyValidator; - } - - @Tool(description = "List or Search Campaigns, optionally filtered by campaignId, name, or status. ALWAYS use this tool when the user asks for information, details, or status of an existing campaign. If the response contains a `presentation.content` field, display its content exactly as provided to the user. Use `data` only for your internal reasoning.") - public McpResponseWrapper> listCampaigns( - @org.springframework.lang.Nullable String campaignId, - @org.springframework.lang.Nullable String name, - @org.springframework.lang.Nullable String status, - @org.springframework.lang.Nullable Integer page, - @org.springframework.lang.Nullable Integer size) { - int pageNum = page != null ? page : 0; - int sizeNum = size != null ? size : 20; - - try { - String jsonResponse = rewardRestClient.get() - .uri(uriBuilder -> { - uriBuilder.path("/api/campaign/csr/list") - .queryParam("page", pageNum) - .queryParam("size", sizeNum); - - if (campaignId != null && !campaignId.isEmpty()) { - uriBuilder.queryParam("campaignId.equals", campaignId); - } - if (name != null && !name.isEmpty()) { - uriBuilder.queryParam("name.contains", name); - } - if (status != null && !status.isEmpty()) { - uriBuilder.queryParam("status.equals", status); - } - return uriBuilder.build(); - }) - .retrieve() - .body(String.class); - - // Fix missing timezone in date-time strings from backend (e.g. "2026-06-25T16:16:01" -> "2026-06-25T16:16:01Z") - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\""); - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\""); - - com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); - mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); - mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - CampaignDto[] campaigns = null; - try { - campaigns = mapper.readValue(jsonResponse, CampaignDto[].class); - } catch (Exception parseEx) { - com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse); - if (root.has("content")) { - campaigns = mapper.treeToValue(root.get("content"), CampaignDto[].class); - } else if (root.has("data")) { - campaigns = mapper.treeToValue(root.get("data"), CampaignDto[].class); - } else { - throw parseEx; - } - } - - List data = campaigns != null ? List.of(campaigns) : List.of(); - StringBuilder markdown = new StringBuilder(); - if (data.isEmpty()) { - markdown.append("No campaigns found."); - } else { - for (CampaignDto c : data) { - markdown.append(MarkdownGenerator.generateCampaignMarkdown(c)).append("\n"); - } - } - return new McpResponseWrapper<>(data, new Presentation("markdown", markdown.toString())); - } catch (Exception e) { - log.error("Failed to list Campaigns", e); - throw new RuntimeException("Failed to list Campaigns: " + e.getMessage(), e); - } - } - - @Tool(description = "Update local Campaign Draft.") - public Map updateCampaignDraft(UpdateCampaignDraftRequest request) { - if (request.conversationId() == null || request.conversationId().isBlank()) { - throw new IllegalArgumentException("conversationId is required"); - } - - draftSessionManager.updateSession(request.conversationId(), session -> { - CampaignDto campaign = session.getCampaignDraft().getCampaign(); - if (request.campaignId() != null) campaign.setCampaignId(request.campaignId()); - if (request.name() != null) campaign.setName(request.name()); - if (request.targetTierId() != null) campaign.setTargetTierId(request.targetTierId()); - }); - - return Map.of("status", "Campaign draft updated successfully", "campaignId", request.campaignId()); - } - - @Tool(description = "Submit the completed CampaignDraft to Core API.") - public Map submitCampaignDraft(SubmitCampaignDraftRequest request) { - if (request.conversationId() == null || request.conversationId().isBlank()) { - throw new IllegalArgumentException("conversationId is required"); - } - - DraftSession session = draftSessionManager.getSession(request.conversationId()); - CampaignDto campaign = session.getCampaignDraft().getCampaign(); - - // Validate - dependencyValidator.validateCampaignRequiredFields(campaign); - - // Submit via Core API - try { - return rewardRestClient.post() - .uri("/api/campaign/csr/create-draft") - .body(campaign) - .retrieve() - .body(new ParameterizedTypeReference<>() {}); - } catch (Exception e) { - log.error("Failed to submit Campaign draft via Core API", e); - return Map.of("error", "Failed to submit Campaign draft via Core API: " + e.getMessage()); - } - } -} diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java index 624f1d5..8ca4a87 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/util/MarkdownGenerator.java @@ -1,60 +1,88 @@ package dev.sonpx.loyalty.mcp.util; -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignDto; -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; +import dev.sonpx.loyalty.mcp.model.OperationResult; +import dev.sonpx.loyalty.mcp.reward.model.Campaign; +import java.util.List; +import org.springframework.util.CollectionUtils; public class MarkdownGenerator { - public static String generateCampaignMarkdown(CampaignDto campaign) { + public static String generateCampaignMD(Campaign campaign) { if (campaign == null) { return "No campaign data available."; } - + StringBuilder sb = new StringBuilder(); sb.append("### 🏆 Campaign Details\n\n"); sb.append("| Field | Value |\n"); sb.append("| :--- | :--- |\n"); sb.append("| **Campaign ID** | ").append(campaign.getCampaignId()).append(" |\n"); sb.append("| **Name** | ").append(campaign.getName()).append(" |\n"); - sb.append("| **Status** | ").append(campaign.getStatus() != null ? campaign.getStatus() : "N/A").append(" |\n"); - sb.append("| **Target Tier ID** | ").append(campaign.getTargetTierId() != null ? campaign.getTargetTierId() : "N/A").append(" |\n"); - sb.append("| **Reward Pool ID** | ").append(campaign.getRewardPoolId() != null ? campaign.getRewardPoolId() : "N/A").append(" |\n"); - + if (campaign.getDescription() != null) { sb.append("| **Description** | ").append(campaign.getDescription()).append(" |\n"); } - - if (campaign.getExtraData() != null && !campaign.getExtraData().isEmpty()) { - for (java.util.Map.Entry entry : campaign.getExtraData().entrySet()) { - String key = entry.getKey(); - String formattedKey = key.substring(0, 1).toUpperCase() + key.substring(1).replaceAll("([A-Z])", " $1"); - sb.append("| **").append(formattedKey).append("** | ").append(entry.getValue()).append(" |\n"); - } - } + return sb.toString(); } - public static String generateCampaignRuleMarkdown(CampaignRuleDto rule) { - if (rule == null) { - return "No campaign rule data available."; + public static String generateCampaignMD(List campaigns) { + if (CollectionUtils.isEmpty(campaigns)) { + return "No campaigns data available."; + } + + StringBuilder sb = new StringBuilder(); + sb.append("### 🏆 Campaign List\n\n"); + sb.append("| Id | Name | Description |\n"); + sb.append("| :--- | :--- | :--- |\n"); + + campaigns.forEach((campaign) -> { + sb.append("| ").append(campaign.getCampaignId()); + sb.append(" | ").append(campaign.getName()); + sb.append(" | ").append(campaign.getDescription()).append(" |\n"); + }); + + + return sb.toString(); + } + + public static String generateCampaignMD(Long count) { + if (count == null) { + return "Total campaigns: 0"; + } + return "Total campaigns: " + count; + } + + public static String generateCampaignIdMD(String id) { + if (id == null) { + return "No campaign ID available."; + } + return "Campaign ID: " + id; + } + + public static String generateCheckResultMD(String result) { + if (result == null) { + return "No check result available."; + } + return "Check result: " + result; + } + + public static String generateOperationResultMD(OperationResult result) { + if (result == null) { + return "No operation result available."; } - String campaignCodeStr = (rule.getCampaignId() != null && rule.getCampaignId().getCode() != null) ? rule.getCampaignId().getCode() : "N/A"; - String poolCodeStr = (rule.getPoolId() != null && rule.getPoolId().getCode() != null) ? rule.getPoolId().getCode() : "N/A"; - StringBuilder sb = new StringBuilder(); - sb.append("### 📜 Campaign Rule Details\n\n"); - sb.append("| Field | Value |\n"); - sb.append("| :--- | :--- |\n"); - sb.append("| **Rule ID** | ").append(rule.getRuleId()).append(" |\n"); - sb.append("| **Rule Name** | ").append(rule.getRuleName()).append(" |\n"); - sb.append("| **Rule Type** | ").append(rule.getRuleType() != null ? rule.getRuleType().getValue() : "N/A").append(" |\n"); - sb.append("| **Campaign ID** | ").append(campaignCodeStr).append(" |\n"); - sb.append("| **Pool ID** | ").append(poolCodeStr).append(" |\n"); - sb.append("| **Description** | ").append(rule.getDescription() != null ? rule.getDescription() : "N/A").append(" |\n"); - sb.append("| **Effective From** | ").append(rule.getEffectiveFrom() != null ? rule.getEffectiveFrom().toString() : "N/A").append(" |\n"); - sb.append("| **Effective To** | ").append(rule.getEffectiveTo() != null ? rule.getEffectiveTo().toString() : "N/A").append(" |\n"); - + if ("00".equals(result.getErrorCode()) || result.getErrorCode() == null) { + sb.append("✅ Operation successful.\n\n"); + if (result.getEntity() != null) { + sb.append(generateCampaignMD(result.getEntity())); + } + } else { + sb.append("❌ Operation failed.\n\n"); + sb.append("**Error Code:** ").append(result.getErrorCode()).append("\n"); + sb.append("**Message:** ").append(result.getMessage()).append("\n"); + } return sb.toString(); } } diff --git a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/web/rest/RewardResource.java b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/web/rest/RewardResource.java index fda2f37..c310793 100644 --- a/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/web/rest/RewardResource.java +++ b/loyalty-mcp-server/src/main/java/dev/sonpx/loyalty/mcp/web/rest/RewardResource.java @@ -1,11 +1,11 @@ package dev.sonpx.loyalty.mcp.web.rest; -import dev.sonpx.loyalty.mcp.reward.client.RewardClient; +import dev.sonpx.loyalty.mcp.model.Result; import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria; -import dev.sonpx.loyalty.mcp.reward.model.CampaignDto; +import dev.sonpx.loyalty.mcp.reward.model.Campaign; +import dev.sonpx.loyalty.mcp.service.CampaignService; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -19,10 +19,11 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor public class RewardResource { - private final RewardClient rewardClient; + private final CampaignService campaignService; @GetMapping - public ResponseEntity> getAll(CampaignCriteria criteria) { - return rewardClient.getAll(criteria, Pageable.ofSize(10)); + public ResponseEntity>> getAll(CampaignCriteria criteria) { + return ResponseEntity.ok(campaignService.getAll(criteria)); } -} + +} \ No newline at end of file diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/RewardClientTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/RewardClientTest.java deleted file mode 100644 index aeacdd1..0000000 --- a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/RewardClientTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package dev.sonpx.loyalty.mcp; - -import dev.sonpx.loyalty.mcp.reward.client.RewardClient; -import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.domain.Pageable; - -@SpringBootTest -public class RewardClientTest { - @Autowired - RewardClient rewardClient; - - @Test - void test() { - rewardClient.getAll(new CampaignCriteria(), Pageable.unpaged()); - } -} diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java deleted file mode 100644 index 18f381e..0000000 --- a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/test/TestParsing.java +++ /dev/null @@ -1,40 +0,0 @@ -package dev.sonpx.loyalty.mcp.test; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; - -public class TestParsing { - public static void main(String[] args) throws Exception { - String jsonResponse = new String(Files.readAllBytes(Paths.get("rules.json"))); - - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})\"", "\"$1Z\""); - jsonResponse = jsonResponse.replaceAll("\"([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+)\"", "\"$1Z\""); - - System.out.println("Transformed JSON: " + jsonResponse.substring(0, Math.min(200, jsonResponse.length()))); - - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - try { - CampaignRuleDto[] rules = mapper.readValue(jsonResponse, CampaignRuleDto[].class); - System.out.println("Parsed directly as array, length: " + rules.length); - } catch (Exception parseEx) { - System.out.println("Failed to parse as array: " + parseEx.getMessage()); - com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(jsonResponse); - if (root.has("content")) { - CampaignRuleDto[] rules = mapper.treeToValue(root.get("content"), CampaignRuleDto[].class); - System.out.println("Parsed from content, length: " + rules.length); - } else if (root.has("data")) { - CampaignRuleDto[] rules = mapper.treeToValue(root.get("data"), CampaignRuleDto[].class); - System.out.println("Parsed from data, length: " + rules.length); - } else { - throw parseEx; - } - } - } -} diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java deleted file mode 100644 index 295e91a..0000000 --- a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool; - -import dev.sonpx.loyalty.mcp.client.api.CampaignApiClient; -import dev.sonpx.loyalty.mcp.client.api.MemberTierApiClient; -import dev.sonpx.loyalty.mcp.client.api.PointPoolApiClient; -import dev.sonpx.loyalty.mcp.client.model.Campaign; -import dev.sonpx.loyalty.mcp.client.model.CreateCampaignRequest; -import dev.sonpx.loyalty.mcp.client.model.MemberTier; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestClient; -import org.springframework.web.client.support.RestClientAdapter; -import org.springframework.web.service.invoker.HttpServiceProxyFactory; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; - -class LoyaltyAgentToolsRealApiTest { - - private MockRestServiceServer loyaltyServer; - private MockRestServiceServer rewardServer; - private LoyaltyAgentTools tools; - - @BeforeEach - void setUp() { - RestClient.Builder loyaltyRestClientBuilder = RestClient.builder().baseUrl("http://core.test"); - loyaltyServer = MockRestServiceServer.bindTo(loyaltyRestClientBuilder).build(); - RestClient loyaltyRestClient = loyaltyRestClientBuilder.build(); - - RestClient.Builder rewardRestClientBuilder = RestClient.builder().baseUrl("http://reward.test"); - rewardServer = MockRestServiceServer.bindTo(rewardRestClientBuilder).build(); - RestClient rewardRestClient = rewardRestClientBuilder.build(); - - MemberTierApiClient memberTierApiClient = createClient(loyaltyRestClient, MemberTierApiClient.class); - PointPoolApiClient pointPoolApiClient = createClient(rewardRestClient, PointPoolApiClient.class); - CampaignApiClient campaignApiClient = createClient(rewardRestClient, CampaignApiClient.class); - tools = new LoyaltyAgentTools(memberTierApiClient, pointPoolApiClient, campaignApiClient, loyaltyRestClient); - } - - @Test - void listMemberTiersCallsRealEndpoint() { - loyaltyServer.expect(requestTo("http://core.test/svc/reward/api/decide-tier-view/list?page=0&size=100&status.in=A")) - .andExpect(method(GET)) - .andRespond(withSuccess("[{\"key\":\"TIER_GOLD\",\"code\":\"GOLD\",\"name\":\"Gold\"}]", MediaType.APPLICATION_JSON)); - - MemberTier tier = tools.listMemberTiers().getFirst(); - - assertEquals("TIER_GOLD", tier.tierId()); - assertEquals("Gold", tier.tierName()); - loyaltyServer.verify(); - } - - @Test - void listPointPoolsCallsRealEndpoint() { - rewardServer.expect(requestTo("http://reward.test/api/pool-definition/all")) - .andExpect(method(GET)) - .andRespond(withSuccess("[{\"poolId\":\"POOL_1\",\"poolName\":\"Main Pool\"}]", MediaType.APPLICATION_JSON)); - - assertEquals("POOL_1", tools.listPointPools().getFirst().poolId()); - rewardServer.verify(); - } - - @Test - void createCampaignGeneratesIdAndUnwrapsOperationResultEntity() { - rewardServer.expect(requestTo("http://reward.test/api/campaign/csr/id/generate")) - .andExpect(method(GET)) - .andRespond(withSuccess("CAMP_1", MediaType.TEXT_PLAIN)); - rewardServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft")) - .andExpect(method(POST)) - .andRespond(withSuccess( - "{\"message\":\"OK\",\"entity\":{\"campaignId\":\"CAMP_1\",\"name\":\"Summer\",\"status\":\"DRAFT\"}}", - MediaType.APPLICATION_JSON)); - - Campaign campaign = tools.createCampaign(new CreateCampaignRequest("Summer", "TIER_GOLD", "POOL_1")); - - assertEquals("CAMP_1", campaign.campaignId()); - assertEquals("DRAFT", campaign.status()); - rewardServer.verify(); - } - - private static T createClient(RestClient restClient, Class clientType) { - HttpServiceProxyFactory factory = HttpServiceProxyFactory - .builderFor(RestClientAdapter.create(restClient)) - .build(); - return factory.createClient(clientType); - } -} diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java deleted file mode 100644 index 60b0ed7..0000000 --- a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool; - -import dev.sonpx.loyalty.mcp.draft.DependencyValidator; -import dev.sonpx.loyalty.mcp.draft.DraftSessionManager; -import dev.sonpx.loyalty.mcp.draft.model.DraftSession; -import dev.sonpx.loyalty.mcp.tool.model.pool.CreatePoolDefinitionRequest; -import dev.sonpx.loyalty.mcp.tool.model.pool.CreateSopRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.AddRuleToCampaignRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.SubmitCampaignDraftRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignDraftRequest; -import dev.sonpx.loyalty.mcp.tool.pool.PointPoolTools; -import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleMapper; -import dev.sonpx.loyalty.mcp.tool.reward.CampaignRuleTools; -import dev.sonpx.loyalty.mcp.tool.reward.CampaignTools; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.web.client.RestClient; - -import java.time.OffsetDateTime; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.method; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; -import static org.springframework.http.HttpMethod.POST; - -class McpOrchestratorTest { - - private DraftSessionManager sessionManager; - private DependencyValidator dependencyValidator; - private MockRestServiceServer mockServer; - - private PointPoolTools pointPoolTools; - private CampaignTools campaignTools; - private CampaignRuleTools campaignRuleTools; - - @BeforeEach - void setUp() { - sessionManager = new DraftSessionManager(); - dependencyValidator = new DependencyValidator(); - RestClient.Builder restClientBuilder = RestClient.builder().baseUrl("http://reward.test"); - mockServer = MockRestServiceServer.bindTo(restClientBuilder).build(); - RestClient restClient = restClientBuilder.build(); - - pointPoolTools = new PointPoolTools(sessionManager, dependencyValidator, restClient); - campaignTools = new CampaignTools(restClient, sessionManager, dependencyValidator); - campaignRuleTools = new CampaignRuleTools(new CampaignRuleMapper(), restClient, sessionManager, dependencyValidator); - } - - @Test - void testFullDependencyGraphAndErrorPropagation() { - String convId = "test-conv-123"; - - mockServer.expect(requestTo("http://reward.test/api/pool-definition/csr/create-draft")) - .andExpect(method(POST)) - .andRespond(withSuccess("{\"poolId\":\"POOL-1\"}", MediaType.APPLICATION_JSON)); - mockServer.expect(requestTo("http://reward.test/api/sop/csr/create-draft")) - .andExpect(method(POST)) - .andRespond(withSuccess("{\"sopId\":\"SOP-1\"}", MediaType.APPLICATION_JSON)); - mockServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft")) - .andExpect(method(POST)) - .andRespond(withSuccess("{\"campaignId\":\"CAMP-1\",\"status\":\"DRAFT_CREATED\"}", MediaType.APPLICATION_JSON)); - mockServer.expect(requestTo("http://reward.test/api/campaign/csr/create-draft")) - .andExpect(method(POST)) - .andRespond(withStatus(HttpStatus.BAD_REQUEST).body("400 Bad Request")); - - // 1. Create Pool Definition - CreatePoolDefinitionRequest poolReq = new CreatePoolDefinitionRequest(convId, "POOL-1", "Test Pool", "STANDARD", "USD"); - Map poolRes = pointPoolTools.createPoolDefinition(poolReq); - assertEquals("POOL-1", poolRes.get("poolId")); - - // 2. Create SOP - CreateSopRequest sopReq = new CreateSopRequest(convId, "SOP-1", "POOL-1", "Test SOP", "ACCRUAL"); - Map sopRes = pointPoolTools.createSop(sopReq); - assertEquals("SOP-1", sopRes.get("sopId")); - - // 3. Create Campaign Draft - UpdateCampaignDraftRequest updateCampReq = new UpdateCampaignDraftRequest(convId, "CAMP-1", "My Campaign", "TIER-1"); - campaignTools.updateCampaignDraft(updateCampReq); - - // 4. Add Rule to Campaign (Valid) - CreateCampaignRuleToolRequest ruleDetails = new CreateCampaignRuleToolRequest("CAMP-1", "RULE-1", "My Rule", "AWD", "Desc", OffsetDateTime.now(), OffsetDateTime.now(), "POOL-1", null); - AddRuleToCampaignRequest addRuleReq = new AddRuleToCampaignRequest(convId, "CAMP-1", "TXN-CODE-1", true, "POOL-1", ruleDetails); - Map ruleRes = campaignRuleTools.addRuleToCampaign(addRuleReq); - assertEquals("RULE-1", ruleRes.get("ruleId")); - - // 5. Submit Campaign Draft - SubmitCampaignDraftRequest submitCampReq = new SubmitCampaignDraftRequest(convId); - Map submitCampRes = campaignTools.submitCampaignDraft(submitCampReq); - assertEquals("CAMP-1", submitCampRes.get("campaignId")); - - // Test Error Propagation: Missing Pool ID for SOP - CreateSopRequest badSopReq = new CreateSopRequest(convId, "SOP-2", "", "Bad SOP", "ACCRUAL"); - assertThrows(IllegalArgumentException.class, () -> pointPoolTools.createSop(badSopReq)); - - // Test Error Propagation: Add Rule missing required Pool ID - AddRuleToCampaignRequest badRuleReq = new AddRuleToCampaignRequest(convId, "CAMP-1", "TXN-CODE-2", true, "", ruleDetails); - assertThrows(IllegalArgumentException.class, () -> campaignRuleTools.addRuleToCampaign(badRuleReq)); - - // Test Error propagation from Core API - Map errorRes = campaignTools.submitCampaignDraft(submitCampReq); - assertTrue(((String)errorRes.get("error")).contains("400 Bad Request")); - - mockServer.verify(); - } -} diff --git a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java b/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java deleted file mode 100644 index d5eb29b..0000000 --- a/loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package dev.sonpx.loyalty.mcp.tool.reward; - -import dev.sonpx.loyalty.mcp.client.model.reward.CampaignRuleDto; -import dev.sonpx.loyalty.mcp.tool.model.reward.CreateCampaignRuleToolRequest; -import dev.sonpx.loyalty.mcp.tool.model.reward.UpdateCampaignRuleToolRequest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.OffsetDateTime; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class CampaignRuleMapperTest { - - private CampaignRuleMapper mapper; - - @BeforeEach - void setUp() { - mapper = new CampaignRuleMapper(); - } - - @Test - void testToDto_CreateRequest_WithFormulaOne() { - CreateCampaignRuleToolRequest request = new CreateCampaignRuleToolRequest( - "CAMPAIGN-123", - "RULE-1", - "My Rule", - "AWD", - "Description", - OffsetDateTime.now(), - OffsetDateTime.now().plusDays(10), - "POOL-ABC", - new CreateCampaignRuleToolRequest.FormulaOneConfig(10.0, 100.0) - ); - - CampaignRuleDto dto = mapper.toDto(request); - - assertNotNull(dto); - assertNotNull(dto.getCampaignId()); - assertEquals("CAMPAIGN-123", dto.getCampaignId().getCode()); - assertEquals("RULE-1", dto.getRuleId()); - assertEquals("My Rule", dto.getRuleName()); - assertEquals(CampaignRuleDto.RuleTypeEnum.AWD, dto.getRuleType()); - assertNotNull(dto.getPoolId()); - assertEquals("POOL-ABC", dto.getPoolId().getCode()); - assertNotNull(dto.getCampaignFormulaOne()); - assertEquals(10.0, dto.getCampaignFormulaOne().getAwardPerTxnAmt()); - assertEquals(100.0, dto.getCampaignFormulaOne().getTxnAmt()); - assertEquals(1, dto.getFormulaSequence().size()); - assertEquals("1", dto.getFormulaSequence().get(0)); - } - - @Test - void testToDto_UpdateRequest_WithoutFormulaOne() { - UpdateCampaignRuleToolRequest request = new UpdateCampaignRuleToolRequest( - "CAMPAIGN-456", - "RULE-2", - "My Updated Rule", - "RED", - "Updated Description", - null, - null, - "POOL-XYZ", - null - ); - - CampaignRuleDto dto = mapper.toDto(request); - - assertNotNull(dto); - assertNotNull(dto.getCampaignId()); - assertEquals("CAMPAIGN-456", dto.getCampaignId().getCode()); - assertEquals("RULE-2", dto.getRuleId()); - assertEquals("My Updated Rule", dto.getRuleName()); - assertEquals(CampaignRuleDto.RuleTypeEnum.RED, dto.getRuleType()); - assertNotNull(dto.getPoolId()); - assertEquals("POOL-XYZ", dto.getPoolId().getCode()); - assertNull(dto.getCampaignFormulaOne()); - assertTrue(dto.getFormulaSequence() == null || dto.getFormulaSequence().isEmpty()); - } -} diff --git a/plan/master_plan.md b/plan/master_plan.md index 03faf21..7b1114b 100644 --- a/plan/master_plan.md +++ b/plan/master_plan.md @@ -55,7 +55,7 @@ MCP Server cần giữ local draft theo conversation/session để user có th Required fields quan trọng theo spec: -- `CampaignDto`: `campaignId`, `name`. +- `Campaign`: `campaignId`, `name`. - `CampaignRuleDto`: `campaignId`, `ruleId`, `ruleName`. - `PoolDefinitionDto`: `poolId`, `poolName`. - `TransactionCodeDto`: `code`, `description`. @@ -132,7 +132,7 @@ Không làm cả 11 resource ngang nhau ngay. Ưu tiên theo giá trị end-to-e **Milestone 3: Campaign Setup** -- Tool tạo draft `CampaignDto`. +- Tool tạo draft `Campaign`. - Agent tự generate/check `campaignId` nếu user chưa có mã. - Giữ template tối thiểu: `campaignId`, `name`, `ownerName`, `description`, `campaignType`, `effectiveFrom`, `effectiveTo`. - Không yêu cầu user nhập `numOfRule`; field này nên derive/hiển thị từ Core nếu có. @@ -220,7 +220,7 @@ _Mục tiêu: từ intent tự nhiên tạo được campaign/rule/pool hoàn ch - [ ] Parse `reward.json` thành metadata nội bộ: resources, schemas, required fields, enums, readOnly fields, endpoints. - [ ] Implement generic Reward API client cho CSR pattern. - [ ] Implement MCP tools read-only cho Campaign/Rule/Pool/Transaction Code. -- [ ] Implement local draft store cho `CampaignDto`, `PoolDefinitionDto`, `CampaignRuleDto`. +- [ ] Implement local draft store cho `Campaign`, `PoolDefinitionDto`, `CampaignRuleDto`. - [ ] Implement validator dùng OpenAPI schema. - [ ] Implement create-draft flow cho Pool Definition. - [ ] Implement create-draft flow cho Campaign.