feat: implement RestClient configuration and service layers for reward system integration

This commit is contained in:
SonPhung
2026-07-26 23:48:54 +07:00
parent 4534e4ecb8
commit 66fe2ae9d7
16 changed files with 378 additions and 0 deletions

View File

@@ -88,4 +88,24 @@ public class RestClientConfig {
public CampaignRuleClient campaignRuleClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
return rewardHttpServiceProxyFactory.createClient(CampaignRuleClient.class);
}
@Bean
public dev.sonpx.loyalty.mcp.reward.client.PoolDefinitionClient poolDefinitionClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
return rewardHttpServiceProxyFactory.createClient(dev.sonpx.loyalty.mcp.reward.client.PoolDefinitionClient.class);
}
@Bean
public dev.sonpx.loyalty.mcp.reward.client.CounterDefinitionClient counterDefinitionClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
return rewardHttpServiceProxyFactory.createClient(dev.sonpx.loyalty.mcp.reward.client.CounterDefinitionClient.class);
}
@Bean
public dev.sonpx.loyalty.mcp.reward.client.DeductionSequenceClient deductionSequenceClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
return rewardHttpServiceProxyFactory.createClient(dev.sonpx.loyalty.mcp.reward.client.DeductionSequenceClient.class);
}
@Bean
public dev.sonpx.loyalty.mcp.reward.client.TransactionCodeClient transactionCodeClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
return rewardHttpServiceProxyFactory.createClient(dev.sonpx.loyalty.mcp.reward.client.TransactionCodeClient.class);
}
}

View File

@@ -40,4 +40,13 @@ public interface CampaignRuleClient {
@PostExchange("/api/campaign-rule/csr/create")
OperationResult<CampaignRule> create(@Valid @RequestBody CampaignRule dto);
@GetExchange("/api/campaign-rule/find-matching-rules")
List<CampaignRule> findMatchingRules(
@RequestParam("ruleType") String ruleType,
@RequestParam("transactionCode") String transactionCode,
@RequestParam("transactionDate") String transactionDate,
@RequestParam("postDate") String postDate,
@RequestParam("languageCode") String languageCode
);
}

View File

@@ -0,0 +1,18 @@
package dev.sonpx.loyalty.mcp.reward.client;
import dev.sonpx.loyalty.mcp.reward.model.CounterDefinition;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
@HttpExchange
public interface CounterDefinitionClient {
@GetExchange("/api/counter-definition/counter-id/{counterId}")
CounterDefinition findById(@PathVariable("counterId") String counterId);
@GetExchange("/api/counter-definition/list-by-ids")
List<CounterDefinition> findActiveByIds(@RequestParam("idList") List<String> idList);
}

View File

@@ -0,0 +1,16 @@
package dev.sonpx.loyalty.mcp.reward.client;
import dev.sonpx.loyalty.mcp.reward.model.DeductionSequences;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
@HttpExchange
public interface DeductionSequenceClient {
@GetExchange("/api/deduction-seq/find-deduction-seq-by-id")
DeductionSequences findById(@RequestParam("deductionSeqId") String deductionSeqId);
@GetExchange("/api/deduction-seq/find-default")
DeductionSequences findDefault();
}

View File

@@ -0,0 +1,18 @@
package dev.sonpx.loyalty.mcp.reward.client;
import dev.sonpx.loyalty.mcp.reward.model.PoolDefinition;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
@HttpExchange
public interface PoolDefinitionClient {
@GetExchange("/api/pool-definition/{poolId}")
PoolDefinition findById(@PathVariable("poolId") String poolId);
@GetExchange("/api/pool-definition/list-by-ids")
List<PoolDefinition> findActiveByIds(@RequestParam("idList") List<String> idList);
}

View File

@@ -0,0 +1,20 @@
package dev.sonpx.loyalty.mcp.reward.client;
import dev.sonpx.loyalty.mcp.reward.model.TransactionCode;
import java.util.List;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.HttpExchange;
@HttpExchange
public interface TransactionCodeClient {
@GetExchange("/api/transaction-code")
TransactionCode findByCode(@RequestParam("transactionCode") String transactionCode);
@GetExchange("/api/transaction-code/list-by-codes")
List<TransactionCode> findByCodes(@RequestParam("transactionCodes") List<String> transactionCodes);
@GetExchange("/api/transaction-code/exists")
Boolean exists(@RequestParam("transactionCode") String transactionCode);
}

View File

@@ -162,5 +162,17 @@ public class CampaignRuleService {
- Nếu lỗi: thông báo lỗi rõ ràng, giải thích nguyên nhân, gợi ý cách khắc phục.
""");
}
public Result<List<CampaignRule>> findMatchingRules(String ruleType, String transactionCode, String transactionDate, String postDate, String languageCode) {
List<CampaignRule> rules = campaignRuleClient.findMatchingRules(ruleType, transactionCode, transactionDate, postDate, languageCode);
if (rules != null) {
rules.forEach(this::sanitizeFormulas);
}
return Result.of(rules, """
Trả lời tự nhiên bằng tiếng Việt. Hiển thị danh sách các rule/thể lệ khớp với thông tin giao dịch,
mỗi rule ghi: Tên, Mã Rule, Loại, Thời gian hiệu lực.
Tóm tắt ngắn gọn cấu hình thưởng nếu có. Không dùng markdown table.
""");
}
}

View File

@@ -0,0 +1,37 @@
package dev.sonpx.loyalty.mcp.service;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.client.CounterDefinitionClient;
import dev.sonpx.loyalty.mcp.reward.model.CounterDefinition;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class CounterDefinitionService {
private final CounterDefinitionClient counterDefinitionClient;
public Result<CounterDefinition> findById(String counterId) {
CounterDefinition counter = counterDefinitionClient.findById(counterId);
return Result.of(counter, """
Trả lời tự nhiên bằng tiếng Việt. Trình bày chi tiết thông tin Counter một cách rõ ràng:
- Thông tin cơ bản: Tên Counter, Mã Counter, Loại Counter.
- Chu kỳ, Giới hạn Counter.
Dùng bullet list hoặc dạng key-value dễ đọc.
""");
}
public Result<List<CounterDefinition>> findActiveByIds(String ids) {
List<String> idList = Arrays.asList(ids.split(",\\s*"));
List<CounterDefinition> counters = counterDefinitionClient.findActiveByIds(idList);
return Result.of(counters, """
Trả lời tự nhiên bằng tiếng Việt. Hiển thị danh sách các Counter, mỗi Counter ghi: Tên, Mã, Loại.
Không dùng markdown table.
""");
}
}

View File

@@ -0,0 +1,33 @@
package dev.sonpx.loyalty.mcp.service;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.client.DeductionSequenceClient;
import dev.sonpx.loyalty.mcp.reward.model.DeductionSequences;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class DeductionSequenceService {
private final DeductionSequenceClient deductionSequenceClient;
public Result<DeductionSequences> findById(String deductionSeqId) {
DeductionSequences seq = deductionSequenceClient.findById(deductionSeqId);
return Result.of(seq, """
Trả lời tự nhiên bằng tiếng Việt. Trình bày chi tiết cấu hình Deduction Sequence:
- Tên Sequence, Mã Sequence.
- Các Pool được liên kết và thứ tự trừ điểm.
Dùng bullet list dễ đọc.
""");
}
public Result<DeductionSequences> findDefault() {
DeductionSequences seq = deductionSequenceClient.findDefault();
return Result.of(seq, """
Trả lời tự nhiên bằng tiếng Việt. Trình bày thông tin Deduction Sequence mặc định.
""");
}
}

View File

@@ -0,0 +1,38 @@
package dev.sonpx.loyalty.mcp.service;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.client.PoolDefinitionClient;
import dev.sonpx.loyalty.mcp.reward.model.PoolDefinition;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class PoolDefinitionService {
private final PoolDefinitionClient poolDefinitionClient;
public Result<PoolDefinition> findById(String poolId) {
PoolDefinition pool = poolDefinitionClient.findById(poolId);
return Result.of(pool, """
Trả lời tự nhiên bằng tiếng Việt. Trình bày chi tiết thông tin Point Pool một cách rõ ràng:
- Thông tin cơ bản: Tên Pool, Mã Pool, Đơn vị tiền tệ.
- Loại Pool, Cấp độ Pool (Entity Level).
- Nhóm Pool (Pool Group), Tài khoản kế toán (GL Account).
Dùng bullet list hoặc dạng key-value dễ đọc.
""");
}
public Result<List<PoolDefinition>> findActiveByIds(String ids) {
List<String> idList = Arrays.asList(ids.split(",\\s*"));
List<PoolDefinition> pools = poolDefinitionClient.findActiveByIds(idList);
return Result.of(pools, """
Trả lời tự nhiên bằng tiếng Việt. Hiển thị danh sách các Pool, mỗi pool ghi: Tên, Mã, Loại Pool.
Không dùng markdown table.
""");
}
}

View File

@@ -0,0 +1,44 @@
package dev.sonpx.loyalty.mcp.service;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.client.TransactionCodeClient;
import dev.sonpx.loyalty.mcp.reward.model.TransactionCode;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class TransactionCodeService {
private final TransactionCodeClient transactionCodeClient;
public Result<TransactionCode> findByCode(String code) {
TransactionCode txn = transactionCodeClient.findByCode(code);
return Result.of(txn, """
Trả lời tự nhiên bằng tiếng Việt. Trình bày chi tiết thông tin Transaction Code:
- Tên Giao dịch, Mã Giao dịch, Nhóm/Loại.
- Loại tiền tệ, Cấu hình khác nếu có.
Dùng bullet list dễ đọc.
""");
}
public Result<List<TransactionCode>> findByCodes(String codes) {
List<String> codeList = Arrays.asList(codes.split(",\\s*"));
List<TransactionCode> txnList = transactionCodeClient.findByCodes(codeList);
return Result.of(txnList, """
Trả lời tự nhiên bằng tiếng Việt. Hiển thị danh sách các Transaction Code.
Không dùng markdown table.
""");
}
public Result<Boolean> exists(String code) {
Boolean isExist = transactionCodeClient.exists(code);
return Result.of(isExist, """
Trả lời tự nhiên bằng tiếng Việt. Nếu exists=true thì báo mã giao dịch có tồn tại, nếu false thì không tồn tại.
""");
}
}

View File

@@ -65,4 +65,9 @@ public class CampaignRuleTools {
public Result<OperationResult<CampaignRule>> createRule(CampaignRule dto) {
return campaignRuleService.create(dto);
}
@McpTool(description = "Tìm kiếm các rule/thể lệ khớp với một giao dịch cụ thể. Yêu cầu truyền vào loại rule, mã giao dịch (transaction code), ngày giao dịch, ngày post và ngôn ngữ (ví dụ: 'vi' hoặc 'en'). Ngày tháng format yyyy-MM-dd'T'HH:mm:ss")
public Result<List<CampaignRule>> findMatchingRules(String ruleType, String transactionCode, String transactionDate, String postDate, String languageCode) {
return campaignRuleService.findMatchingRules(ruleType, transactionCode, transactionDate, postDate, languageCode);
}
}

View File

@@ -0,0 +1,26 @@
package dev.sonpx.loyalty.mcp.tool;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.model.CounterDefinition;
import dev.sonpx.loyalty.mcp.service.CounterDefinitionService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class CounterDefinitionTools {
private final CounterDefinitionService counterDefinitionService;
@McpTool(description = "Lấy thông tin chi tiết một Counter theo ID.")
public Result<CounterDefinition> getCounterDefinitionById(String counterId) {
return counterDefinitionService.findById(counterId);
}
@McpTool(description = "Lấy thông tin nhiều Counter theo danh sách ID, phân tách bằng dấu phẩy.")
public Result<List<CounterDefinition>> getCounterDefinitionsByIds(String ids) {
return counterDefinitionService.findActiveByIds(ids);
}
}

View File

@@ -0,0 +1,25 @@
package dev.sonpx.loyalty.mcp.tool;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.model.DeductionSequences;
import dev.sonpx.loyalty.mcp.service.DeductionSequenceService;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class DeductionSequenceTools {
private final DeductionSequenceService deductionSequenceService;
@McpTool(description = "Lấy thông tin cấu hình trừ điểm (Deduction Sequence) theo ID.")
public Result<DeductionSequences> getDeductionSequenceById(String deductionSeqId) {
return deductionSequenceService.findById(deductionSeqId);
}
@McpTool(description = "Lấy thông tin cấu hình trừ điểm (Deduction Sequence) mặc định của hệ thống.")
public Result<DeductionSequences> getDefaultDeductionSequence() {
return deductionSequenceService.findDefault();
}
}

View File

@@ -0,0 +1,26 @@
package dev.sonpx.loyalty.mcp.tool;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.model.PoolDefinition;
import dev.sonpx.loyalty.mcp.service.PoolDefinitionService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class PoolDefinitionTools {
private final PoolDefinitionService poolDefinitionService;
@McpTool(description = "Lấy thông tin chi tiết một Point Pool theo ID.")
public Result<PoolDefinition> getPoolDefinitionById(String poolId) {
return poolDefinitionService.findById(poolId);
}
@McpTool(description = "Lấy thông tin nhiều Point Pool theo danh sách ID, phân tách bằng dấu phẩy.")
public Result<List<PoolDefinition>> getPoolDefinitionsByIds(String ids) {
return poolDefinitionService.findActiveByIds(ids);
}
}

View File

@@ -0,0 +1,31 @@
package dev.sonpx.loyalty.mcp.tool;
import dev.sonpx.loyalty.mcp.model.Result;
import dev.sonpx.loyalty.mcp.reward.model.TransactionCode;
import dev.sonpx.loyalty.mcp.service.TransactionCodeService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class TransactionCodeTools {
private final TransactionCodeService transactionCodeService;
@McpTool(description = "Lấy thông tin chi tiết một Mã giao dịch (Transaction Code) theo mã.")
public Result<TransactionCode> getTransactionCode(String transactionCode) {
return transactionCodeService.findByCode(transactionCode);
}
@McpTool(description = "Lấy thông tin nhiều Mã giao dịch theo danh sách mã, phân tách bằng dấu phẩy.")
public Result<List<TransactionCode>> getTransactionCodes(String transactionCodes) {
return transactionCodeService.findByCodes(transactionCodes);
}
@McpTool(description = "Kiểm tra xem một Mã giao dịch có tồn tại hay không.")
public Result<Boolean> checkTransactionCodeExists(String transactionCode) {
return transactionCodeService.exists(transactionCode);
}
}