From 876bdfee08aecf0e854f8b6fc5d00a338b52c711 Mon Sep 17 00:00:00 2001 From: herui Date: Fri, 27 Jun 2025 21:36:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 19 +++++ .idea/encodings.xml | 9 ++ .idea/jarRepositories.xml | 40 +++++++++ .idea/misc.xml | 12 +++ .idea/vcs.xml | 6 ++ .idea/workspace.xml | 77 ++++++++++++++++++ .../rzdata/mcp/server/crm/CrmMcpService.java | 34 ++++---- crm-mcp/target/classes/application-dev.yml | 20 +++++ crm-mcp/target/classes/application.yml | 43 ++++++++++ .../rzdata/mcp/server/crm/CrmMcpService.class | Bin 0 -> 9275 bytes .../mcp/server/crm/McpServerApplication.class | Bin 0 -> 1816 bytes .../mcp/server/crm/config/CrmProperties.class | Bin 0 -> 3633 bytes 12 files changed, 242 insertions(+), 18 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 crm-mcp/target/classes/application-dev.yml create mode 100644 crm-mcp/target/classes/application.yml create mode 100644 crm-mcp/target/classes/net/rzdata/mcp/server/crm/CrmMcpService.class create mode 100644 crm-mcp/target/classes/net/rzdata/mcp/server/crm/McpServerApplication.class create mode 100644 crm-mcp/target/classes/net/rzdata/mcp/server/crm/config/CrmProperties.class diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..325013c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..8b8aad9 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..ee01d94 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..67e1e61 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..af5cbac --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1751008087769 + + + + + + \ No newline at end of file diff --git a/crm-mcp/src/main/java/net/rzdata/mcp/server/crm/CrmMcpService.java b/crm-mcp/src/main/java/net/rzdata/mcp/server/crm/CrmMcpService.java index b3a5661..60a824c 100644 --- a/crm-mcp/src/main/java/net/rzdata/mcp/server/crm/CrmMcpService.java +++ b/crm-mcp/src/main/java/net/rzdata/mcp/server/crm/CrmMcpService.java @@ -31,7 +31,7 @@ public class CrmMcpService { } - @Tool(description = "根据客户名称或客户编码查询客户负责人。clientCode和clientName必须二选一。如果都提取不到则输出:请提供需要查询的客户。如果提取到多个值则输出:目前只能查询单个客户数据") + @Tool(description = "根据客户名称或客户编码查询客户负责人。并返回相关的信息") public String getClientResponsible( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'或'某某测试'等组织,例如:张三课题组,广州小懒科技有限公司,金域测试、金域课题组111、玉尘测试A01、睿展小组3号。与clientCode必须二选一", required = false) String clientName @@ -47,7 +47,7 @@ public class CrmMcpService { return executeRequest("ai/query/getClientResponsible", requestBody); } - @Tool(description = "根据订单编码(即订单号)查询订单负责人。orderCode是必填项,如果提取不到则输出:请告知需要查询的订单号") + @Tool(description = "根据订单编码(即订单号)查询订单负责人。并返回相关的信息") public String getOrderSaller( @ToolParam(description = "订单编码(即订单号),值一般是由字母、数字和'-'组成,特征一般类似于'NJ00027202506090001',或'NJ00027202506090001-G'或'NJ00027202506090001-T-1',或'dev-NJ00027202506090001'") String orderCode ) { @@ -59,12 +59,12 @@ public class CrmMcpService { return executeRequest("ai/query/getOrderSaller", requestBody); } - @Tool(description = "根据客户名称或客户编码查询某个时间段内的订单销售额。clientCode和clientName必须二选一。如果都提取不到则输出:请提供需要查询的客户。如果提取到多个值则输出:目前只能查询单个客户数据。对于'去年'、'今年'这种时间词,需要结合当前系统时间来判断") + @Tool(description = "根据客户名称或客户编码查询某个时间段内的订单销售额。并返回相关的信息") public String sumOrderTotalForClientInTime( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'或'某某测试'等组织,例如:张三课题组,广州小懒科技有限公司,金域测试、金域课题组111、玉尘测试A01、睿展小组3号。与clientCode必须二选一", required = false) String clientName, @ToolParam(description = "开始时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。非必填", required = false) String startTime, - @ToolParam(description = "结束时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。非必填", required = false) String endTime + @ToolParam(description = "结束时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-12-31 00:00:00。非必填", required = false) String endTime ) { JSONObject requestBody = new JSONObject(); @@ -76,13 +76,13 @@ public class CrmMcpService { return executeRequest("ai/query/sumOrderTotalForClientInTime", requestBody); } - @Tool(description = "根据客户名称或客户编码查询某个时间段内购买的某个产品货号的数量。clientCode和clientName必须二选一。如果都提取不到则输出:请提供需要查询的客户。productnum必填,如果提取不到则输出:请提供需要查询的货号。startTime和endTime必填,如果提取不到则输出:请提供需要查询的采购时间范围") + @Tool(description = "根据客户名称或客户编码查询某个时间段内购买的某个产品货号的数量。并返回相关的信息") public String sumProductNumForClientInTime( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'等组织,例如:张三课题组,广州小懒科技有限公司。与clientCode必须二选一", required = false) String clientName, - @ToolParam(description = "产品货号,一般类似于'XXXX-XX'格式,例如:C112-01。必填参数", required = false) String productnum, - @ToolParam(description = "采购开始时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。必填参数", required = false) String startTime, - @ToolParam(description = "采购结束时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。必填参数", required = false) String endTime + @ToolParam(description = "产品货号,一般类似于'XXXX-XX'格式,例如:C112-01。", required = false) String productnum, + @ToolParam(description = "开始时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。", required = false) String startTime, + @ToolParam(description = "结束时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-12-31 00:00:00。", required = false) String endTime ) { JSONObject requestBody = new JSONObject(); requestBody.put("clientCode", clientCode); @@ -94,7 +94,7 @@ public class CrmMcpService { return executeRequest("ai/query/sumProductNumForClientInTime", requestBody); } - @Tool(description = "根据订单编码(即订单号)查询订单购买的产品明细。orderCode是必填项,如果提取不到则输出:请告知需要查询的订单号") + @Tool(description = "根据订单编码(即订单号)查询订单购买的产品明细,订单编码(即订单号),值一般是由字母、数字和'-'组成,特征一般类似于'NJ00027202506090001',或'NJ00027202506090001-G'或'NJ00027202506090001-T-1',或'dev-NJ00027202506090001'。并返回相关的信息") public String detailOrderProduct( @ToolParam(description = "订单编码(即订单号),值一般是由字母、数字和'-'组成,特征一般类似于'NJ00027202506090001',或'NJ00027202506090001-G'或'NJ00027202506090001-T-1',或'dev-NJ00027202506090001'") String orderCode ) { @@ -106,7 +106,7 @@ public class CrmMcpService { return executeRequest("ai/query/detailOrderProduct", requestBody); } - @Tool(description = "根据客户名称或客户编码查询某个时间段内购买的产品主货号列表") + @Tool(description = "根据客户名称或客户编码查询某个时间段内购买的产品主货号列表。并返回相关的信息") public String listProductForClientInTime( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'或'某某测试'等组织,例如:张三课题组,广州小懒科技有限公司,金域测试、金域课题组111、玉尘测试A01、睿展小组3号。与clientCode必须二选一", required = false) String clientName, @@ -122,7 +122,7 @@ public class CrmMcpService { return executeRequest("ai/query/listProductForClientInTime", requestBody); } - @Tool(description = "查询客户在某个时间段内的欠款额") + @Tool(description = "查询客户在某个时间段内的欠款额。并返回相关的信息") public String getUnpaidAmountForClient( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'或'某某测试'等组织,例如:张三课题组,广州小懒科技有限公司,金域测试、金域课题组111、玉尘测试A01、睿展小组3号。与clientCode必须二选一", required = false) String clientName, @@ -138,7 +138,7 @@ public class CrmMcpService { return executeRequest("ai/query/getUnpaidAmountForClient", requestBody); } - @Tool(description = "根据主货号查询某个时间段内购买过该产品的客户名称列表") + @Tool(description = "根据主货号查询某个时间段内购买过该产品的客户名称列表。并返回相关的信息") public String listClientsByMainNumInTime( @ToolParam(description = "主货号,一般类似于'XXXX-XXXX'格式,例如:1234-5678", required = false) String mainNum, @ToolParam(description = "开始时间,一般类似于'yyyy-MM-dd HH:mm:ss'格式,例如:2024-01-01 00:00:00。非必填", required = false) String startTime, @@ -152,7 +152,7 @@ public class CrmMcpService { return executeRequest("ai/query/listClientsByMainNumInTime", requestBody); } - @Tool(description = "根据客户编码或客户名称查询时间区间内的订单列表") + @Tool(description = "根据客户编码或客户名称查询时间区间内的订单列表。并返回相关的信息") public String listOrdersByClientInTime( @ToolParam(description = "客户编码,一般类似于'XX.XX.XXXX.XXX'格式,例如:01.01.0023.024。与clientName必须二选一", required = false) String clientCode, @ToolParam(description = "客户名称,一般类似于'某某课题组'或'某某公司'或'某某实验室'或'某某经营部'或'某某研究院'或'某某研究所'或'某某测试'等组织,例如:张三课题组,广州小懒科技有限公司,金域测试、金域课题组111、玉尘测试A01、睿展小组3号。与clientCode必须二选一", required = false) String clientName, @@ -168,7 +168,7 @@ public class CrmMcpService { return executeRequest("ai/query/listOrdersByClientInTime", requestBody); } - @Tool(description = "根据订单编码查询订单信息。orderCode是必填项,如果提取不到则输出:请告知需要查询的订单号") + @Tool(description = "根据订单编码(即订单号)查询订单信息。并返回相关的信息") public JSONObject getOrderInfoByCode( @ToolParam(description = "订单编码(即订单号),值一般是由字母、数字和'-'组成,特征一般类似于'NJ00027202506090001',或'NJ00027202506090001-G'或'NJ00027202506090001-T-1',或'dev-NJ00027202506090001'") String orderCode ) { @@ -181,7 +181,7 @@ public class CrmMcpService { return JSONObject.parseObject(executed); } - @Tool(description = "根据合同编码查询下游订单编码列表。contractCode是必填项,如果提取不到则输出:请告知需要查询的合同编码") + @Tool(description = "根据合同编码查询下游订单编码列表。并返回相关的信息") public String listOrdersByContractCode( @ToolParam(description = "合同编码,值一般是由字母、数字和'-'组成的合同标识符") String contractCode ) { @@ -197,6 +197,4 @@ public class CrmMcpService { public String getCurrentTime() { return DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"); } - - -} +} \ No newline at end of file diff --git a/crm-mcp/target/classes/application-dev.yml b/crm-mcp/target/classes/application-dev.yml new file mode 100644 index 0000000..d9109d3 --- /dev/null +++ b/crm-mcp/target/classes/application-dev.yml @@ -0,0 +1,20 @@ +crm: + url: https://www.rzdata.net/bcrm-statis-jwt/ + tenantId: BIO + token: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJJUXdrczhuUWc4NnJPQkpuenJDUThtbjl1N3FEQ2lWZyJ9.91GO7OMhVbc0tLBRZ7HYiFDEA92PXE7H1gHduwxWu2I + +spring: + webflux: + base-path: /crm-mcp + ai: + mcp: + server: + name: crm-mcp-server + version: 0.0.1 + type: ASYNC + # 配置 URL 前缀 + base-url: /crm-mcp + # 配置 sse 的根路径,默认值为 /sse + sse-endpoint: /sse + sse-message-endpoint: /mcp + diff --git a/crm-mcp/target/classes/application.yml b/crm-mcp/target/classes/application.yml new file mode 100644 index 0000000..a93a38f --- /dev/null +++ b/crm-mcp/target/classes/application.yml @@ -0,0 +1,43 @@ +# +# Copyright 2025-2026 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# spring.main.web-application-type=none + +# NOTE: You must disable the banner and the console logging +# to allow the STDIO transport to work !!! +# Config reference: https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html#_webflux_server_configuration +spring: + main: + banner-mode: off + profiles: + active: dev + ai: + mcp: + server: + name: crm-mcp-server + version: 0.0.1 + type: ASYNC # Recommended for reactive applications + # 配置 sse 的根路径,包含前缀 /crm-mcp + # 下面的最终路径为 ip:port/crm-mcp/sse/mcp + sse-endpoint: /crm-mcp/sse + sse-message-endpoint: /mcp + capabilities: + tool: true + resource: true + prompt: true + completion: true + +# logging.pattern.console= diff --git a/crm-mcp/target/classes/net/rzdata/mcp/server/crm/CrmMcpService.class b/crm-mcp/target/classes/net/rzdata/mcp/server/crm/CrmMcpService.class new file mode 100644 index 0000000000000000000000000000000000000000..8a533b6594efd37e7b8dddae1cace9e8be8bc3c4 GIT binary patch literal 9275 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 F{{>wSRAB%B literal 0 HcmV?d00001 diff --git a/crm-mcp/target/classes/net/rzdata/mcp/server/crm/McpServerApplication.class b/crm-mcp/target/classes/net/rzdata/mcp/server/crm/McpServerApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..510c8244a12bb1d22583a3916ccd297d345e5cc6 GIT binary patch literal 1816 zcmbtVZEq7t5Pmj}bHSJbrld_NEwoS^Q?j(Blq!IfSRkRA7u6^gqW!QwZ_L8^)>`j{ z!mpxVD5^w%Kz~%#XU{giN*6%z!8<#1JI~I}JTw01-@pC_a0Nfsuz*U0#X7235;*iw z?JAk5bRci{9_rW&EL}FK@mB>Z&9&_a%L41E_R>A=E3f1*9?7wGyV}Xv4du;v)XlZc z(I_#o^2Vk$)UZE7y^aI;K%iyaK#oVwq=Ox&hWfE}k7UnUFT1(1;K@5(mJ0Nmoq1gC zM2*KA_Y0P;4`3T>oigOure6QFVO?H|~_yqSx;w6s-{^i=%lj1x{YX1+ zG#_!kvo}M@2l7C+iynkn{W(j$8R~5_Hi02&YJJXL!d)sQR!^&REO%7wt$QNXD+jrm zdHdKn-6!l~+xE4;g>a7xM!#g8)fBx z)tmLjHoq%3Ao7u7ai^cz^b;oEQK22Zc09Puj0R?N`&;uh*<>205t&%OfhT31GA-U5 zLtyQ#uM;>|TA5kP7izM_V)3q8*Jdu(*G(wFYFRGV!=MqkS_+&`DaG4r!6=UVKG_asO8&P3u&iPEV>aI#MhFrgqrrPBbFz`H@js~1W#5oQK zuAk!sf-;WOxVnrnS>0AJw@wS8z}ji-Y1 zMapn)xe>KqVBaru|L{iwdPnq{V=SYEv$TKBUzM|;f>pyhV+wHB8CP&?yuiW5msq%0 zIoo}Xqt9{d8Lh>@S~=*-Ic}f%^A!xOg8T R#tHmDy+(b7utyWx9x-EDh0Xx|F_jtnAKh634c z&=M%r?>T#p-EzEbdp!zVZ@X&16gYp#uC-cn+i9&wP9#_M8?qC*zGosYP+VSHow~DD zpK`+~&yS`OL)}0&^Dtmp7{dvH+$Gm@qa}e;vxCOwHcU(i2s@7gzGmR0g;O{!FmY%< zaZA9IUNd~tjds35(`JFdJa3_hGXl@Gon5)?dkrU2S4f4C<3)^nW|$X)jwyf6S@^ng zCeOP;6x&wp^A@J?qQFSl-3nvOlt;C>K_vwUyrevu7PvUX?%<6fO7fV&Hx0}xIOmQa z^TyUa*@&3R*|`l>4rL4Tsus*hdJMMKRLn&Swqoj$zbm~wIp4DIZG4Af4aZw~zw59J zj-)10Tb2G5!|MXCKNE(}geucV<5xRw9F&&W8?U>b zyxDDU$>6rL)uIQ5y5DeG8&2S=eah6L9ha5#a{d47xhi904R?IkW73Q*CzP@Au|`WW z69*?_Y_-Pjs?$l$O{~gj$8X+p0;eq_8PN3EwXPSrZCUg7+|Z?GS3NeMqdJqPg?h_x zZ~43Sb?M1~ZQG>jT$mwY(A z$ZKw#s3YTTQH>7{s1&A+At06Nez0wa9X0drDrE2b!LA(=U+G65bfmrPw>!Qky@=qg z3>Dt;{YXvMNbW~==U}$-rW&|iBF}sr&Rms*P9X2P`>bpZX7=){)W8wqbdnT@<2k&a zM}W{kq-wq^Fnu@)U2l)5rE@wNZr7Yf-{oQV-I@le3 zoFf-+6NMq-@AS#{MnxYhRk7@}TI+5kt0wLXWQ1*kUS@BiWi|NnIB6Q@wP@ zw5pHGf&Q{8?sfzew3{g}%uIS8D74eA~ zqG;i`qVrExbO$F0r^M!LDNhN0HkYuDQ$K@ zsGlED|K@P@3(u-vRsVr{dq6$k`~)0vk%5m8oR7+-KSI|Ymj8v!AE0L+mj8*&>U`-D zUXHgUzI$_?pNh8dIYx`xXP9J@EEL%i7kd)bicXPgC0EpyOztxj9$>7f7ju0=OBI7c zOBJ(dC?PWyGKywTNP9?!lD6tEq|CVS%CHRc3!E^J`4XoM%<#_?U*h?M9O)-} zrSaA=a5eQiEv>v)=$)!OU7@Oi>Tjx~JCnleb*JjnO}cZCy3J$Sh5ETgHLPo~$W_g_ zI}_DQ+T}Pw=O(Hl~zcH$(vxW{#zaO`qzQF}Xt2cMghG}a+wFaGYw-w*K%p5)2>C4P