feat: overhaul chat architecture with persistent JdbcChatMemory, reactive UI components, and improved conversation management
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package dev.sonpx.loyalty.mcp.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -11,7 +12,11 @@ import org.springframework.context.annotation.Configuration;
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.findAndRegisterModules();
|
||||
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY);
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.sonpx.loyalty.mcp.reward.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import dev.sonpx.loyalty.mcp.reward.enums.RuleType;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.ReferenceData;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CampaignRuleSummaryDto implements Serializable {
|
||||
|
||||
@JsonPropertyDescription("Mã định danh của rule")
|
||||
private String ruleId;
|
||||
|
||||
@JsonPropertyDescription("Tên của rule")
|
||||
private String ruleName;
|
||||
|
||||
@JsonPropertyDescription("Loại rule: RED, REP, AWD, ADJ, MAWD, CEP")
|
||||
private RuleType ruleType;
|
||||
|
||||
@JsonPropertyDescription("Tham chiếu đến Campaign chứa rule này")
|
||||
private ReferenceData campaignId;
|
||||
|
||||
@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("Tham chiếu đến Pool chính")
|
||||
private ReferenceData poolId;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sonpx.loyalty.mcp.reward.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||
import dev.sonpx.loyalty.mcp.reward.enums.CampaignType;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CampaignSummaryDto implements Serializable {
|
||||
|
||||
@JsonPropertyDescription("Mã chiến dịch")
|
||||
private String campaignId;
|
||||
|
||||
@JsonPropertyDescription("Tên hiển thị của chiến dịch")
|
||||
private String name;
|
||||
|
||||
@JsonPropertyDescription("Tên người hoặc phòng ban sở hữu chiến dịch")
|
||||
private String ownerName;
|
||||
|
||||
@JsonPropertyDescription("Loại chiến dịch (POINT, VOUCHER...)")
|
||||
private CampaignType campaignType;
|
||||
|
||||
@JsonPropertyDescription("Ngày bắt đầu có hiệu lực")
|
||||
private LocalDate effectiveFrom;
|
||||
|
||||
@JsonPropertyDescription("Ngày kết thúc hiệu lực")
|
||||
private LocalDate effectiveTo;
|
||||
|
||||
@JsonPropertyDescription("Số lượng quy tắc (rules) trong chiến dịch")
|
||||
private Integer numOfRule;
|
||||
|
||||
@JsonPropertyDescription("Mô tả chi tiết")
|
||||
private String description;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
package dev.sonpx.loyalty.mcp.reward.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
@@ -15,27 +16,35 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public abstract class Fw implements Serializable {
|
||||
|
||||
@JsonIgnore
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING)
|
||||
protected Long recordNo;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String status;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String lastUpdateBy;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected Instant lastUpdateDate;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String lastApproveBy;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected Instant lastApproveDate;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String lastUpdateByName;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String lastApproveByName;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ 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.dto.CampaignRuleSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.CampaignFormulaOne;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -22,9 +24,12 @@ public class CampaignRuleService {
|
||||
|
||||
private final CampaignRuleClient campaignRuleClient;
|
||||
|
||||
public Result<List<CampaignRule>> getAll(CampaignRuleCriteria criteria) {
|
||||
public Result<List<CampaignRuleSummaryDto>> getAll(CampaignRuleCriteria criteria) {
|
||||
List<CampaignRule> rules = campaignRuleClient.getAll(criteria, Pageable.ofSize(10));
|
||||
return Result.of(rules, """
|
||||
List<CampaignRuleSummaryDto> summaries = rules != null
|
||||
? rules.stream().map(this::toSummaryDto).toList()
|
||||
: List.of();
|
||||
return Result.of(summaries, """
|
||||
Trả lời tự nhiên bằng tiếng Việt. Không dùng markdown table.
|
||||
Hiển thị mỗi rule theo dạng danh sách đánh số:
|
||||
**[ruleName]** (`[ruleId]`) — Chiến dịch: [campaignId.description hoặc campaignId.code], Loại: [ruleType - dùng tên tiếng Việt], Hiệu lực: [effectiveFrom] → [effectiveTo]
|
||||
@@ -34,6 +39,21 @@ public class CampaignRuleService {
|
||||
""");
|
||||
}
|
||||
|
||||
public CampaignRuleSummaryDto toSummaryDto(CampaignRule rule) {
|
||||
if (rule == null) {
|
||||
return null;
|
||||
}
|
||||
return CampaignRuleSummaryDto.builder()
|
||||
.ruleId(rule.getRuleId())
|
||||
.ruleName(rule.getRuleName())
|
||||
.ruleType(rule.getRuleType())
|
||||
.campaignId(rule.getCampaignId())
|
||||
.effectiveFrom(rule.getEffectiveFrom())
|
||||
.effectiveTo(rule.getEffectiveTo())
|
||||
.poolId(rule.getPoolId())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Result<Long> count(CampaignRuleCriteria criteria) {
|
||||
Long count = campaignRuleClient.count(criteria);
|
||||
return Result.of(count, """
|
||||
@@ -44,6 +64,7 @@ public class CampaignRuleService {
|
||||
|
||||
public Result<CampaignRule> findActiveById(String id) {
|
||||
CampaignRule rule = campaignRuleClient.findActiveById(id);
|
||||
sanitizeFormulas(rule);
|
||||
return Result.of(rule, """
|
||||
Trả lời tự nhiên bằng tiếng Việt. Trình bày chi tiết rule một cách rõ ràng:
|
||||
- Thông tin cơ bản: Tên, Mã Rule, Loại rule (tên tiếng Việt), Chiến dịch liên kết
|
||||
@@ -60,6 +81,9 @@ public class CampaignRuleService {
|
||||
public Result<List<CampaignRule>> findActiveByIds(String ids) {
|
||||
List<String> idList = java.util.Arrays.asList(ids.split(",\\s*"));
|
||||
List<CampaignRule> rules = campaignRuleClient.findActiveByIds(idList);
|
||||
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 rule theo dạng đánh số,
|
||||
mỗi rule ghi: Tên, Mã Rule, Chiến dịch, Loại rule, Trạng thái.
|
||||
@@ -67,6 +91,52 @@ public class CampaignRuleService {
|
||||
""");
|
||||
}
|
||||
|
||||
public void sanitizeFormulas(CampaignRule rule) {
|
||||
if (rule == null) {
|
||||
return;
|
||||
}
|
||||
if (rule.getCampaignFormulaOne() != null && isFormulaOneEmpty(rule.getCampaignFormulaOne())) {
|
||||
rule.setCampaignFormulaOne(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaTwo() != null && rule.getCampaignFormulaTwo().getFixedAmt() == null) {
|
||||
rule.setCampaignFormulaTwo(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaFour() != null && (rule.getCampaignFormulaFour().getTiers() == null || rule.getCampaignFormulaFour().getTiers().isEmpty())) {
|
||||
rule.setCampaignFormulaFour(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaSix() != null && (rule.getCampaignFormulaSix().getTiers() == null || rule.getCampaignFormulaSix().getTiers().isEmpty())) {
|
||||
rule.setCampaignFormulaSix(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaFives() != null && rule.getCampaignFormulaFives().isEmpty()) {
|
||||
rule.setCampaignFormulaFives(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaEights() != null && rule.getCampaignFormulaEights().isEmpty()) {
|
||||
rule.setCampaignFormulaEights(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaTens() != null && rule.getCampaignFormulaTens().isEmpty()) {
|
||||
rule.setCampaignFormulaTens(null);
|
||||
}
|
||||
if (rule.getCampaignFormulaElevens() != null && rule.getCampaignFormulaElevens().isEmpty()) {
|
||||
rule.setCampaignFormulaElevens(null);
|
||||
}
|
||||
if (rule.getCampaignAwardLimits() != null && rule.getCampaignAwardLimits().isEmpty()) {
|
||||
rule.setCampaignAwardLimits(null);
|
||||
}
|
||||
if (rule.getCampaignTcLinkages() != null && rule.getCampaignTcLinkages().isEmpty()) {
|
||||
rule.setCampaignTcLinkages(null);
|
||||
}
|
||||
if (rule.getCampaignContributors() != null && rule.getCampaignContributors().isEmpty()) {
|
||||
rule.setCampaignContributors(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFormulaOneEmpty(CampaignFormulaOne f) {
|
||||
return f.getAwardPerTxnAmt() == null
|
||||
&& f.getTxnAmt() == null
|
||||
&& f.getAwardPerTxnAmtAfter() == null
|
||||
&& f.getTxnAmtAfter() == null;
|
||||
}
|
||||
|
||||
public Result<String> generateId() {
|
||||
String id = campaignRuleClient.generateId();
|
||||
return Result.of(id, """
|
||||
@@ -93,3 +163,4 @@ public class CampaignRuleService {
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import dev.sonpx.loyalty.mcp.model.OperationResult;
|
||||
import dev.sonpx.loyalty.mcp.model.Result;
|
||||
import dev.sonpx.loyalty.mcp.reward.client.CampaignClient;
|
||||
import dev.sonpx.loyalty.mcp.reward.criteria.CampaignCriteria;
|
||||
import dev.sonpx.loyalty.mcp.reward.dto.CampaignSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -21,9 +22,12 @@ public class CampaignService {
|
||||
|
||||
private final CampaignClient campaignClient;
|
||||
|
||||
public Result<List<Campaign>> getAll(CampaignCriteria criteria) {
|
||||
public Result<List<CampaignSummaryDto>> getAll(CampaignCriteria criteria) {
|
||||
List<Campaign> campaigns = campaignClient.getAll(criteria, Pageable.ofSize(5));
|
||||
return Result.of(campaigns, """
|
||||
List<CampaignSummaryDto> summaries = campaigns != null
|
||||
? campaigns.stream().map(this::toSummaryDto).toList()
|
||||
: List.of();
|
||||
return Result.of(summaries, """
|
||||
Trả lời tự nhiên bằng tiếng Việt. Không dùng markdown table.
|
||||
Hiển thị mỗi chiến dịch theo dạng danh sách đánh số:
|
||||
**[Tên]** (`[campaignId]`) — Owner: [ownerName hoặc "Chưa có"], Loại: [campaignType], Hiệu lực: [effectiveFrom] → [effectiveTo], Số rule: [numOfRule]
|
||||
@@ -33,6 +37,22 @@ public class CampaignService {
|
||||
""");
|
||||
}
|
||||
|
||||
public CampaignSummaryDto toSummaryDto(Campaign campaign) {
|
||||
if (campaign == null) {
|
||||
return null;
|
||||
}
|
||||
return CampaignSummaryDto.builder()
|
||||
.campaignId(campaign.getCampaignId())
|
||||
.name(campaign.getName())
|
||||
.ownerName(campaign.getOwnerName())
|
||||
.campaignType(campaign.getCampaignType())
|
||||
.effectiveFrom(campaign.getEffectiveFrom())
|
||||
.effectiveTo(campaign.getEffectiveTo())
|
||||
.numOfRule(campaign.getNumOfRule())
|
||||
.description(campaign.getDescription())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Result<Long> count(CampaignCriteria criteria) {
|
||||
Long count = campaignClient.count(criteria);
|
||||
return Result.of(count, """
|
||||
@@ -91,3 +111,4 @@ public class CampaignService {
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.dto.CampaignRuleSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||
import dev.sonpx.loyalty.mcp.service.CampaignRuleService;
|
||||
import dev.sonpx.loyalty.mcp.util.SearchUtils;
|
||||
@@ -26,12 +27,13 @@ public class CampaignRuleTools {
|
||||
- QUAN TRỌNG: Khi người dùng hỏi lấy danh sách chung ("danh sách rule", "xem thể lệ"), KHÔNG truyền param `search` (để null hoặc rỗng ""). Tuyệt đối KHÔNG truyền các từ chung như "rule", "thể lệ", "danh sách" vào `search`.
|
||||
- KHÔNG dùng tool này khi người dùng chỉ hỏi về thông tin cơ bản chiến dịch (tên, owner, loại) → hãy dùng tool searchCampaigns.
|
||||
Hiển thị tối đa 10 bản ghi.""")
|
||||
public Result<List<CampaignRule>> searchRules(String search) {
|
||||
public Result<List<CampaignRuleSummaryDto>> searchRules(String search) {
|
||||
CampaignRuleCriteria criteria = new CampaignRuleCriteria();
|
||||
criteria.setSearch(SearchUtils.sanitizeSearch(search));
|
||||
return campaignRuleService.getAll(criteria);
|
||||
}
|
||||
|
||||
|
||||
@McpTool(description = "Đếm số lượng RULE/THỂ LỆ, không phải chiến dịch. Để search = null nếu đếm tất cả.")
|
||||
public Result<Long> countRules(String search) {
|
||||
CampaignRuleCriteria criteria = new CampaignRuleCriteria();
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.dto.CampaignSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
||||
import dev.sonpx.loyalty.mcp.service.CampaignService;
|
||||
import dev.sonpx.loyalty.mcp.util.SearchUtils;
|
||||
@@ -25,12 +26,13 @@ public class CampaignTools {
|
||||
- Param `search` (String): từ khóa tìm kiếm theo tên hoặc mã chiến dịch cụ thể.
|
||||
- QUAN TRỌNG: Khi người dùng hỏi lấy danh sách chung ("danh sách chiến dịch", "xem các chiến dịch"), KHÔNG truyền param `search` (để null hoặc rỗng ""). Tuyệt đối KHÔNG truyền các từ chung như "chiến dịch", "danh sách", "campaign" vào `search`.
|
||||
- KHÔNG dùng tool này khi người dùng hỏi về: rule, thể lệ, quy tắc, điều kiện, công thức thưởng → hãy dùng tool searchRules.""")
|
||||
public Result<List<Campaign>> searchCampaigns(String search) {
|
||||
public Result<List<CampaignSummaryDto>> searchCampaigns(String search) {
|
||||
CampaignCriteria criteria = new CampaignCriteria();
|
||||
criteria.setSearch(SearchUtils.sanitizeSearch(search));
|
||||
return campaignService.getAll(criteria);
|
||||
}
|
||||
|
||||
|
||||
@McpTool(description = "Đếm số lượng CHIẾN DỊCH (Campaign), không phải rule. Để search = null nếu đếm tất cả.")
|
||||
public Result<Long> countCampaigns(String search) {
|
||||
CampaignCriteria criteria = new CampaignCriteria();
|
||||
|
||||
@@ -2,7 +2,7 @@ package dev.sonpx.loyalty.mcp.web.rest;
|
||||
|
||||
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.reward.dto.CampaignSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.service.CampaignService;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -22,8 +22,9 @@ public class RewardResource {
|
||||
private final CampaignService campaignService;
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Result<List<Campaign>>> getAll(CampaignCriteria criteria) {
|
||||
public ResponseEntity<Result<List<CampaignSummaryDto>>> getAll(CampaignCriteria criteria) {
|
||||
return ResponseEntity.ok(campaignService.getAll(criteria));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ spring:
|
||||
server:
|
||||
sse-endpoint: /sse
|
||||
sse-message-endpoint: /message
|
||||
keep-alive-interval: 30s
|
||||
security:
|
||||
oauth2:
|
||||
client:
|
||||
@@ -35,4 +36,6 @@ logging:
|
||||
level:
|
||||
root: warn
|
||||
org.springframework.ai: INFO
|
||||
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR
|
||||
org.springframework.security.oauth2.client.registration.ClientRegistration: ERROR
|
||||
dev.sonpx.loyalty: DEBUG
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package dev.sonpx.loyalty.mcp.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.sonpx.loyalty.mcp.reward.dto.CampaignRuleSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.dto.CampaignSummaryDto;
|
||||
import dev.sonpx.loyalty.mcp.reward.enums.CampaignType;
|
||||
import dev.sonpx.loyalty.mcp.reward.enums.RuleType;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.Campaign;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.CampaignRule;
|
||||
import dev.sonpx.loyalty.mcp.reward.model.ReferenceData;
|
||||
import dev.sonpx.loyalty.mcp.service.CampaignRuleService;
|
||||
import dev.sonpx.loyalty.mcp.service.CampaignService;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class McpResponseSerializationTest {
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
private CampaignService campaignService;
|
||||
private CampaignRuleService campaignRuleService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
AppConfig config = new AppConfig();
|
||||
objectMapper = config.objectMapper();
|
||||
campaignService = new CampaignService(null);
|
||||
campaignRuleService = new CampaignRuleService(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFwAuditFieldsIgnoredAndNullsOmittedInCampaign() throws Exception {
|
||||
Campaign campaign = new Campaign();
|
||||
campaign.setCampaignId("CMP01");
|
||||
campaign.setName("Happy Birthday");
|
||||
campaign.setOwnerName("Marketing");
|
||||
campaign.setCampaignType(CampaignType.BASE);
|
||||
campaign.setRecordNo(100L);
|
||||
campaign.setLastUpdateBy("admin");
|
||||
campaign.setLastUpdateByName("Admin User");
|
||||
campaign.setLastApproveBy("checker");
|
||||
campaign.setLastApproveByName("Checker User");
|
||||
|
||||
String json = objectMapper.writeValueAsString(campaign);
|
||||
|
||||
// Audit fields must be ignored
|
||||
assertThat(json).doesNotContain("recordNo");
|
||||
assertThat(json).doesNotContain("lastUpdateBy");
|
||||
assertThat(json).doesNotContain("lastUpdateByName");
|
||||
assertThat(json).doesNotContain("lastApproveBy");
|
||||
assertThat(json).doesNotContain("lastApproveByName");
|
||||
assertThat(json).doesNotContain("lastUpdateDate");
|
||||
assertThat(json).doesNotContain("lastApproveDate");
|
||||
|
||||
// Null fields (effectiveFrom, effectiveTo, etc.) must be omitted
|
||||
assertThat(json).doesNotContain("effectiveFrom");
|
||||
assertThat(json).doesNotContain("effectiveTo");
|
||||
assertThat(json).doesNotContain("noOfCustomerTarget");
|
||||
|
||||
// Essential fields present
|
||||
assertThat(json).contains("\"campaignId\":\"CMP01\"");
|
||||
assertThat(json).contains("\"name\":\"Happy Birthday\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCampaignSummaryDtoMappingAndSerialization() throws Exception {
|
||||
Campaign campaign = new Campaign();
|
||||
campaign.setCampaignId("CMP02");
|
||||
campaign.setName("Summer Sale");
|
||||
campaign.setOwnerName("Sales");
|
||||
campaign.setCampaignType(CampaignType.TACTICAL);
|
||||
campaign.setEffectiveFrom(LocalDate.of(2026, 6, 1));
|
||||
campaign.setEffectiveTo(LocalDate.of(2026, 8, 31));
|
||||
campaign.setNumOfRule(3);
|
||||
|
||||
CampaignSummaryDto dto = campaignService.toSummaryDto(campaign);
|
||||
String json = objectMapper.writeValueAsString(dto);
|
||||
|
||||
assertThat(json).contains("\"campaignId\":\"CMP02\"");
|
||||
assertThat(json).contains("\"name\":\"Summer Sale\"");
|
||||
assertThat(json).contains("\"ownerName\":\"Sales\"");
|
||||
assertThat(json).contains("\"numOfRule\":3");
|
||||
assertThat(json).doesNotContain("description"); // Null field omitted
|
||||
assertThat(json).doesNotContain("targetAtv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCampaignRuleSummaryDtoMappingAndSerialization() throws Exception {
|
||||
CampaignRule rule = new CampaignRule();
|
||||
rule.setRuleId("R01");
|
||||
rule.setRuleName("Bonus Point Rule");
|
||||
rule.setRuleType(RuleType.AWARD);
|
||||
rule.setCampaignId(new ReferenceData("CMP01", "Happy Birthday Campaign"));
|
||||
rule.setPoolId(new ReferenceData("POOL01", "Main Point Pool"));
|
||||
rule.setEffectiveFrom(LocalDateTime.of(2026, 1, 1, 0, 0));
|
||||
|
||||
CampaignRuleSummaryDto dto = campaignRuleService.toSummaryDto(rule);
|
||||
String json = objectMapper.writeValueAsString(dto);
|
||||
|
||||
assertThat(json).contains("\"ruleId\":\"R01\"");
|
||||
assertThat(json).contains("\"ruleName\":\"Bonus Point Rule\"");
|
||||
assertThat(json).contains("\"ruleType\":\"AWD\"");
|
||||
assertThat(json).contains("\"code\":\"CMP01\"");
|
||||
assertThat(json).doesNotContain("effectiveTo"); // Null field omitted
|
||||
assertThat(json).doesNotContain("campaignFormulaOne");
|
||||
assertThat(json).doesNotContain("campaignFormulaSetting");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSanitizeFormulasInCampaignRule() throws Exception {
|
||||
CampaignRule rule = new CampaignRule();
|
||||
rule.setRuleId("R02");
|
||||
rule.setRuleName("Double Points");
|
||||
|
||||
// Formulas with all nulls
|
||||
rule.setCampaignFormulaOne(new dev.sonpx.loyalty.mcp.reward.model.CampaignFormulaOne());
|
||||
rule.setCampaignFormulaTwo(new dev.sonpx.loyalty.mcp.reward.model.CampaignFormulaTwo());
|
||||
|
||||
campaignRuleService.sanitizeFormulas(rule);
|
||||
|
||||
assertThat(rule.getCampaignFormulaOne()).isNull();
|
||||
assertThat(rule.getCampaignFormulaTwo()).isNull();
|
||||
|
||||
String json = objectMapper.writeValueAsString(rule);
|
||||
assertThat(json).doesNotContain("campaignFormulaOne");
|
||||
assertThat(json).doesNotContain("campaignFormulaTwo");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package dev.sonpx.loyalty.mcp.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.mcp.server.common.autoconfigure.properties.McpServerSseProperties;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class McpServerKeepAliveTest {
|
||||
|
||||
@Test
|
||||
void testKeepAlivePropertiesSetting() {
|
||||
McpServerSseProperties properties = new McpServerSseProperties();
|
||||
properties.setKeepAliveInterval(Duration.ofSeconds(30));
|
||||
|
||||
assertThat(properties.getKeepAliveInterval()).isEqualTo(Duration.ofSeconds(30));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user