forked from kimhyungsik/ax_hub_mcp_tool
fix: restore tool category metadata
This commit is contained in:
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
|
|||||||
public @interface ToolHint {
|
public @interface ToolHint {
|
||||||
boolean register() default false;
|
boolean register() default false;
|
||||||
boolean requiresApproval() default false;
|
boolean requiresApproval() default false;
|
||||||
String group() default "";
|
String categoryKey() default "com";
|
||||||
String mappingId() default "";
|
String mappingId() default "";
|
||||||
String inputSchemaResource() default "";
|
String inputSchemaResource() default "";
|
||||||
String outputSchemaResource() default "";
|
String outputSchemaResource() default "";
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ public class ToolRegistryHeartbeatSender {
|
|||||||
meta.setTimeoutMillis(5000L);
|
meta.setTimeoutMillis(5000L);
|
||||||
meta.setEnabled(true);
|
meta.setEnabled(true);
|
||||||
meta.setDescription(functionAnnotation.description());
|
meta.setDescription(functionAnnotation.description());
|
||||||
meta.setCategoryKey(hintAnnotation == null || hintAnnotation.group().isBlank()
|
meta.setCategoryKey(hintAnnotation == null || hintAnnotation.categoryKey().isBlank()
|
||||||
? "default" : hintAnnotation.group());
|
? "common" : hintAnnotation.categoryKey());
|
||||||
meta.setIntegrationType("REST");
|
meta.setIntegrationType("REST");
|
||||||
meta.setMciServiceId(hintAnnotation != null ? hintAnnotation.mappingId() : "");
|
meta.setMciServiceId(hintAnnotation != null ? hintAnnotation.mappingId() : "");
|
||||||
meta.setPodUrl(podUrl);
|
meta.setPodUrl(podUrl);
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ public final class ToolSourceUpdater {
|
|||||||
private ToolSourceUpdater() {
|
private ToolSourceUpdater() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateToolSource(String toolName, String domainGroup, String description,
|
public static void updateToolSource(String toolName, String categoryKey, String description,
|
||||||
boolean register, Boolean requiresApproval) throws IOException {
|
boolean register, Boolean requiresApproval) throws IOException {
|
||||||
String configuredSourceDirectory = System.getenv("AXHUB_SOURCE_DIR");
|
String configuredSourceDirectory = System.getenv("AXHUB_SOURCE_DIR");
|
||||||
Path rootDirectory = configuredSourceDirectory == null || configuredSourceDirectory.isBlank()
|
Path rootDirectory = configuredSourceDirectory == null || configuredSourceDirectory.isBlank()
|
||||||
? Paths.get(".") : Paths.get(configuredSourceDirectory);
|
? Paths.get(".") : Paths.get(configuredSourceDirectory);
|
||||||
updateToolSource(rootDirectory, toolName, domainGroup, description, register, requiresApproval);
|
updateToolSource(rootDirectory, toolName, categoryKey, description, register, requiresApproval);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateToolSource(Path rootDirectory, String toolName, String domainGroup, String description,
|
static void updateToolSource(Path rootDirectory, String toolName, String categoryKey, String description,
|
||||||
boolean register, Boolean requiresApproval) throws IOException {
|
boolean register, Boolean requiresApproval) throws IOException {
|
||||||
Path targetFile = findToolSource(rootDirectory, toolName);
|
Path targetFile = findToolSource(rootDirectory, toolName);
|
||||||
if (targetFile == null) {
|
if (targetFile == null) {
|
||||||
@@ -29,7 +29,7 @@ public final class ToolSourceUpdater {
|
|||||||
|
|
||||||
String content = Files.readString(targetFile);
|
String content = Files.readString(targetFile);
|
||||||
content = updateMcpTool(content, toolName, description);
|
content = updateMcpTool(content, toolName, description);
|
||||||
content = updateToolHint(content, domainGroup, register, requiresApproval);
|
content = updateToolHint(content, categoryKey, register, requiresApproval);
|
||||||
Files.writeString(targetFile, content);
|
Files.writeString(targetFile, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ public final class ToolSourceUpdater {
|
|||||||
return description == null ? content : replaceAttribute(content, range, "description", quote(description));
|
return description == null ? content : replaceAttribute(content, range, "description", quote(description));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String updateToolHint(String content, String domainGroup, boolean register, Boolean requiresApproval) {
|
private static String updateToolHint(String content, String categoryKey, boolean register, Boolean requiresApproval) {
|
||||||
AnnotationRange range = annotationArguments(content, "ToolHint", null);
|
AnnotationRange range = annotationArguments(content, "ToolHint", null);
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
throw new IllegalArgumentException("ToolHint declaration not found next to McpTool");
|
throw new IllegalArgumentException("ToolHint declaration not found next to McpTool");
|
||||||
@@ -72,8 +72,8 @@ public final class ToolSourceUpdater {
|
|||||||
updated = replaceAttribute(updated, range, "requiresApproval", Boolean.toString(requiresApproval));
|
updated = replaceAttribute(updated, range, "requiresApproval", Boolean.toString(requiresApproval));
|
||||||
range = annotationArguments(updated, "ToolHint", null);
|
range = annotationArguments(updated, "ToolHint", null);
|
||||||
}
|
}
|
||||||
if (domainGroup != null && !domainGroup.isBlank()) {
|
if (categoryKey != null && !categoryKey.isBlank()) {
|
||||||
updated = replaceAttribute(updated, range, "group", quote(domainGroup));
|
updated = replaceAttribute(updated, range, "categoryKey", quote(categoryKey));
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ class ToolSourceUpdaterTest {
|
|||||||
String updated = Files.readString(source);
|
String updated = Files.readString(source);
|
||||||
assertTrue(updated.contains("@McpTool(name = \"oth.cmm.sample.search\", description = \"new\")"));
|
assertTrue(updated.contains("@McpTool(name = \"oth.cmm.sample.search\", description = \"new\")"));
|
||||||
assertTrue(updated.contains("@ToolHint(register = true, requiresApproval = true"));
|
assertTrue(updated.contains("@ToolHint(register = true, requiresApproval = true"));
|
||||||
assertTrue(updated.contains("group = \"customer\""), updated);
|
assertTrue(updated.contains("categoryKey = \"customer\""), updated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.*;
|
|||||||
|
|
||||||
public interface BalanceUseCase {
|
public interface BalanceUseCase {
|
||||||
@McpTool(name = "oth.cmm.balance.inquiry", title = "잔고 조회 툴", description = "고객의 계좌 잔액을 조회합니다.")
|
@McpTool(name = "oth.cmm.balance.inquiry", title = "잔고 조회 툴", description = "고객의 계좌 잔액을 조회합니다.")
|
||||||
@ToolHint(register = false, mappingId = "ACC_001")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "ACC_001")
|
||||||
Object execute(BalanceRequest req);
|
Object execute(BalanceRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ public interface BillingProcessUseCase {
|
|||||||
Object getStatus(BillingStatusRequest req);
|
Object getStatus(BillingStatusRequest req);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.billing.process", title = "청구 프로세스 툴", description = "청구 처리")
|
@McpTool(name = "oth.cmm.billing.process", title = "청구 프로세스 툴", description = "청구 처리")
|
||||||
@ToolHint(register = false, mappingId = "BILL_002")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "BILL_002")
|
||||||
Object processBilling(BillingProcessRequest data);
|
Object processBilling(BillingProcessRequest data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ public interface BondIssueUseCase {
|
|||||||
Object check(BondCheckRequest req);
|
Object check(BondCheckRequest req);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.bond.issue", title = "채권 발행 툴", description = "증권 발행 테스트1")
|
@McpTool(name = "oth.cmm.bond.issue", title = "채권 발행 툴", description = "증권 발행 테스트1")
|
||||||
@ToolHint(register = false, mappingId = "BOND_002")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "BOND_002")
|
||||||
Object issue(BondIssueRequest data);
|
Object issue(BondIssueRequest data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchResponse;
|
|||||||
public interface ClaimSearchSchemaSampleUseCase {
|
public interface ClaimSearchSchemaSampleUseCase {
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.claim.search", title = "청구 조회 스키마 샘플", description = "Claim search Tool sample using input and output JSON Schema resources.", annotations = @McpTool.McpAnnotations(readOnlyHint = true, idempotentHint = true))
|
@McpTool(name = "oth.cmm.claim.search", title = "청구 조회 스키마 샘플", description = "Claim search Tool sample using input and output JSON Schema resources.", annotations = @McpTool.McpAnnotations(readOnlyHint = true, idempotentHint = true))
|
||||||
@ToolHint(register = false,
|
@ToolHint(register = false, categoryKey = "cmm",
|
||||||
inputSchemaResource = "classpath:mcp/schema/claim-search-resource-input-schema.json",
|
inputSchemaResource = "classpath:mcp/schema/claim-search-resource-input-schema.json",
|
||||||
outputSchemaResource = "classpath:mcp/schema/claim-search-resource-output-schema.json")
|
outputSchemaResource = "classpath:mcp/schema/claim-search-resource-output-schema.json")
|
||||||
ClaimSearchResponse search(ClaimSearchRequest request);
|
ClaimSearchResponse search(ClaimSearchRequest request);
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ public interface CommonUtilityUseCase {
|
|||||||
Object registerVacation(VacationRegisterRequest req);
|
Object registerVacation(VacationRegisterRequest req);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.leave.count", title = "공통 유틸리티 툴", description = "연차 갯수 조회")
|
@McpTool(name = "oth.cmm.leave.count", title = "공통 유틸리티 툴", description = "연차 갯수 조회")
|
||||||
@ToolHint(register = false, mappingId = "HR_VAC_02")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "HR_VAC_02")
|
||||||
Object getLeaveCount(LeaveCountRequest data);
|
Object getLeaveCount(LeaveCountRequest data);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.secret.execute", title = "공통 유틸리티 툴", description = "비공개 툴 테스트")
|
@McpTool(name = "oth.cmm.secret.execute", title = "공통 유틸리티 툴", description = "비공개 툴 테스트")
|
||||||
@ToolHint(register = false, mappingId = "SECRET_001")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "SECRET_001")
|
||||||
Object secretTool(LeaveCountRequest data);
|
Object secretTool(LeaveCountRequest data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ public interface ContractInquiryUseCase {
|
|||||||
Object getStatus(ContractStatusRequest req);
|
Object getStatus(ContractStatusRequest req);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.contract.detail", title = "계약 상세조회 툴", description = "계약상세 조회")
|
@McpTool(name = "oth.cmm.contract.detail", title = "계약 상세조회 툴", description = "계약상세 조회")
|
||||||
@ToolHint(register = false, mappingId = "CNTR_002")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "CNTR_002")
|
||||||
Object getDetail(ContractDetailRequest data);
|
Object getDetail(ContractDetailRequest data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ public interface CustomerInfoUseCase {
|
|||||||
Object getGrade(CustomerGradeRequest req);
|
Object getGrade(CustomerGradeRequest req);
|
||||||
|
|
||||||
@McpTool(name = "oth.cmm.customer.detail", title = "고객 상세조회 툴", description = "고객상세 정보 조회")
|
@McpTool(name = "oth.cmm.customer.detail", title = "고객 상세조회 툴", description = "고객상세 정보 조회")
|
||||||
@ToolHint(register = false, mappingId = "CRM_002")
|
@ToolHint(register = false, categoryKey = "cmm", mappingId = "CRM_002")
|
||||||
Object getDetail(CustomerDetailRequest data);
|
Object getDetail(CustomerDetailRequest data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.MetaCommonCodeRequest;
|
|||||||
*/
|
*/
|
||||||
public interface MetaCommonCodeUseCase {
|
public interface MetaCommonCodeUseCase {
|
||||||
@McpTool(name = "oth.cmm.common-code.lookup", title = "메타 공통코드 조회 툴", description = "메타 통합코드 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
@McpTool(name = "oth.cmm.common-code.lookup", title = "메타 공통코드 조회 툴", description = "메타 통합코드 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
||||||
@ToolHint(register = false, requiresApproval = false, mappingId = "CLCNNB00001")
|
@ToolHint(register = false, requiresApproval = false, categoryKey = "cmm", mappingId = "CLCNNB00001")
|
||||||
Object execute(MetaCommonCodeRequest req);
|
Object execute(MetaCommonCodeRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,6 @@ import io.shinhanlife.dap.mcc.biz.cmm.dto.MetaTableRequest;
|
|||||||
*/
|
*/
|
||||||
public interface MetaTableUseCase {
|
public interface MetaTableUseCase {
|
||||||
@McpTool(name = "oth.cmm.meta.table", title = "메타 테이블 조회 툴", description = "메타 테이블 정보 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
@McpTool(name = "oth.cmm.meta.table", title = "메타 테이블 조회 툴", description = "메타 테이블 정보 목록을 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
||||||
@ToolHint(register = false, requiresApproval = false, mappingId = "CLCNNB00001")
|
@ToolHint(register = false, requiresApproval = false, categoryKey = "cmm", mappingId = "CLCNNB00001")
|
||||||
Object execute(MetaTableRequest req);
|
Object execute(MetaTableRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
public interface TemplateUtilityUseCase {
|
public interface TemplateUtilityUseCase {
|
||||||
@McpTool(name = "oth.cmm.template.url", title = "템플릿 유틸리티 툴", description = "요청한 템플릿(엑셀, 워드 등) 파일(양식)을 다운로드 받을 수 있는 시스템 URL을 반환합니다. AI는 이 URL을 사용자에게 마크다운 링크 형태로 제공해야 합니다.")
|
@McpTool(name = "oth.cmm.template.url", title = "템플릿 유틸리티 툴", description = "요청한 템플릿(엑셀, 워드 등) 파일(양식)을 다운로드 받을 수 있는 시스템 URL을 반환합니다. AI는 이 URL을 사용자에게 마크다운 링크 형태로 제공해야 합니다.")
|
||||||
@ToolHint
|
@ToolHint(categoryKey = "cmm")
|
||||||
Map<String, Object> getTemplateFileUrl(TemplateDownloadRequest req);
|
Map<String, Object> getTemplateFileUrl(TemplateDownloadRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import io.shinhanlife.dap.mcc.biz.oth.dto.*;
|
|||||||
|
|
||||||
public interface Onnba3011UseCase {
|
public interface Onnba3011UseCase {
|
||||||
@McpTool(name = "oth.sol.request.detail", title = "보종By가입설계한도계산조회 툴")
|
@McpTool(name = "oth.sol.request.detail", title = "보종By가입설계한도계산조회 툴")
|
||||||
@ToolHint(register = false)
|
@ToolHint(register = false, categoryKey = "oth")
|
||||||
Object callOnnba3011(Onnba3011Request req);
|
Object callOnnba3011(Onnba3011Request req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.DailyQuoteResponse;
|
|||||||
|
|
||||||
public interface DailyQuoteToolUseCase {
|
public interface DailyQuoteToolUseCase {
|
||||||
@McpTool(name = "oth.smp.quote.daily", title = "오늘의 명언 툴", description = "무작위로 영감을 주는 명언을 하나 가져옵니다.")
|
@McpTool(name = "oth.smp.quote.daily", title = "오늘의 명언 툴", description = "무작위로 영감을 주는 명언을 하나 가져옵니다.")
|
||||||
@ToolHint(register = false, mappingId = "QUOTE_001")
|
@ToolHint(register = false, categoryKey = "smp", mappingId = "QUOTE_001")
|
||||||
DailyQuoteResponse execute(DailyQuoteRequest req);
|
DailyQuoteResponse execute(DailyQuoteRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.ExchangeRateResponse;
|
|||||||
|
|
||||||
public interface ExchangeRateToolUseCase {
|
public interface ExchangeRateToolUseCase {
|
||||||
@McpTool(name = "oth.smp.exchange-rate.inquiry", title = "실시간 환율 조회 툴", description = "원하는 통화의 실시간 환율을 조회합니다. (예: USD, EUR, JPY)")
|
@McpTool(name = "oth.smp.exchange-rate.inquiry", title = "실시간 환율 조회 툴", description = "원하는 통화의 실시간 환율을 조회합니다. (예: USD, EUR, JPY)")
|
||||||
@ToolHint(register = false, mappingId = "EXCHANGE_001")
|
@ToolHint(register = false, categoryKey = "smp", mappingId = "EXCHANGE_001")
|
||||||
ExchangeRateResponse execute(ExchangeRateRequest req);
|
ExchangeRateResponse execute(ExchangeRateRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.TeamMemberRequest;
|
|||||||
|
|
||||||
public interface TeamMemberUseCase {
|
public interface TeamMemberUseCase {
|
||||||
@McpTool(name = "oth.smp.team.list", title = "신한라이프 MCP, TOOL 파트 구성원 조회", description = "신한라이프 MCP, TOOL 파트 구성원을 조회합니다.", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
@McpTool(name = "oth.smp.team.list", title = "신한라이프 MCP, TOOL 파트 구성원 조회", description = "신한라이프 MCP, TOOL 파트 구성원을 조회합니다.", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
||||||
@ToolHint(register = false, requiresApproval = false, mappingId = "DIRECT0001")
|
@ToolHint(register = false, requiresApproval = false, categoryKey = "smp", mappingId = "DIRECT0001")
|
||||||
Object execute(TeamMemberRequest req);
|
Object execute(TeamMemberRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import io.shinhanlife.dap.mcc.biz.smp.dto.*;
|
|||||||
|
|
||||||
public interface WeatherToolUseCase {
|
public interface WeatherToolUseCase {
|
||||||
@McpTool(name = "oth.smp.weather.inquiry", title = "날씨 조회 툴", description = "특정 도시의 현재 날씨, 온도, 풍속 정보를 조회합니다.")
|
@McpTool(name = "oth.smp.weather.inquiry", title = "날씨 조회 툴", description = "특정 도시의 현재 날씨, 온도, 풍속 정보를 조회합니다.")
|
||||||
@ToolHint(register = false, mappingId = "WEATHER_001")
|
@ToolHint(register = false, categoryKey = "smp", mappingId = "WEATHER_001")
|
||||||
WeatherResponse execute(WeatherRequest req);
|
WeatherResponse execute(WeatherRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ import io.shinhanlife.dap.mcc.biz.sol.dto.SolReqDetailRequest;
|
|||||||
public interface SolReqDetailUseCase {
|
public interface SolReqDetailUseCase {
|
||||||
|
|
||||||
@McpTool(name = "oth.sol.request.detail", title = "SolReqDetail 툴", description = "SOL 의뢰서 상세 조회", annotations = @McpTool.McpAnnotations(openWorldHint = true, readOnlyHint = true))
|
@McpTool(name = "oth.sol.request.detail", title = "SolReqDetail 툴", description = "SOL 의뢰서 상세 조회", annotations = @McpTool.McpAnnotations(openWorldHint = true, readOnlyHint = true))
|
||||||
@ToolHint(register = false, requiresApproval = false, mappingId = "SOLG00000002")
|
@ToolHint(register = false, requiresApproval = false, categoryKey = "sol", mappingId = "SOLG00000002")
|
||||||
Object execute(SolReqDetailRequest req);
|
Object execute(SolReqDetailRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import io.shinhanlife.dap.mcc.biz.sol.dto.SolReqListRequest;
|
|||||||
|
|
||||||
public interface SolReqListUseCase {
|
public interface SolReqListUseCase {
|
||||||
@McpTool(name = "oth.sol.request.list", title = "SolReqList 툴", description = "SOL 의뢰서 목록 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
@McpTool(name = "oth.sol.request.list", title = "SolReqList 툴", description = "SOL 의뢰서 목록 조회해줘", annotations = @McpTool.McpAnnotations(openWorldHint = true))
|
||||||
@ToolHint(register = false, requiresApproval = false, mappingId = "SOLG00000001")
|
@ToolHint(register = false, requiresApproval = false, categoryKey = "sol", mappingId = "SOLG00000001")
|
||||||
Object execute(SolReqListRequest req);
|
Object execute(SolReqListRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import io.shinhanlife.dap.mcc.biz.sms.dto.*;
|
|||||||
|
|
||||||
public interface SmsToolUseCase {
|
public interface SmsToolUseCase {
|
||||||
@McpTool(name = "sms.send", title = "SMS 발송 툴", description = "SMS 발송 기능을 제공합니다.")
|
@McpTool(name = "sms.send", title = "SMS 발송 툴", description = "SMS 발송 기능을 제공합니다.")
|
||||||
@ToolHint(register = false, mappingId = "SMS_SEND")
|
@ToolHint(register = false, categoryKey = "notification", mappingId = "SMS_SEND")
|
||||||
Object sendSms(SmsSendRequest req);
|
Object sendSms(SmsSendRequest req);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user