feat: implement campaign rule management infrastructure including criteria models, services, and tooling support
This commit is contained in:
@@ -4,7 +4,8 @@ import static org.springframework.security.oauth2.client.web.client.RequestAttri
|
|||||||
import static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;
|
import static org.springframework.security.oauth2.client.web.client.RequestAttributePrincipalResolver.principal;
|
||||||
|
|
||||||
import org.springframework.http.HttpStatusCode;
|
import org.springframework.http.HttpStatusCode;
|
||||||
import dev.sonpx.loyalty.mcp.reward.client.RewardClient;
|
import dev.sonpx.loyalty.mcp.reward.client.CampaignClient;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.client.CampaignRuleClient;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -20,13 +21,13 @@ import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
|||||||
* Created by SonPhung on Sunday, 19-Jul-2026
|
* Created by SonPhung on Sunday, 19-Jul-2026
|
||||||
**/
|
**/
|
||||||
@Configuration
|
@Configuration
|
||||||
public class ClientConfig {
|
public class RestClientConfig {
|
||||||
|
|
||||||
@Value("${loyalty.core.base-url}")
|
@Value("${loyalty.core.base-url}")
|
||||||
private String coreBaseUrl;
|
private String coreBaseUrl;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RestClient rewardReClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
public RestClient rewardRestClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
||||||
OAuth2ClientHttpRequestInterceptor oauth2Interceptor =
|
OAuth2ClientHttpRequestInterceptor oauth2Interceptor =
|
||||||
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
|
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
|
||||||
oauth2Interceptor.setClientRegistrationIdResolver(new RequestAttributeClientRegistrationIdResolver());
|
oauth2Interceptor.setClientRegistrationIdResolver(new RequestAttributeClientRegistrationIdResolver());
|
||||||
@@ -50,15 +51,20 @@ public class ClientConfig {
|
|||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public HttpServiceProxyFactory httpServiceProxyFactory(RestClient rewardReClient) {
|
public HttpServiceProxyFactory rewardHttpServiceProxyFactory(RestClient rewardRestClient) {
|
||||||
return HttpServiceProxyFactory.builderFor(RestClientAdapter.create(rewardReClient))
|
return HttpServiceProxyFactory.builderFor(RestClientAdapter.create(rewardRestClient))
|
||||||
.customArgumentResolver(new PageableArgumentResolver())
|
.customArgumentResolver(new PageableArgumentResolver())
|
||||||
.customArgumentResolver(new PojoArgumentResolver())
|
.customArgumentResolver(new PojoArgumentResolver())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RewardClient rewardClient(HttpServiceProxyFactory httpServiceProxyFactory) {
|
public CampaignClient campaignClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
|
||||||
return httpServiceProxyFactory.createClient(RewardClient.class);
|
return rewardHttpServiceProxyFactory.createClient(CampaignClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CampaignRuleClient campaignRuleClient(HttpServiceProxyFactory rewardHttpServiceProxyFactory) {
|
||||||
|
return rewardHttpServiceProxyFactory.createClient(CampaignRuleClient.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ import lombok.Data;
|
|||||||
* Created by SonPhung on Monday, 20-Jul-2026
|
* Created by SonPhung on Monday, 20-Jul-2026
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public abstract class AbstractFwCriteria implements FwCriteria {
|
public abstract class AbstractFwCriteria {
|
||||||
|
|
||||||
protected StringFilter status;
|
protected StringFilter status;
|
||||||
protected RangeFilter<Instant> lastUpdateDate;
|
protected RangeFilter<Instant> lastUpdateDate;
|
||||||
@@ -31,8 +31,4 @@ public abstract class AbstractFwCriteria implements FwCriteria {
|
|||||||
this.search = search;
|
this.search = search;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String[] getColumns();
|
|
||||||
|
|
||||||
protected abstract String[] getComponentColumns();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
package dev.sonpx.loyalty.mcp.criteria;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by SonPhung on Tuesday, 10-Oct-2023
|
|
||||||
*/
|
|
||||||
@JsonIgnoreProperties(value = {"columns", "columnsComponent"})
|
|
||||||
public interface FwCriteria {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@ import org.springframework.web.service.annotation.PostExchange;
|
|||||||
* Created by SonPhung on Sunday, 19-Jul-2026
|
* Created by SonPhung on Sunday, 19-Jul-2026
|
||||||
**/
|
**/
|
||||||
@HttpExchange
|
@HttpExchange
|
||||||
public interface RewardClient {
|
public interface CampaignClient {
|
||||||
|
|
||||||
@GetExchange("/api/campaign/csr/list")
|
@GetExchange("/api/campaign/csr/list")
|
||||||
List<Campaign> getAll(CampaignCriteria criteria, Pageable pageable);
|
List<Campaign> getAll(CampaignCriteria criteria, Pageable pageable);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package dev.sonpx.loyalty.mcp.reward.client;
|
||||||
|
|
||||||
|
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.criteria.CampaignRuleCriteria;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
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
|
||||||
|
**/
|
||||||
|
@HttpExchange
|
||||||
|
public interface CampaignRuleClient {
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/list")
|
||||||
|
List<CampaignRule> getAll(CampaignRuleCriteria criteria, Pageable pageable);
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/count")
|
||||||
|
Long count(CampaignRuleCriteria criteria);
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/id/{id}")
|
||||||
|
CampaignRule findActiveById(@PathVariable String id);
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/id")
|
||||||
|
List<CampaignRule> findActiveByIds(@RequestParam Map<String, String> params);
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/id/generate")
|
||||||
|
String generateId();
|
||||||
|
|
||||||
|
@GetExchange("/api/campaign-rule/csr/id/check/{id}")
|
||||||
|
String checkId(@PathVariable String id);
|
||||||
|
|
||||||
|
@PostExchange("/api/campaign-rule/csr/create")
|
||||||
|
OperationResult<CampaignRule> create(@Valid @RequestBody CampaignRule dto);
|
||||||
|
}
|
||||||
@@ -27,8 +27,4 @@ public class CampaignCriteria extends AbstractFwCriteria {
|
|||||||
|
|
||||||
@JsonPropertyDescription("Bộ lọc theo Loại chiến dịch (ví dụ: POINT, VOUCHER, v.v.)")
|
@JsonPropertyDescription("Bộ lọc theo Loại chiến dịch (ví dụ: POINT, VOUCHER, v.v.)")
|
||||||
private StringFilter campaignType;
|
private StringFilter campaignType;
|
||||||
|
|
||||||
// for search text and component
|
|
||||||
private String[] componentColumns = {"campaign_id", "name"};
|
|
||||||
private String[] columns = {"description"};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,4 @@ public class CampaignRuleCriteria extends AbstractFwCriteria {
|
|||||||
private LocalDateTimeFilter effectiveTo;
|
private LocalDateTimeFilter effectiveTo;
|
||||||
private StringFilter ruleType;
|
private StringFilter ruleType;
|
||||||
private StringFilter poolId;
|
private StringFilter poolId;
|
||||||
|
|
||||||
// for search text and component
|
|
||||||
private String[] componentColumns = {"rule_id", "rule_name"};
|
|
||||||
private String[] columns = {"campaign_id", "description"};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,4 @@ public class ComponentCriteria extends AbstractFwCriteria {
|
|||||||
private StringFilter key;
|
private StringFilter key;
|
||||||
private StringFilter code;
|
private StringFilter code;
|
||||||
private StringFilter name;
|
private StringFilter name;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"code", "name"};
|
|
||||||
private String[] columns = {};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,4 @@ public class CounterDefinitionCriteria extends AbstractFwCriteria {
|
|||||||
private LocalDateFilter firstStartDate;
|
private LocalDateFilter firstStartDate;
|
||||||
private StringFilter stateCounter;
|
private StringFilter stateCounter;
|
||||||
private StringFilter lateValuePosting;
|
private StringFilter lateValuePosting;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"counter_id", "counter_name"};
|
|
||||||
private String[] columns = {"entity", "what_to_count"};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,4 @@ public class CurrencyRateCriteria extends AbstractFwCriteria {
|
|||||||
private StringFilter effectiveTo;
|
private StringFilter effectiveTo;
|
||||||
private DoubleFilter buyRate;
|
private DoubleFilter buyRate;
|
||||||
private DoubleFilter sellRate;
|
private DoubleFilter sellRate;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"pcr_code"};
|
|
||||||
private String[] columns = {};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,4 @@ public class DeductionSequencesCriteria extends AbstractFwCriteria {
|
|||||||
private LocalDateFilter effectiveFrom;
|
private LocalDateFilter effectiveFrom;
|
||||||
private LocalDateFilter effectiveTo;
|
private LocalDateFilter effectiveTo;
|
||||||
private StringFilter isDefault;
|
private StringFilter isDefault;
|
||||||
|
|
||||||
// for search and component
|
|
||||||
private String[] componentColumns = {"sequence_id", "sequence_name"};
|
|
||||||
private String[] columns = {"description", "effective_from", "effective_to", "is_default"};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,4 @@ public class EventMaintenanceCriteria extends AbstractFwCriteria {
|
|||||||
|
|
||||||
private StringFilter eventId;
|
private StringFilter eventId;
|
||||||
private StringFilter description;
|
private StringFilter description;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"event_id", "description"};
|
|
||||||
private String[] columns = {};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,4 @@ public class PoolConversionRateCriteria extends AbstractFwCriteria {
|
|||||||
|
|
||||||
private StringFilter code;
|
private StringFilter code;
|
||||||
private StringFilter description;
|
private StringFilter description;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"code", "description"};
|
|
||||||
private String[] columns = {};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,4 @@ public class PoolDefinitionCriteria extends AbstractFwCriteria {
|
|||||||
private StringFilter entityLevel;
|
private StringFilter entityLevel;
|
||||||
private StringFilter expiryPolicy;
|
private StringFilter expiryPolicy;
|
||||||
private StringFilter atgGroupId;
|
private StringFilter atgGroupId;
|
||||||
|
|
||||||
// for search and component
|
|
||||||
private String[] componentColumns = {"pool_id", "pool_name"};
|
|
||||||
private String[] columns = {"description"};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,4 @@ public class StatementOutputPoolCriteria extends AbstractFwCriteria {
|
|||||||
|
|
||||||
private StringFilter poolId;
|
private StringFilter poolId;
|
||||||
private StringFilter entityLevel;
|
private StringFilter entityLevel;
|
||||||
|
|
||||||
// for search text
|
|
||||||
private String[] componentColumns = {"pool_id"};
|
|
||||||
private String[] columns = {"entity_level"};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,4 @@ public class TransactionCategoryCriteria extends AbstractFwCriteria {
|
|||||||
private StringFilter code;
|
private StringFilter code;
|
||||||
private StringFilter name;
|
private StringFilter name;
|
||||||
private StringFilter description;
|
private StringFilter description;
|
||||||
|
|
||||||
// for search text and component
|
|
||||||
private String[] componentColumns = {"code", "name"};
|
|
||||||
private String[] columns = {};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,4 @@ public class TransactionCodeCriteria extends AbstractFwCriteria {
|
|||||||
private StringFilter code;
|
private StringFilter code;
|
||||||
private StringFilter description;
|
private StringFilter description;
|
||||||
private BooleanFilter reversalInd;
|
private BooleanFilter reversalInd;
|
||||||
|
|
||||||
// for search text and component
|
|
||||||
private String[] componentColumns = {"code", "description"};
|
|
||||||
private String[] columns = {"reversal_ind"};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package dev.sonpx.loyalty.mcp.reward.enums;
|
package dev.sonpx.loyalty.mcp.reward.enums;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||||
import com.fasterxml.jackson.annotation.JsonValue;
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -13,7 +14,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
@Getter
|
@Getter
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public enum BaseDateType {
|
public enum BaseDateType {
|
||||||
|
|
||||||
|
@JsonPropertyDescription("Ngày hệ thống ghi nhận giao dịch")
|
||||||
POST_DATE("PD"),
|
POST_DATE("PD"),
|
||||||
|
|
||||||
|
@JsonPropertyDescription("Ngày khách hàng thực hiện giao dịch")
|
||||||
TRANSACTION_DATE("TD");
|
TRANSACTION_DATE("TD");
|
||||||
|
|
||||||
@JsonValue
|
@JsonValue
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
package dev.sonpx.loyalty.mcp.reward.model;
|
package dev.sonpx.loyalty.mcp.reward.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -13,5 +14,6 @@ import lombok.Setter;
|
|||||||
public class CampaignCriteria extends Fw {
|
public class CampaignCriteria extends Fw {
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
|
@JsonPropertyDescription("Cấu hình các tiêu chí có định dạng json")
|
||||||
private String criteria;
|
private String criteria;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class CampaignRule extends Fw {
|
|||||||
private Boolean notUpdatePool;
|
private Boolean notUpdatePool;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@JsonPropertyDescription("Danh sách liên kết điều khoản (T&C)")
|
@JsonPropertyDescription("Danh sách liên kết Transaction Code")
|
||||||
private List<CampaignTcLinkage> campaignTcLinkages;
|
private List<CampaignTcLinkage> campaignTcLinkages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,11 +107,9 @@ public class CampaignRule extends Fw {
|
|||||||
@JsonPropertyDescription("Mã sản phẩm/quà tặng")
|
@JsonPropertyDescription("Mã sản phẩm/quà tặng")
|
||||||
private ReferenceData itemCode;
|
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")
|
@JsonPropertyDescription("Chỉ áp dụng tiêu chí đầu tiên thỏa mãn trong Formula 10")
|
||||||
private Boolean attrApplyFirstFlag;
|
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")
|
@JsonPropertyDescription("Chỉ áp dụng tiêu chí đầu tiên thỏa mãn trong Formula 11")
|
||||||
private Boolean segApplyFirstFlag;
|
private Boolean segApplyFirstFlag;
|
||||||
|
|
||||||
@@ -128,7 +126,7 @@ public class CampaignRule extends Fw {
|
|||||||
private CampaignFormulaOne campaignFormulaOne;
|
private CampaignFormulaOne campaignFormulaOne;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
@JsonPropertyDescription("Cấu hình Formula 2")
|
@JsonPropertyDescription("Cấu hình Formula 2, Thưởng điểm theo giá trị cố định")
|
||||||
private CampaignFormulaTwo campaignFormulaTwo;
|
private CampaignFormulaTwo campaignFormulaTwo;
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package dev.sonpx.loyalty.mcp.service;
|
||||||
|
|
||||||
|
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
||||||
|
import dev.sonpx.loyalty.mcp.model.Result;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.client.CampaignRuleClient;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.criteria.CampaignRuleCriteria;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||||
|
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 CampaignRuleService {
|
||||||
|
|
||||||
|
private final CampaignRuleClient campaignRuleClient;
|
||||||
|
|
||||||
|
public Result<List<CampaignRule>> getAll(CampaignRuleCriteria criteria) {
|
||||||
|
try {
|
||||||
|
List<CampaignRule> rules = campaignRuleClient.getAll(criteria, Pageable.ofSize(10));
|
||||||
|
return Result.of(rules, MarkdownGenerator.generateCampaignRuleMD(rules));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<Long> count(CampaignRuleCriteria criteria) {
|
||||||
|
try {
|
||||||
|
Long count = campaignRuleClient.count(criteria);
|
||||||
|
return Result.of(count, MarkdownGenerator.generateCampaignRuleMD(count));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<CampaignRule> findActiveById(String id) {
|
||||||
|
try {
|
||||||
|
CampaignRule rule = campaignRuleClient.findActiveById(id);
|
||||||
|
return Result.of(rule, MarkdownGenerator.generateCampaignRuleMD(rule));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<List<CampaignRule>> findActiveByIds(String ids) {
|
||||||
|
try {
|
||||||
|
List<CampaignRule> rules = campaignRuleClient.findActiveByIds(java.util.Map.of("id", ids));
|
||||||
|
return Result.of(rules, MarkdownGenerator.generateCampaignRuleMD(rules));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<String> generateId() {
|
||||||
|
try {
|
||||||
|
String id = campaignRuleClient.generateId();
|
||||||
|
return Result.of(id, MarkdownGenerator.generateCampaignRuleIdMD(id));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<String> checkId(String id) {
|
||||||
|
try {
|
||||||
|
String checkResult = campaignRuleClient.checkId(id);
|
||||||
|
return Result.of(checkResult, MarkdownGenerator.generateCheckResultMD(checkResult));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<OperationResult<CampaignRule>> create(CampaignRule dto) {
|
||||||
|
try {
|
||||||
|
OperationResult<CampaignRule> opResult = campaignRuleClient.create(dto);
|
||||||
|
return Result.of(opResult, MarkdownGenerator.generateCampaignRuleOperationResultMD(opResult));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Result.of(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import static dev.sonpx.loyalty.mcp.util.MarkdownGenerator.generateCampaignMD;
|
|||||||
|
|
||||||
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
||||||
import dev.sonpx.loyalty.mcp.model.Result;
|
import dev.sonpx.loyalty.mcp.model.Result;
|
||||||
import dev.sonpx.loyalty.mcp.reward.client.RewardClient;
|
import dev.sonpx.loyalty.mcp.reward.client.CampaignClient;
|
||||||
import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria;
|
import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria;
|
||||||
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
||||||
import dev.sonpx.loyalty.mcp.util.MarkdownGenerator;
|
import dev.sonpx.loyalty.mcp.util.MarkdownGenerator;
|
||||||
@@ -22,11 +22,11 @@ import org.springframework.stereotype.Service;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CampaignService {
|
public class CampaignService {
|
||||||
|
|
||||||
private final RewardClient rewardClient;
|
private final CampaignClient campaignClient;
|
||||||
|
|
||||||
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
|
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
|
||||||
try {
|
try {
|
||||||
List<Campaign> campaigns = rewardClient.getAll(criteria, Pageable.ofSize(10));
|
List<Campaign> campaigns = campaignClient.getAll(criteria, Pageable.ofSize(10));
|
||||||
|
|
||||||
return Result.of(campaigns, generateCampaignMD(campaigns));
|
return Result.of(campaigns, generateCampaignMD(campaigns));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -37,7 +37,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<Long> count(CampaignCriteria criteria) {
|
public Result<Long> count(CampaignCriteria criteria) {
|
||||||
try {
|
try {
|
||||||
Long count = rewardClient.count(criteria);
|
Long count = campaignClient.count(criteria);
|
||||||
return Result.of(count, MarkdownGenerator.generateCampaignMD(count));
|
return Result.of(count, MarkdownGenerator.generateCampaignMD(count));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
@@ -47,7 +47,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<Campaign> findActiveById(String id) {
|
public Result<Campaign> findActiveById(String id) {
|
||||||
try {
|
try {
|
||||||
Campaign campaign = rewardClient.findActiveById(id);
|
Campaign campaign = campaignClient.findActiveById(id);
|
||||||
return Result.of(campaign, MarkdownGenerator.generateCampaignMD(campaign));
|
return Result.of(campaign, MarkdownGenerator.generateCampaignMD(campaign));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
@@ -57,7 +57,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<List<Campaign>> findActiveByIds(String ids) {
|
public Result<List<Campaign>> findActiveByIds(String ids) {
|
||||||
try {
|
try {
|
||||||
List<Campaign> campaigns = rewardClient.findActiveByIds(java.util.Map.of("id", ids));
|
List<Campaign> campaigns = campaignClient.findActiveByIds(java.util.Map.of("id", ids));
|
||||||
return Result.of(campaigns, MarkdownGenerator.generateCampaignMD(campaigns));
|
return Result.of(campaigns, MarkdownGenerator.generateCampaignMD(campaigns));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
@@ -67,7 +67,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<String> generateId() {
|
public Result<String> generateId() {
|
||||||
try {
|
try {
|
||||||
String id = rewardClient.generateId();
|
String id = campaignClient.generateId();
|
||||||
return Result.of(id, MarkdownGenerator.generateCampaignIdMD(id));
|
return Result.of(id, MarkdownGenerator.generateCampaignIdMD(id));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
@@ -77,7 +77,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<String> checkId(String id) {
|
public Result<String> checkId(String id) {
|
||||||
try {
|
try {
|
||||||
String checkResult = rewardClient.checkId(id);
|
String checkResult = campaignClient.checkId(id);
|
||||||
return Result.of(checkResult, MarkdownGenerator.generateCheckResultMD(checkResult));
|
return Result.of(checkResult, MarkdownGenerator.generateCheckResultMD(checkResult));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
@@ -87,7 +87,7 @@ public class CampaignService {
|
|||||||
|
|
||||||
public Result<OperationResult<Campaign>> create(Campaign dto) {
|
public Result<OperationResult<Campaign>> create(Campaign dto) {
|
||||||
try {
|
try {
|
||||||
OperationResult<Campaign> opResult = rewardClient.create(dto);
|
OperationResult<Campaign> opResult = campaignClient.create(dto);
|
||||||
return Result.of(opResult, MarkdownGenerator.generateOperationResultMD(opResult));
|
return Result.of(opResult, MarkdownGenerator.generateOperationResultMD(opResult));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
|
|||||||
@@ -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.CampaignRuleCriteria;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||||
|
import dev.sonpx.loyalty.mcp.service.CampaignRuleService;
|
||||||
|
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 CampaignRuleTools {
|
||||||
|
|
||||||
|
private final CampaignRuleService campaignRuleService;
|
||||||
|
|
||||||
|
@McpTool(description = "Lấy danh sách các Campaign Rule, cho phép tìm kiếm với param search, hiển thị tối đa 10 bản ghi")
|
||||||
|
public Result<List<CampaignRule>> campaignRules(String search) {
|
||||||
|
CampaignRuleCriteria criteria = new CampaignRuleCriteria();
|
||||||
|
criteria.setSearch(search);
|
||||||
|
return campaignRuleService.getAll(criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Đếm số lượng Campaign Rule, cho phép tìm kiếm với param search")
|
||||||
|
public Result<Long> countCampaignRules(String search) {
|
||||||
|
CampaignRuleCriteria criteria = new CampaignRuleCriteria();
|
||||||
|
criteria.setSearch(search);
|
||||||
|
return campaignRuleService.count(criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Lấy thông tin chi tiết một Campaign Rule theo ID")
|
||||||
|
public Result<CampaignRule> getCampaignRuleById(String id) {
|
||||||
|
return campaignRuleService.findActiveById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Lấy thông tin nhiều Campaign Rule theo danh sách ID, phân tách bằng dấu phẩy")
|
||||||
|
public Result<List<CampaignRule>> getCampaignRulesByIds(String ids) {
|
||||||
|
return campaignRuleService.findActiveByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Tạo một ID Campaign Rule mới")
|
||||||
|
public Result<String> generateCampaignRuleId() {
|
||||||
|
return campaignRuleService.generateId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Kiểm tra trạng thái hoặc tính hợp lệ của một ID Campaign Rule")
|
||||||
|
public Result<String> checkCampaignRuleId(String id) {
|
||||||
|
return campaignRuleService.checkId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@McpTool(description = "Tạo một Campaign Rule mới")
|
||||||
|
public Result<OperationResult<CampaignRule>> createCampaignRule(CampaignRule dto) {
|
||||||
|
return campaignRuleService.create(dto);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ApiMcpTools {
|
public class CampaignTools {
|
||||||
|
|
||||||
private final CampaignService campaignService;
|
private final CampaignService campaignService;
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@ package dev.sonpx.loyalty.mcp.util;
|
|||||||
|
|
||||||
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
import dev.sonpx.loyalty.mcp.model.OperationResult;
|
||||||
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
||||||
|
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@@ -60,6 +61,65 @@ public class MarkdownGenerator {
|
|||||||
return "Campaign ID: " + id;
|
return "Campaign ID: " + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateCampaignRuleMD(CampaignRule rule) {
|
||||||
|
if (rule == null) {
|
||||||
|
return "No campaign rule data available.";
|
||||||
|
}
|
||||||
|
|
||||||
|
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("| **Name** | ").append(rule.getRuleName()).append(" |\n");
|
||||||
|
if (rule.getRuleType() != null) {
|
||||||
|
sb.append("| **Type** | ").append(rule.getRuleType()).append(" |\n");
|
||||||
|
}
|
||||||
|
if (rule.getCampaignId() != null) {
|
||||||
|
sb.append("| **Campaign ID** | ").append(rule.getCampaignId().getCode()).append(" |\n");
|
||||||
|
}
|
||||||
|
if (rule.getDescription() != null) {
|
||||||
|
sb.append("| **Description** | ").append(rule.getDescription()).append(" |\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateCampaignRuleMD(List<CampaignRule> rules) {
|
||||||
|
if (CollectionUtils.isEmpty(rules)) {
|
||||||
|
return "No campaign rules data available.";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("### 📜 Campaign Rule List\n\n");
|
||||||
|
sb.append("| Rule Id | Name | Type | Campaign Id | Description |\n");
|
||||||
|
sb.append("| :--- | :--- | :--- | :--- | :--- |\n");
|
||||||
|
|
||||||
|
rules.forEach((rule) -> {
|
||||||
|
sb.append("| ").append(rule.getRuleId());
|
||||||
|
sb.append(" | ").append(rule.getRuleName());
|
||||||
|
sb.append(" | ").append(rule.getRuleType() != null ? rule.getRuleType() : "");
|
||||||
|
sb.append(" | ").append(rule.getCampaignId() != null ? rule.getCampaignId().getCode() : "");
|
||||||
|
sb.append(" | ").append(rule.getDescription() != null ? rule.getDescription() : "").append(" |\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateCampaignRuleMD(Long count) {
|
||||||
|
if (count == null) {
|
||||||
|
return "Total campaign rules: 0";
|
||||||
|
}
|
||||||
|
return "Total campaign rules: " + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateCampaignRuleIdMD(String id) {
|
||||||
|
if (id == null) {
|
||||||
|
return "No campaign rule ID available.";
|
||||||
|
}
|
||||||
|
return "Campaign Rule ID: " + id;
|
||||||
|
}
|
||||||
|
|
||||||
public static String generateCheckResultMD(String result) {
|
public static String generateCheckResultMD(String result) {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return "No check result available.";
|
return "No check result available.";
|
||||||
@@ -85,4 +145,23 @@ public class MarkdownGenerator {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String generateCampaignRuleOperationResultMD(OperationResult<CampaignRule> result) {
|
||||||
|
if (result == null) {
|
||||||
|
return "No operation result available.";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if ("00".equals(result.getErrorCode()) || result.getErrorCode() == null) {
|
||||||
|
sb.append("✅ Operation successful.\n\n");
|
||||||
|
if (result.getEntity() != null) {
|
||||||
|
sb.append(generateCampaignRuleMD(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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user