xucaiqin 3 місяців тому
батько
коміт
887a2e5276

+ 85 - 5
sckw-ai-biz/pom.xml

@@ -18,10 +18,9 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <dependencies>
-        <!-- 核心模块 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
+            <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -94,13 +93,29 @@
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
         </dependency>
+
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.redisson</groupId>
             <artifactId>redisson-spring-boot-starter</artifactId>
+            <!--            <exclusions>-->
+            <!--                <exclusion>-->
+            <!--                    <artifactId>spring-boot-actuator</artifactId>-->
+            <!--                    <groupId>org.springframework.boot</groupId>-->
+            <!--                </exclusion>-->
+            <!--                <exclusion>-->
+            <!--                    <artifactId>spring-boot-starter-actuator</artifactId>-->
+            <!--                    <groupId>org.springframework.boot</groupId>-->
+            <!--                </exclusion>-->
+            <!--                <exclusion>-->
+            <!--                    <artifactId>spring-boot-actuator-autoconfigure</artifactId>-->
+            <!--                    <groupId>org.springframework.boot</groupId>-->
+            <!--                </exclusion>-->
+            <!--            </exclusions>-->
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
@@ -118,23 +133,88 @@
             <groupId>com.sckw</groupId>
             <artifactId>sckw-ai-api</artifactId>
             <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <artifactId>sckw-order-api</artifactId>
             <groupId>com.sckw</groupId>
             <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.sckw</groupId>
+                    <artifactId>sckw-common-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <artifactId>sckw-transport-api</artifactId>
+            <groupId>com.sckw</groupId>
+            <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.sckw</groupId>
+                    <artifactId>sckw-common-core</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>com.sckw</groupId>
             <artifactId>sckw-system-api</artifactId>
             <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.sckw</groupId>
+                    <artifactId>sckw-common-core</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
-            <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
-            <version>4.5.0</version>
+            <groupId>com.sckw</groupId>
+            <artifactId>sckw-report-api</artifactId>
+            <version>1.0.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-webmvc</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.sckw</groupId>
+                    <artifactId>sckw-common-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
+            <version>2.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
+            <version>2.0.0-M2</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 5 - 2
sckw-ai-biz/src/main/java/com/sckw/ai/biz/AiApplication.java

@@ -4,11 +4,14 @@ package com.sckw.ai.biz;
 import com.sckw.ai.biz.config.ChatProperties;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 
-@SpringBootApplication
-@EnableFeignClients({"com.sckw.order.api.feign","com.sckw.system.api"}) //声明具体的feign
+@SpringBootApplication(exclude = {
+        WebMvcAutoConfiguration.class
+})
+@EnableFeignClients({"com.sckw.order.api.feign","com.sckw.system.api","com.sckw.transport.api.feign","com.sckw.report.api.feign"}) //声明具体的feign
 @EnableConfigurationProperties(ChatProperties.class)
 public class AiApplication {
 

+ 32 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/config/WebClientConfig.java

@@ -0,0 +1,32 @@
+package com.sckw.ai.biz.config;
+
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * @author xucaiqin
+ * @date 2026-03-09 11:16:35
+ */
+@Configuration
+public class WebClientConfig {
+    @Bean
+    @LoadBalanced
+    public WebClient.Builder webClientBuilder() {
+        return WebClient.builder();
+    }
+
+    @Bean
+    public WebClient webClientOrder(WebClient.Builder builder) {
+        return builder.baseUrl("http://sckw-ng-order").build();
+    }
+    @Bean
+    public WebClient webClientReport(WebClient.Builder builder) {
+        return builder.baseUrl("http://sckw-ng-report").build();
+    }
+    @Bean
+    public WebClient webClientTransport(WebClient.Builder builder) {
+        return builder.baseUrl("http://sckw-ng-transport").build();
+    }
+}

+ 1 - 2
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/ChatController.java

@@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
-import org.springdoc.core.annotations.ParameterObject;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -52,7 +51,7 @@ public class ChatController {
 
     @GetMapping(value = "/conversations")
     @Operation(summary = "获取当前用户的会话列表", description = "获取当前用户的会话列表")
-    public CommonResult<ApiPage<ConversionVo>> selectAll(@Valid @ParameterObject ConversionPara conversionPara) {
+    public CommonResult<ApiPage<ConversionVo>> selectAll(@Valid ConversionPara conversionPara) {
         return chatService.conversation(conversionPara);
     }
 

+ 1 - 2
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/IndexController.java

@@ -5,7 +5,6 @@ import com.sckw.ai.biz.pojo.OrderDto;
 import com.sckw.ai.biz.pojo.para.OrderPara;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import org.springdoc.core.annotations.ParameterObject;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
@@ -24,7 +23,7 @@ public class IndexController {
 
     @RequestMapping(value = "", method = RequestMethod.GET)
     @Operation(summary = "测试", description = "测试")
-    public CommonResult<List<OrderDto>> selectAll(@ParameterObject OrderPara orderPara) {
+    public CommonResult<List<OrderDto>> selectAll( OrderPara orderPara) {
         ArrayList<OrderDto> objects = new ArrayList<>();
         OrderDto orderDto = new OrderDto();
         orderDto.setStatus(1);

+ 2 - 3
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/MessageController.java

@@ -15,7 +15,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
-import org.springdoc.core.annotations.ParameterObject;
 import org.springframework.web.bind.annotation.*;
 
 
@@ -36,13 +35,13 @@ public class MessageController {
 
     @GetMapping(value = "/list")
     @Operation(summary = "获取当前会话的历史聊天", description = "获取当前会话的历史聊天")
-    public CommonResult<ApiPage<MessageVo>> selectAll(@Valid @ParameterObject MessagePara messagePara) {
+    public CommonResult<ApiPage<MessageVo>> selectAll(@Valid  MessagePara messagePara) {
         return messageService.list(messagePara);
     }
 
     @GetMapping(value = "/feedbacks")
     @Operation(summary = "获取消息点赞和反馈", description = "获取消息点赞和反馈")
-    public CommonResult<ApiPage<FeedbackVo>> get(@Valid @ParameterObject PagePara pagePara) {
+    public CommonResult<ApiPage<FeedbackVo>> get(@Valid  PagePara pagePara) {
         return messageService.feedbackList(pagePara);
     }
 }

+ 0 - 20
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/AuthInterceptorConfig.java

@@ -1,20 +0,0 @@
-package com.sckw.ai.biz.core;
-
-import com.sckw.ai.biz.interceptor.AuthenticationInterceptor;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-/**
- * 认证拦截器配置
- */
-@ConditionalOnClass(WebMvcConfigurer.class)
-@Configuration
-public class AuthInterceptorConfig implements WebMvcConfigurer {
-    @Override
-    public void addInterceptors(InterceptorRegistry registry) {
-        // 注册自定义拦截器实例,并指定拦截的路径模式
-        registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/**"); // 可以排除某些路径不被拦截
-    }
-}

+ 0 - 149
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/PageRes.java

@@ -1,149 +0,0 @@
-package com.sckw.ai.biz.core;
-
-import com.github.pagehelper.PageInfo;
-import com.sckw.core.utils.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 分页结果泛型类
- *
- * @author xucaiqin
- * @date 2023-07-12 08:42:26
- */
-public class PageRes<T> {
-
-    /**
-     * 当前页数
-     */
-    protected int page;
-    /**
-     * 每页显示条数
-     */
-    protected int pageSize;
-    /**
-     * 总条数
-     */
-    protected long size;
-    /**
-     * 总页数
-     */
-    protected int pages;
-    /**
-     * 数据
-     */
-    protected List<T> list;
-
-    public PageRes() {
-        this.list = new ArrayList<>();
-    }
-
-    public PageRes(int page, int pageSize, long size, int pages, List<T> list) {
-        this.page = page;
-        this.pageSize = pageSize;
-        this.size = size;
-        this.pages = pages;
-        this.list = list;
-    }
-
-    public PageRes(PageInfo<T> pageInfo) {
-        this.page = pageInfo.getPageNum();
-        this.pageSize = pageInfo.getPageSize();
-        this.size = pageInfo.getTotal();
-        this.pages = pageInfo.getPages();
-        this.list = CollectionUtils.isEmpty(pageInfo.getList()) ? new ArrayList<>() : pageInfo.getList();
-    }
-
-    public PageRes(PageInfo<T> pageInfo, List<T> list) {
-        this.page = pageInfo.getPageNum();
-        this.pageSize = pageInfo.getPageSize();
-        this.size = pageInfo.getTotal();
-        this.pages = pageInfo.getPages();
-        this.list = list;
-    }
-
-    public PageRes(List<T> list) {
-        PageInfo<T> pageInfo = new PageInfo<>(list);
-        this.page = pageInfo.getPageNum();
-        this.pageSize = pageInfo.getPageSize();
-        this.size = pageInfo.getTotal();
-        this.pages = pageInfo.getPages();
-        this.list = pageInfo.getList();
-    }
-
-    /**
-     * 构建返回数据
-     *
-     * @param pagInfo pageHelper的分页对象
-     * @param list    实际返回的数据集
-     * @return
-     */
-    public static <T> PageRes<T> build(PageInfo<?> pagInfo, List<T> list) {
-        PageRes<T> tPageRes = new PageRes<>();
-        tPageRes.setPage(pagInfo.getPageNum());
-        tPageRes.setPageSize(pagInfo.getPageSize());
-        tPageRes.setSize(pagInfo.getTotal());
-        tPageRes.setPages(pagInfo.getPages());
-        tPageRes.setList(list);
-        return tPageRes;
-    }
-
-    /**
-     * 手动对list进行分页数据处理
-     *
-     * @param page     当前页
-     * @param pageSize 每页大小
-     * @param list     总数据
-     * @return
-     */
-    public static <T> PageRes<T> handPage(int page, int pageSize, List<T> list) {
-        PageRes<T> tPageRes = new PageRes<>();
-        tPageRes.setPage(page);
-        tPageRes.setPageSize(pageSize);
-        tPageRes.setSize(list.size());
-        tPageRes.setPages(list.size() / pageSize + 1);
-        tPageRes.setList(list.stream().skip((long) (page - 1) * pageSize).limit(pageSize).toList());
-        return tPageRes;
-    }
-
-    public void setPage(int page) {
-        this.page = page;
-    }
-
-    public void setPageSize(int pageSize) {
-        this.pageSize = pageSize;
-    }
-
-    public void setSize(long size) {
-        this.size = size;
-    }
-
-    public void setPages(int pages) {
-        this.pages = pages;
-    }
-
-    public void setList(List<T> list) {
-        this.list = list;
-    }
-
-    public int getPage() {
-        return page;
-    }
-
-    public int getPageSize() {
-        return pageSize;
-    }
-
-    public long getSize() {
-        return size;
-    }
-
-    public int getPages() {
-        return pages;
-    }
-
-    public List<T> getList() {
-        return list;
-    }
-}

+ 31 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/web/ReactiveLoginContext.java

@@ -0,0 +1,31 @@
+package com.sckw.ai.biz.core.web;
+
+import com.sckw.ai.biz.core.model.LoginEntInfo;
+import com.sckw.ai.biz.core.model.LoginUserInfo;
+import reactor.util.context.Context;
+import reactor.util.context.ContextView;
+
+/**
+ * @author xucaiqin
+ * @date 2026-03-05 11:13:34
+ */
+public class ReactiveLoginContext {
+    // 定义 Key
+    public static final String USER_INFO_KEY = "loginUserInfo";
+    public static final String ENT_INFO_KEY = "loginEntInfo";
+
+    // 写入 Context (在 Filter 中做)
+    public static Context withLoginInfo(LoginUserInfo user, LoginEntInfo ent) {
+        return Context.of(USER_INFO_KEY, user)
+                .put(ENT_INFO_KEY, ent);
+    }
+
+    // 读取 Context (在 Service/Mapper 中做)
+    public static LoginUserInfo getUser(ContextView context) {
+        return context.getOrDefault(USER_INFO_KEY, null);
+    }
+
+    public static LoginEntInfo getEnt(ContextView context) {
+        return context.getOrDefault(ENT_INFO_KEY, null);
+    }
+}

+ 31 - 22
sckw-ai-biz/src/main/java/com/sckw/ai/biz/interceptor/AuthenticationInterceptor.java

@@ -1,48 +1,57 @@
 package com.sckw.ai.biz.interceptor;
 
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSON;
-import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.sckw.ai.biz.core.model.LoginEntInfo;
 import com.sckw.ai.biz.core.model.LoginUserInfo;
-import com.sckw.ai.biz.core.web.LoginEntHolder;
-import com.sckw.ai.biz.core.web.LoginUserHolder;
+import com.sckw.ai.biz.core.web.ReactiveLoginContext;
 import com.sckw.ai.biz.pojo.Global;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
 
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
+import java.util.Objects;
 
 /**
  * 登录认证拦截器,用于从请求头中获取登录用户信息并设置到上下文中
  */
 @Slf4j
-public class AuthenticationInterceptor implements HandlerInterceptor {
+@Component
+@Order(-1)
+public class AuthenticationInterceptor implements WebFilter {
+
     @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        String userInfoStrEncode = request.getHeader(Global.USER_INFO_STR_ENCODE);
-        String entInfoStrEncode = request.getHeader(Global.ENT_INFO_STR_ENCODE);
+    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+        ServerHttpRequest request = exchange.getRequest();
+
+        // 1. 获取 Header
+        String userInfoStrEncode = request.getHeaders().getFirst(Global.USER_INFO_STR_ENCODE);
+        String entInfoStrEncode = request.getHeaders().getFirst(Global.ENT_INFO_STR_ENCODE);
+
         LoginUserInfo loginUserInfo = null;
         LoginEntInfo loginEntInfo = null;
-        if(StringUtils.isNotBlank(userInfoStrEncode)){
+
+        if (StrUtil.isNotBlank(userInfoStrEncode)) {
             String userInfoStr = URLDecoder.decode(userInfoStrEncode, StandardCharsets.UTF_8);
             loginUserInfo = JSON.parseObject(userInfoStr, LoginUserInfo.class);
         }
-        if(StringUtils.isNotBlank(entInfoStrEncode)){
+        if (StrUtil.isNotBlank(entInfoStrEncode)) {
             String entInfoStr = URLDecoder.decode(entInfoStrEncode, StandardCharsets.UTF_8);
             loginEntInfo = JSON.parseObject(entInfoStr, LoginEntInfo.class);
         }
-        LoginUserHolder.set(loginUserInfo);
-        LoginEntHolder.set(loginEntInfo);
-        return true;
-    }
-
-    @Override
-    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
-        //清理用户和企业上下文线程变量
-        LoginUserHolder.remove();
-        LoginEntHolder.remove();
+        if (Objects.nonNull(loginUserInfo) && Objects.nonNull(loginEntInfo)) {
+            LoginUserInfo finalLoginUserInfo = loginUserInfo;
+            LoginEntInfo finalLoginEntInfo = loginEntInfo;
+            return chain.filter(exchange)
+                    .contextWrite(ctx -> ReactiveLoginContext.withLoginInfo(finalLoginUserInfo, finalLoginEntInfo));
+        }
+        return chain.filter(exchange);
     }
 }

+ 121 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/mcp/DatabaseTools.java

@@ -0,0 +1,121 @@
+package com.sckw.ai.biz.mcp;
+
+import com.sckw.ai.biz.util.R;
+import com.sckw.order.api.model.TradeOrderVo;
+import com.sckw.report.api.model.TodoCountVo;
+import com.sckw.transport.api.model.LogisticsBaseOrderVo;
+import com.sckw.transport.api.model.dto.McpLogisticsOrderVo;
+import com.sckw.transport.api.model.param.OrderPara;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springaicommunity.mcp.annotation.McpTool;
+import org.springaicommunity.mcp.annotation.McpToolParam;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * @author xucaiqin
+ * @date 2026-03-05 09:19:48
+ */
+@Component
+@Slf4j
+public class DatabaseTools {
+    @Resource
+    private  WebClient webClientReport;
+    @Resource
+    private  WebClient webClientOrder;
+    @Resource
+    private  WebClient webClientTransport;
+
+
+    @McpTool(name = "todoList", description = "统计当前用户待处理的贸易订单、物流运单、贸易合同、物流合同数据。")
+    public R<TodoCountVo> todoList(@McpToolParam(description = "用户ID,由上游传入") Long userId) {
+        try {
+            TodoCountVo todoCountVo = webClientReport.get().uri("/kwHome/count") // 后端 SSE 路径
+                    .header("Content-Type", "application/json")
+                    .retrieve().bodyToMono(TodoCountVo.class).block(Duration.ofSeconds(15));
+            return R.ok(todoCountVo);
+        } catch (Exception e) {
+            log.error("异常{} ", e.getMessage(), e);
+            return R.failed("接口异常,请稍后再试");
+        }
+    }
+
+    @McpTool(name = "tradeOrder", description = "根据时间范围或订单号查询用户的贸易订单")
+    public R<List<TradeOrderVo>> tradeOrder(
+            @McpToolParam(description = "用户ID,由上游传入") Long userId,
+            @McpToolParam(description = "订单开始时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String startTime,
+            @McpToolParam(description = "订单结束时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String endTime,
+            @McpToolParam(description = "贸易订单号,当使用具体单号、商品查数据时不使用默认的时间范围", required = false) String orderNo) {
+        try {
+            com.sckw.order.api.model.OrderPara tradeOrderPara = new com.sckw.order.api.model.OrderPara();
+            tradeOrderPara.setStartTime(startTime);
+            tradeOrderPara.setEndTime(endTime);
+            tradeOrderPara.setUserId(userId);
+            tradeOrderPara.setOrderNo(orderNo);
+            List<TradeOrderVo> mcpLogisticsOrderVos = webClientOrder.post().uri("/tradeOrder/trade")
+                    .bodyValue(tradeOrderPara)
+                    .header("Content-Type", "application/json")
+                    .retrieve().bodyToMono(new ParameterizedTypeReference<List<TradeOrderVo>>() {
+                    }).block(Duration.ofSeconds(15));
+            return R.ok(mcpLogisticsOrderVos);
+        } catch (Exception e) {
+            log.error("异常{} ", e.getMessage(), e);
+            return R.failed("接口异常,请稍后再试");
+        }
+    }
+
+    @McpTool(name = "logisticsList", description = "分页查询物流订单")
+    public R<List<McpLogisticsOrderVo>> logisticsList(
+            @McpToolParam(description = "用户ID,由上游传入") Long userId,
+            @McpToolParam(description = "订单开始时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String startTime,
+            @McpToolParam(description = "订单结束时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String endTime,
+            @McpToolParam(description = "物流订单编号,当使用具体单号、商品查数据时不使用默认的时间范围", required = false) String logisticsOrderNo,
+            @McpToolParam(description = "贸易订单号,当使用具体单号、商品查数据时不使用默认的时间范围", required = false) String orderNo) {
+        OrderPara orderPara = new OrderPara();
+        orderPara.setStartTime(startTime);
+        orderPara.setEndTime(endTime);
+        orderPara.setUserId(userId);
+        orderPara.setLogisticsOrderNo(logisticsOrderNo);
+        orderPara.setOrderNo(orderNo);
+        try {
+            List<McpLogisticsOrderVo> mcpLogisticsOrderVos = webClientTransport.post().uri("/logisticsOrder/list")
+                    .bodyValue(orderPara)
+                    .header("Content-Type", "application/json")
+                    .retrieve().bodyToMono(new ParameterizedTypeReference<List<McpLogisticsOrderVo>>() {
+                    }).block(Duration.ofSeconds(15));
+            return R.ok(mcpLogisticsOrderVos);
+        } catch (Exception e) {
+            log.error("异常{} ", e.getMessage(), e);
+            return R.failed("接口异常,请稍后再试");
+        }
+    }
+
+    @McpTool(name = "logisticsBase", description = "问题涉及 物流订单、订单物流、运输,未明确要求查明细时,优先使用该api")
+    public R<List<LogisticsBaseOrderVo>> logisticsBase(
+            @McpToolParam(description = "用户ID,由上游传入") Long userId,
+            @McpToolParam(description = "订单开始时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String startTime,
+            @McpToolParam(description = "订单结束时间,格式:yyyy-MM-dd HH:mm:ss", required = false) String endTime
+    ) {
+        OrderPara orderPara = new OrderPara();
+        orderPara.setStartTime(startTime);
+        orderPara.setEndTime(endTime);
+        orderPara.setUserId(userId);
+
+        try {
+            List<LogisticsBaseOrderVo> mcpLogisticsOrderVos = webClientTransport.post().uri("/logisticsOrder/base")
+                    .bodyValue(orderPara)
+                    .header("Content-Type", "application/json")
+                    .retrieve().bodyToMono(new ParameterizedTypeReference<List<LogisticsBaseOrderVo>>() {
+                    }).block(Duration.ofSeconds(15));
+            return R.ok(mcpLogisticsOrderVos);
+        } catch (Exception e) {
+            log.error("异常{} ", e.getMessage(), e);
+            return R.failed("接口异常,请稍后再试");
+        }
+    }
+}

+ 1 - 1
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/ChatService.java

@@ -2,9 +2,9 @@ package com.sckw.ai.biz.service;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.TypeReference;
-import com.github.xiaoymin.knife4j.core.util.StrUtil;
 import com.sckw.ai.biz.config.ChatProperties;
 import com.sckw.ai.biz.core.CommonResult;
 import com.sckw.ai.biz.core.exception.BusinessException;

+ 2 - 2
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/MessageService.java

@@ -64,7 +64,7 @@ public class MessageService {
     public CommonResult<ApiPage<MessageVo>> list(MessagePara messagePara) {
         Long userId = LoginUserHolder.getUserId();
         if (Objects.isNull(userId)) {
-            throw new com.sckw.core.exception.BusinessException("未登录,请先登录");
+            throw new BusinessException("未登录,请先登录");
         }
         HashMap<String, String> para = new HashMap<>();
         if (StrUtil.isNotBlank(messagePara.getConversionId())) {
@@ -100,7 +100,7 @@ public class MessageService {
     public CommonResult<ApiPage<FeedbackVo>> feedbackList(PagePara pagePara) {
         Long userId = LoginUserHolder.getUserId();
         if (Objects.isNull(userId)) {
-            throw new com.sckw.core.exception.BusinessException("未登录,请先登录");
+            throw new BusinessException("未登录,请先登录");
         }
         HashMap<String, String> para = new HashMap<>();
         para.put("page", String.valueOf(pagePara.getPage()));

+ 3 - 110
sckw-ai-biz/src/main/java/com/sckw/ai/biz/util/OkHttpUtils.java

@@ -1,20 +1,9 @@
 package com.sckw.ai.biz.util;
 
 import com.alibaba.fastjson2.JSONObject;
-import com.sckw.core.utils.UUIDUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.*;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
-import org.apache.hc.client5.http.entity.mime.FileBody;
-import org.apache.hc.client5.http.entity.mime.HttpMultipartMode;
-import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.util.CollectionUtils;
@@ -23,7 +12,6 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;
-import java.io.File;
 import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
@@ -182,25 +170,7 @@ public class OkHttpUtils {
         request = new Request.Builder().post(requestBody).url(buildUrl());
         return this;
     }
-    /**
-     * 初始化post方法
-     */
-    public OkHttpUtils postForm(JSONObject object, byte[] file) {
-        MultipartBody.Builder formBody = new MultipartBody.Builder();
-        formBody.setType(MultipartBody.FORM);
-
-        for (String s : object.keySet()) {
-            formBody.addFormDataPart(s, StringUtils.isBlank(object.getString(s)) ? "" : object.getString(s));
-        }
-        String fileName = UUIDUtils.get32UUID() + ".zip";
-        log.info("文件名:{}", fileName);
-        formBody.addFormDataPart("file", fileName, RequestBody.create(file));
 
-        RequestBody requestBody = formBody.build();
-        // params参数
-        request = new Request.Builder().post(requestBody).url(buildUrl());
-        return this;
-    }
 
     /**
      * 初始化post方法
@@ -404,86 +374,9 @@ public class OkHttpUtils {
     }
 
 
-    /**
-     * 使用示例
-     *
-     * @param args
-     * @author xcq
-     * @date 2023-02-21 10:47:28
-     **/
-    public static void main(String[] args) {
-        String address = "https://file.cloudpnr.com/app-86820a0f-8b13-479b-a466-b261af7290d5%2Fsaturnfile%2F48cf909816b142e5beec68988c1982b1%2F39189ab6-fac3-11ed-9690-0242ac110002.zip?Expires=1685254390&OSSAccessKeyId=LTAI6Yzq9tIYS57h&Signature=oNyqey777CjDk0IH5ZaniwTAdfg%3D";
-        byte[] file = OkHttpUtils.builder().url(address)
-                .get()
-                .fileSync();
-        /*通知中台*/
-        JSONObject tmp = new JSONObject();
-        tmp.put("status", StringUtils.equals("00000000", "00000000") ? "true" : "false");
-        tmp.put("msg", "");
-        log.info("交易确认异步通知中台入参:{}", tmp.toJSONString());
-
-        String sync = OkHttpUtils.builder().url("http://10.10.10.241:9505/notice/huifu/signal_agent_pay/S520267896211968001/00dd5d5dd4682ea3fad88a37ab48223a")
-                .postForm(tmp, file)
-                .sync();
-        log.info("交易确认异步通知中台返回->{}", sync);
-    }
 
-    /**
-     * post请求提交form-data上传文件
-     *
-     * @param url
-     * @return
-     */
-    public static String doPostUploadFile(String url, Map<String, Object> param) {
-        HttpPost httpPost = new HttpPost(url);
-        CloseableHttpResponse response = null;
-        String respContent = null;
-        CloseableHttpClient httpClient = HttpClients.createDefault();
-        try {
-            String boundary = "--" + UUIDUtils.get32UUID();
-            httpPost.setHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
-            MultipartEntityBuilder builder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.LEGACY);
-            builder.setCharset(StandardCharsets.UTF_8);
-            builder.setBoundary(boundary);
-            for (String key : param.keySet()) {
-                Object value = param.get(key);
-                if (value instanceof File file) {
-                    FileBody fileBody = new FileBody(file);
-                    builder.addBinaryBody(key, fileBody.getInputStream(), ContentType.DEFAULT_BINARY, fileBody.getFilename());
-                } else {
-                    builder.addBinaryBody(key, String.valueOf(value).getBytes(StandardCharsets.UTF_8));
-                }
-            }
-            HttpEntity entity = builder.build();
-            httpPost.setEntity(entity);
-            response = httpClient.execute(httpPost);
-            if (response != null) {
-                HttpEntity he = response.getEntity();
-                if (he != null) {
-                    respContent = EntityUtils.toString(he, StandardCharsets.UTF_8);
-                }
-            } else {
-                log.error("对方响应的状态码不在符合的范围内!");
-                throw new RuntimeException();
-            }
-            return respContent;
-        } catch (Exception e) {
-            log.error("网络访问异常,请求url地址={},响应体={},error=", url, response, e);
-            throw new RuntimeException();
-        } finally {
-            log.info("统一外网请求参数打印,post请求url地址={},响应={}", url, respContent);
-            try {
-                if (response != null) {
-                    response.close();
-                }
-                if (null != httpClient) {
-                    httpClient.close();
-                }
-            } catch (IOException e) {
-                log.error("请求链接释放异常", e);
-            }
-        }
-    }
+
+
 
 
     /**

+ 79 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/util/R.java

@@ -0,0 +1,79 @@
+package com.sckw.ai.biz.util;
+
+import lombok.*;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author xcq
+ * @date 2022年06月14日 13:41
+ */
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+@Accessors(chain = true)
+public class R<T> implements Serializable {
+    @Serial
+    private static final long serialVersionUID = -2087972616778698265L;
+    /**
+     * 1-成功 0-失败
+     */
+    @Getter
+    @Setter
+    private int code;
+    /**
+     * 提示信息
+     */
+    @Getter
+    @Setter
+    private String msg;
+    /**
+     * 数据
+     */
+    @Getter
+    @Setter
+    private T data;
+
+
+    public static <T> R<T> ok() {
+        return restResult(null, 1, null);
+    }
+
+    public static <T> R<T> ok(T data) {
+        return restResult(data, 1, null);
+    }
+
+    public static <T> R<T> ok(T data, String msg) {
+        return restResult(data, 1, msg);
+    }
+
+    public static <T> R<T> failed() {
+        return restResult(null, 0, null);
+    }
+
+    public static <T> R<T> failed(String msg) {
+        return restResult(null, 0, msg);
+    }
+
+    public static <T> R<T> failed(T data) {
+        return restResult(data, 0, null);
+    }
+
+    public static <T> R<T> failed(T data, String msg) {
+        return restResult(data, 0, msg);
+    }
+
+    public static <T> R<T> failed(T data, String msg, int code) {
+        return restResult(data, code, msg);
+    }
+
+    private static <T> R<T> restResult(T data, int code, String msg) {
+        R<T> apiResult = new R<>();
+        apiResult.setCode(code);
+        apiResult.setData(data);
+        apiResult.setMsg(msg);
+        return apiResult;
+    }
+}

+ 13 - 0
sckw-ai-biz/src/main/resources/bootstrap.yml

@@ -9,3 +9,16 @@ spring:
   main:
     allow-bean-definition-overriding: true
     allow-circular-references: true
+springdoc:
+  # 开启 Swagger UI
+  swagger-ui:
+    enabled: true
+    path: /swagger-ui.html # 访问地址
+  # API 文档路径
+  api-docs:
+    enabled: true
+    path: /v3/api-docs
+  # 包扫描路径 (可选,通常自动扫描 @RestController)
+  packages-to-scan: com.sckw.ai.biz
+  # WebFlux 特有:如果使用了 ContextPath,需要配置
+  # context-path: /api