xucaiqin преди 1 месец
родител
ревизия
0ee8cfc86c
променени са 52 файла, в които са добавени 3208 реда и са изтрити 116 реда
  1. 6 0
      sckw-ai-api/pom.xml
  2. 4 0
      sckw-ai-api/src/main/java/com/sckw/ai/api/feign/AiApi.java
  3. 25 2
      sckw-ai-biz/pom.xml
  4. 4 1
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/AiApplication.java
  5. 20 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/config/ChatProperties.java
  6. 47 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/ChatController.java
  7. 32 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/GuessController.java
  8. 40 13
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/IndexController.java
  9. 48 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/MessageController.java
  10. 20 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/AuthInterceptorConfig.java
  11. 100 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/CommonResult.java
  12. 0 88
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/HttpResult.java
  13. 149 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/PageRes.java
  14. 12 12
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/exception/GlobalSystemExceptionHandler.java
  15. 50 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/EntCertificateInfo.java
  16. 111 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/LoginEntInfo.java
  17. 107 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/LoginUserInfo.java
  18. 36 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/SystemTypeEnum.java
  19. 97 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/web/LoginEntHolder.java
  20. 219 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/web/LoginUserHolder.java
  21. 70 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/entity/AiGuess.java
  22. 48 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/interceptor/AuthenticationInterceptor.java
  23. 13 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/mapper/AiGuessMapper.java
  24. 33 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/AiGuessVo.java
  25. 34 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/ConversionVo.java
  26. 28 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/FeedbackVo.java
  27. 17 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/FilesItem.java
  28. 483 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/Global.java
  29. 38 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/MessageVo.java
  30. 44 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/OrderDto.java
  31. 23 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/ApiPage.java
  32. 34 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/ConversionDto.java
  33. 18 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/FeedBackDto.java
  34. 35 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/MessageDto.java
  35. 20 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/MessageFileDto.java
  36. 41 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/ChatPara.java
  37. 14 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/ConversionPara.java
  38. 21 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/FeedbackPara.java
  39. 16 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/MessagePara.java
  40. 21 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/OrderPara.java
  41. 31 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/PagePara.java
  42. 16 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/StopChatPara.java
  43. 34 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiApiEnum.java
  44. 88 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiApiInvoker.java
  45. 13 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiGuessService.java
  46. 117 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/ChatService.java
  47. 130 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/MessageService.java
  48. 41 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/impl/AiGuessServiceImpl.java
  49. 500 0
      sckw-ai-biz/src/main/java/com/sckw/ai/biz/util/OkHttpUtils.java
  50. 6 0
      sckw-ai-biz/src/main/resources/bootstrap-local.yml
  51. 35 0
      sckw-ai-biz/src/main/resources/bootstrap-test.yml
  52. 19 0
      sckw-ai-biz/src/main/resources/mapper/AiGuessMapper.xml

+ 6 - 0
sckw-ai-api/pom.xml

@@ -27,5 +27,11 @@
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-openfeign-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations-jakarta</artifactId>
+            <version>2.2.22</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>

+ 4 - 0
sckw-ai-api/src/main/java/com/sckw/ai/api/feign/AiApi.java

@@ -1,6 +1,8 @@
 package com.sckw.ai.api.feign;
 
 import com.sckw.ai.api.dto.ChatSession;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -10,8 +12,10 @@ import org.springframework.web.bind.annotation.RequestParam;
  * @date 2025-12-01 15:26:28
  */
 @FeignClient(name = "sckw-ng-ai", contextId = "aiApi")
+@Tag(name = "rpc-聊天")
 public interface AiApi {
     @GetMapping(value = "/ai/get")
+    @Operation(summary = "测试", description = "测试")
     ChatSession fileFeignDemo(@RequestParam("userName") String userName);
 
 }

+ 25 - 2
sckw-ai-biz/pom.xml

@@ -23,7 +23,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>
@@ -99,11 +102,31 @@
             <groupId>org.redisson</groupId>
             <artifactId>redisson-spring-boot-starter</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.sckw</groupId>
             <artifactId>sckw-ai-api</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <dependency>
+            <artifactId>sckw-order-api</artifactId>
+            <groupId>com.sckw</groupId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sckw</groupId>
+            <artifactId>sckw-system-api</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
+            <version>4.5.0</version>
+        </dependency>
+
     </dependencies>
 
     <build>
@@ -119,7 +142,7 @@
                     </execution>
                 </executions>
                 <configuration>
-                    <mainClass>com.sckw.file.FileApplication</mainClass>
+                    <mainClass>com.sckw.ai.biz.AiApplication</mainClass>
                 </configuration>
             </plugin>
         </plugins>

+ 4 - 1
sckw-ai-biz/src/main/java/com/sckw/ai/biz/AiApplication.java

@@ -1,12 +1,15 @@
 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.context.properties.EnableConfigurationProperties;
 import org.springframework.cloud.openfeign.EnableFeignClients;
 
 @SpringBootApplication
-@EnableFeignClients({"com.sckw.*.api.feign"})
+@EnableFeignClients({"com.sckw.order.api.feign","com.sckw.system.api"}) //声明具体的feign
+@EnableConfigurationProperties(ChatProperties.class)
 public class AiApplication {
 
     public static void main(String[] args) {

+ 20 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/config/ChatProperties.java

@@ -0,0 +1,20 @@
+package com.sckw.ai.biz.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-04 16:10:26
+ */
+@Getter
+@Setter
+@Configuration
+@ConfigurationProperties(prefix = "chat")
+public class ChatProperties {
+    private String url;
+    private String header;
+    private String headerPrefix;
+}

+ 47 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/ChatController.java

@@ -0,0 +1,47 @@
+package com.sckw.ai.biz.controller;
+
+import com.sckw.ai.biz.core.CommonResult;
+import com.sckw.ai.biz.pojo.ConversionVo;
+import com.sckw.ai.biz.pojo.dto.ApiPage;
+import com.sckw.ai.biz.pojo.para.ChatPara;
+import com.sckw.ai.biz.pojo.para.ConversionPara;
+import com.sckw.ai.biz.pojo.para.StopChatPara;
+import com.sckw.ai.biz.service.ChatService;
+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.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Flux;
+
+
+@RestController
+@RequestMapping("/chat")
+@Tag(name = "聊天")
+public class ChatController {
+
+    @Resource
+    private ChatService chatService;
+
+    @Operation(summary = "创建聊天会话", description = "创建聊天会话")
+    @PostMapping(value = "/msg", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public Flux<String> msg(@RequestBody @Valid ChatPara chatPara) {
+        return chatService.chat(chatPara);
+    }
+
+    @Operation(summary = "停止响应", description = "停止响应聊天会话")
+    @PostMapping(value = "/stop")
+    public CommonResult<Object> stop(@RequestBody @Valid StopChatPara stopChatPara) {
+        return chatService.stopChat(stopChatPara);
+    }
+
+    @GetMapping(value = "/conversations")
+    @Operation(summary = "获取当前用户的会话列表", description = "获取当前用户的会话列表")
+    public CommonResult<ApiPage<ConversionVo>> selectAll(@Valid @ParameterObject ConversionPara conversionPara) {
+        return chatService.conversation(conversionPara);
+    }
+
+
+}

+ 32 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/GuessController.java

@@ -0,0 +1,32 @@
+package com.sckw.ai.biz.controller;
+
+import com.sckw.ai.biz.core.CommonResult;
+import com.sckw.ai.biz.pojo.AiGuessVo;
+import com.sckw.ai.biz.service.AiGuessService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+
+@RestController
+@RequestMapping("/guess")
+@Tag(name = "猜测")
+public class GuessController {
+
+    @Resource
+    private AiGuessService aiGuessService;
+
+    @GetMapping(value = "/list")
+    @Operation(summary = "猜你想问", description = "猜你想问")
+    @Parameter(name = "size", description = "数量", example = "4")
+    public CommonResult<List<AiGuessVo>> list(Integer size) {
+        return CommonResult.ok(aiGuessService.randomList(size));
+    }
+
+}

+ 40 - 13
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/IndexController.java

@@ -1,27 +1,54 @@
 package com.sckw.ai.biz.controller;
 
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import org.apache.logging.log4j.core.config.plugins.validation.constraints.NotBlank;
-import org.apache.tomcat.jni.FileInfo;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
+import com.sckw.ai.biz.core.CommonResult;
+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;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.util.Map;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
 
 
 @RestController
 @RequestMapping("/index")
+@Tag(name = "首页")
 public class IndexController {
 
 
     @RequestMapping(value = "", method = RequestMethod.GET)
-    public String selectAll() {
-        return "{}";
+    @Operation(summary = "测试", description = "测试")
+    public CommonResult<List<OrderDto>> selectAll(@ParameterObject OrderPara orderPara) {
+        ArrayList<OrderDto> objects = new ArrayList<>();
+        OrderDto orderDto = new OrderDto();
+        orderDto.setStatus(1);
+        orderDto.setStatusLabel("完结");
+        orderDto.setTOrderNo("TO202500012332586");
+        orderDto.setBuyEntName("开物信息有限公司");
+        orderDto.setSellEntName("开五七元信息企业");
+        orderDto.setGoodsName("煤堆棚");
+        orderDto.setGoodsType("1");
+        orderDto.setGoodsTypeLabel("矿石/煤堆棚");
+        orderDto.setUnitPrice(new BigDecimal("30.0"));
+        orderDto.setUnit(1);
+        orderDto.setUnitLabel("吨");
+        orderDto.setAmount(new BigDecimal("234"));
+        orderDto.setPrice(new BigDecimal("23"));
+        orderDto.setLoadAmount(new BigDecimal("2333"));
+        orderDto.setUnloadAmount(new BigDecimal("4444"));
+        orderDto.setChargeType(1);
+        orderDto.setChargeTypeLabel("到货");
+        orderDto.setCreateTime(LocalDateTime.now());
+
+
+        objects.add(orderDto);
+        return CommonResult.ok(objects);
     }
 
 }

+ 48 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/controller/MessageController.java

@@ -0,0 +1,48 @@
+package com.sckw.ai.biz.controller;
+
+import com.sckw.ai.biz.core.CommonResult;
+import com.sckw.ai.biz.pojo.FeedbackVo;
+import com.sckw.ai.biz.pojo.MessageVo;
+import com.sckw.ai.biz.pojo.dto.ApiPage;
+import com.sckw.ai.biz.pojo.para.FeedbackPara;
+import com.sckw.ai.biz.pojo.para.MessagePara;
+import com.sckw.ai.biz.pojo.para.PagePara;
+import com.sckw.ai.biz.service.MessageService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+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.*;
+
+
+@RestController
+@RequestMapping("/message")
+@Tag(name = "消息")
+public class MessageController {
+
+    @Resource
+    private MessageService messageService;
+
+    @Operation(summary = "消息终端用户反馈、点赞", description = "消息终端用户反馈、点赞")
+    @PostMapping(value = "/feedbacks")
+    @ApiResponse(responseCode = "200", description = "成功", content = @Content(schema = @Schema(implementation = CommonResult.class), mediaType = "application/json"))
+    public CommonResult<Object> msg(@RequestBody @Valid FeedbackPara feedbackPara) {
+        return messageService.feedbacks(feedbackPara);
+    }
+
+    @GetMapping(value = "/list")
+    @Operation(summary = "获取当前会话的历史聊天", description = "获取当前会话的历史聊天")
+    public CommonResult<ApiPage<MessageVo>> selectAll(@Valid @ParameterObject MessagePara messagePara) {
+        return messageService.list(messagePara);
+    }
+
+    @GetMapping(value = "/feedbacks")
+    @Operation(summary = "获取消息点赞和反馈", description = "获取消息点赞和反馈")
+    public CommonResult<ApiPage<FeedbackVo>> get(@Valid @ParameterObject PagePara pagePara) {
+        return messageService.feedbackList(pagePara);
+    }
+}

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

@@ -0,0 +1,20 @@
+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("/**"); // 可以排除某些路径不被拦截
+    }
+}

+ 100 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/CommonResult.java

@@ -0,0 +1,100 @@
+package com.sckw.ai.biz.core;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 通用返回
+ *
+ * @param <T> 数据泛型
+ */
+@Data
+public class CommonResult<T> implements Serializable {
+
+    /**
+     * 错误码
+     */
+    private Integer code = HttpStatus.SUCCESS_CODE;
+    /**
+     * 返回数据
+     */
+    private T data;
+    /**
+     * 错误提示,用户可阅读
+     */
+    private String msg = HttpStatus.SUCCESS_MESSAGE;
+
+
+    public static <T> CommonResult<T> error() {
+        return new CommonResult<>();
+    }
+
+    public static <T> CommonResult<T> error(int code, String msg) {
+        CommonResult<T> result = new CommonResult<>();
+        result.msg = msg;
+        result.code = code;
+        return result;
+    }
+
+    public static <T> CommonResult<T> error(String msg, T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.msg = msg;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> CommonResult<T> error(int code, String msg, T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.code = code;
+        result.msg = msg;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> CommonResult<T> error(String msg) {
+        CommonResult<T> result = new CommonResult<>();
+        result.msg = msg;
+        return result;
+    }
+
+    public static <T> CommonResult<T> error(T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.data = data;
+        result.msg = "";
+        return result;
+    }
+
+    public static <T> CommonResult<T> ok() {
+        return new CommonResult<>();
+    }
+
+    public static <T> CommonResult<T> ok(String msg, T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.msg = msg;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> CommonResult<T> ok(int code, String msg, T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.code = code;
+        result.msg = msg;
+        result.data = data;
+        return result;
+    }
+
+    public static <T> CommonResult<T> ok(String msg) {
+        CommonResult<T> result = new CommonResult<>();
+        result.msg = msg;
+        return result;
+    }
+
+    public static <T> CommonResult<T> ok(T data) {
+        CommonResult<T> result = new CommonResult<>();
+        result.data = data;
+        result.msg = "";
+        return result;
+    }
+
+}

+ 0 - 88
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/HttpResult.java

@@ -1,88 +0,0 @@
-package com.sckw.ai.biz.core;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * HTTP结果封装
- * @author zk
- * @date Oct 29, 2018
- */
-@Data
-public class HttpResult implements Serializable {
-
-	private int code = HttpStatus.SUCCESS_CODE;
-	private String msg = HttpStatus.SUCCESS_MESSAGE;
-	private Object data;
-
-	public static HttpResult error() {
-		return error(HttpStatus.GLOBAL_EXCEPTION_CODE, HttpStatus.GLOBAL_EXCEPTION_MESSAGE);
-	}
-
-	public static HttpResult error(String msg) {
-		return error(HttpStatus.GLOBAL_EXCEPTION_CODE, msg);
-	}
-
-	public static HttpResult error(int code, String msg) {
-		HttpResult r = new HttpResult();
-		r.setCode(code);
-		r.setMsg(msg);
-		return r;
-	}
-
-	public static HttpResult error(String msg, Object data) {
-		HttpResult r = new HttpResult();
-		r.setCode(HttpStatus.GLOBAL_EXCEPTION_CODE);
-		r.setMsg(msg);
-		r.setData(data);
-		return r;
-	}
-
-	public static HttpResult error(int code, String msg, Object data) {
-		HttpResult r = new HttpResult();
-		r.setCode(code);
-		r.setMsg(msg);
-		r.setData(data);
-		return r;
-	}
-
-
-	public static HttpResult ok() {
-		return new HttpResult();
-	}
-
-	public static HttpResult ok(String msg) {
-		HttpResult r = new HttpResult();
-		r.setMsg(msg);
-		return r;
-	}
-
-	public static HttpResult ok(Object data) {
-		HttpResult r = new HttpResult();
-		r.setData(data);
-		return r;
-	}
-
-    public static HttpResult ok(int code, String msg) {
-        HttpResult r = new HttpResult();
-        r.setCode(code);
-        r.setMsg(msg);
-        return r;
-    }
-
-	public static HttpResult ok(String msg, Object data) {
-		HttpResult r = new HttpResult();
-		r.setMsg(msg);
-		r.setData(data);
-		return r;
-	}
-
-    public static HttpResult ok(int code,String msg, Object data) {
-        HttpResult r = new HttpResult();
-        r.setCode(code);
-        r.setMsg(msg);
-        r.setData(data);
-        return r;
-    }
-}

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

@@ -0,0 +1,149 @@
+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;
+    }
+}

+ 12 - 12
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/exception/GlobalSystemExceptionHandler.java

@@ -1,6 +1,6 @@
 package com.sckw.ai.biz.core.exception;
 
-import com.sckw.ai.biz.core.HttpResult;
+import com.sckw.ai.biz.core.CommonResult;
 import com.sckw.ai.biz.core.HttpStatus;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Configuration;
@@ -22,13 +22,13 @@ public class GlobalSystemExceptionHandler {
 
     @ExceptionHandler(value = RuntimeException.class)
     @ResponseBody
-    public HttpResult handlerRuntimeException(RuntimeException e) {
+    public CommonResult<Object> handlerRuntimeException(RuntimeException e) {
         log.error("业务异常:", e);
         // 处理Dubbo服务不可用异常
         if (e.getMessage() != null && e.getMessage().contains("No provider available")) {
-            return HttpResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, "服务暂时不可用,请稍后重试");
+            return CommonResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, "服务暂时不可用,请稍后重试");
         }
-        return HttpResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, e.getMessage());
+        return CommonResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, e.getMessage());
     }
 
     /**
@@ -39,17 +39,17 @@ public class GlobalSystemExceptionHandler {
      */
     @ResponseBody
     @ExceptionHandler(BusinessException.class)
-    public HttpResult businessExceptionHandler(BusinessException ex) {
+    public CommonResult<Object> businessExceptionHandler(BusinessException ex) {
         log.error("业务异常,message={},param={}", ex.getMsg(), ex.getParam());
-        return HttpResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, ex.getMessage());
+        return CommonResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, ex.getMessage());
     }
 
 
     @ResponseBody
     @ExceptionHandler(MissingServletRequestParameterException.class)
-    public HttpResult noArgs(MissingServletRequestParameterException ex) {
+    public CommonResult<Object> noArgs(MissingServletRequestParameterException ex) {
         String format = "参数:[%s]不能为空";
-        return HttpResult.error(HttpStatus.PARAMETERS_PATTERN_ERROR_CODE, String.format(format, ex.getParameterName()));
+        return CommonResult.error(HttpStatus.PARAMETERS_PATTERN_ERROR_CODE, String.format(format, ex.getParameterName()));
     }
 
     /**
@@ -60,7 +60,7 @@ public class GlobalSystemExceptionHandler {
      */
     @ResponseBody
     @ExceptionHandler(MethodArgumentNotValidException.class)
-    public HttpResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
+    public CommonResult<Object> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
         List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
         StringBuilder sb = new StringBuilder();
         if (!CollectionUtils.isEmpty(fieldErrors)) {
@@ -75,7 +75,7 @@ public class GlobalSystemExceptionHandler {
         }
         String errMsg = sb.toString();
         log.error("参数校验异常:{}", errMsg);
-        return HttpResult.error(HttpStatus.PARAMETERS_PATTERN_ERROR_CODE, errMsg);
+        return CommonResult.error(HttpStatus.PARAMETERS_PATTERN_ERROR_CODE, errMsg);
     }
 
     /**
@@ -86,9 +86,9 @@ public class GlobalSystemExceptionHandler {
      */
     @ResponseBody
     @ExceptionHandler(Exception.class)
-    public HttpResult defaultExceptionHandler(Exception ex) {
+    public CommonResult<Object> defaultExceptionHandler(Exception ex) {
         log.error("系统异常", ex);
-        return HttpResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, ex.toString());
+        return CommonResult.error(HttpStatus.GLOBAL_EXCEPTION_CODE, ex.toString());
     }
 
 }

+ 50 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/EntCertificateInfo.java

@@ -0,0 +1,50 @@
+package com.sckw.ai.biz.core.model;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author czh
+ * @desc 资质
+ * @date 2023/6/29
+ */
+@Data
+public class EntCertificateInfo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -1913060977212008558L;
+
+    /**
+     * 执照编号
+     */
+    private String code;
+
+    /**
+     * 证书类型 (1联系人身份证 2法人身份证 3营业执照 4道路运输许可证 5开户许可证号 6授权证书 6电子签章授权书)
+     */
+    private int type;
+
+    /**
+     * 证书正面
+     */
+    private String certificateMain;
+
+    /**
+     * 证书反面
+     */
+    private String certificateRevolt;
+
+    /**
+     * 有效期
+     */
+    private Date expireTime;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 111 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/LoginEntInfo.java

@@ -0,0 +1,111 @@
+package com.sckw.ai.biz.core.model;
+
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 登录中的用户企业信息
+ * @Author zhaokang
+ * @date 2020/04/13 0021
+ */
+@Data
+public class LoginEntInfo {
+
+    /**
+     * 用户所属企业id
+     */
+    private Long id;
+
+    /**
+     * 用户所属企业名称
+     */
+    private String firmName;
+
+    /**
+     * 资料审批状态(0未审批、1通过、2未通过、3审批中)
+     */
+    private int approval;
+
+    /**
+     * 用户状态(-1删除、0正常、1已锁)
+     */
+    private int status;
+
+    /**
+     * 交通安培账号状态
+     */
+    private int jtaqStatus;
+
+    /**
+     * 交通安培账号删除标识
+     */
+    private int jtaqDelFlag;
+
+    /**
+     * 企业联系人
+     */
+    private String contacts;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 法人姓名
+     */
+    private String legalName;
+
+    /**
+     * 法人联系电话
+     */
+    private String legalPhone;
+
+    /**
+     * 企业注册时间
+     */
+    private Date regTime;
+
+    /**
+     * 是否有效
+     */
+    private Boolean valid;
+
+    /**
+     * 企业属性
+     */
+    private String entTypes;
+
+    /**
+     * 专场标识
+     */
+    private String special;
+
+    /**
+     * 资质
+     */
+    private List<EntCertificateInfo> certificateInfo;
+
+    /**
+     * 企业类型(名称)
+     */
+    private String entTypeNames;
+
+    /**
+     * 企业联系人(企业管理员)
+     */
+    private Long mainId;
+
+    /**
+     * 企业联系人(企业管理员)
+     */
+    private String mainName;
+
+    /**
+     * 联系电话(企业管理员)
+     */
+    private String mainPhone;
+
+}

+ 107 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/LoginUserInfo.java

@@ -0,0 +1,107 @@
+package com.sckw.ai.biz.core.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 登录中的用户信息
+ *
+ * @Author zhaokang
+ * @date 2020/04/13 0021
+ */
+@Data
+public class LoginUserInfo {
+    /**
+     * 用户id
+     */
+    private Long id;
+
+    /**
+     * 用户所属系统
+     */
+    private Integer systemType;
+
+    /**
+     * 用户账号
+     */
+    private String account;
+
+    /**
+     * 用户姓名
+     */
+    private String userName;
+
+    /**
+     * 用户电话
+     */
+    private String phone;
+
+    /**
+     * 是否主账号(0是/1否)
+     */
+    private int isMain;
+
+    /**
+     * 用户账号状态(0正常/1锁定)
+     */
+    private Integer status;
+
+    /**
+     * 用户所属企业id
+     */
+    private Long entId;
+    /**
+     * 当前用户使用的角色id和角色关联的企业id
+     */
+    private Long useRoleId;
+    private Long useEntId;
+
+    /**
+     * 用户登录终端
+     */
+    private String clientType;
+
+    /**
+     * 用户所属机构id
+     */
+    private String deptIds;
+
+    /**
+     * 企业名
+     */
+    private String entName;
+
+    /**
+     * 用户权限
+     */
+    private List<Long> authUserIdList;
+
+    /**
+     * 用户(客户经理)企业权限
+     */
+    private List<Long> authEntIdList;
+    /**
+     * 当前用户,所有的子企业,包含用户所在企业 新增的企业数据权限部分
+     */
+    private List<Long> childEntList;
+
+    public LoginUserInfo() {
+    }
+
+    public LoginUserInfo(Long id, Integer systemType, String account, String userName, String phone,
+                         int isMain, int status, Long entId, String clientType, String deptIds, List<Long> authUserIdList, List<Long> authEntIdList) {
+        this.id = id;
+        this.systemType = systemType;
+        this.account = account;
+        this.userName = userName;
+        this.phone = phone;
+        this.isMain = isMain;
+        this.status = status;
+        this.entId = entId;
+        this.clientType = clientType;
+        this.deptIds = deptIds;
+        this.authUserIdList = authUserIdList;
+        this.authEntIdList = authEntIdList;
+    }
+}

+ 36 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/model/SystemTypeEnum.java

@@ -0,0 +1,36 @@
+package com.sckw.ai.biz.core.model;
+
+import lombok.Getter;
+
+/**
+ * @author czh
+ * @desc 系统类型枚举
+ * @date 2023/6/15
+ */
+@Getter
+public enum SystemTypeEnum {
+    //运营端
+    MANAGE(1, "运营端"), //平台运营端
+    //企业开户
+    COMPANY(2, "企业端"),//管理端
+    //司机
+    DRIVER(3, "司机端");
+
+    private final Integer code;
+
+    private final String name;
+
+    SystemTypeEnum(Integer code, String name){
+        this.code = code;
+        this.name = name;
+    }
+
+    public static SystemTypeEnum getName(Integer code){
+        for (SystemTypeEnum systemTypeEnum : values()) {
+            if (systemTypeEnum.getCode().equals(code)) {
+                return systemTypeEnum;
+            }
+        }
+        return null;
+    }
+}

+ 97 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/web/LoginEntHolder.java

@@ -0,0 +1,97 @@
+package com.sckw.ai.biz.core.web;
+
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.sckw.ai.biz.core.model.LoginEntInfo;
+
+/**
+ * 当前登录用户企业的临时保存容器
+ * @Author zhaokang
+ * @date 2020/04/13 0021
+ */
+public class LoginEntHolder {
+    private static final ThreadLocal<LoginEntInfo> LONGIN_ENT_HOLDER = new ThreadLocal<LoginEntInfo>();
+
+    /**
+     * 赋值
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static void set(LoginEntInfo loginEnterpriseInfo) {
+        LONGIN_ENT_HOLDER.set(loginEnterpriseInfo);
+    }
+
+    /**
+     * 取值
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static LoginEntInfo get() {
+        return LONGIN_ENT_HOLDER == null ? null : LONGIN_ENT_HOLDER.get();
+    }
+
+    /**
+     * 删除保存的用户
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static void remove() {
+        LONGIN_ENT_HOLDER.remove();
+    }
+
+    /**
+     * 用户所属企业id
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Long getEntId(){
+        return LONGIN_ENT_HOLDER.get() == null ? null : LONGIN_ENT_HOLDER.get().getId();
+    }
+
+    /**
+     * 用户所属企业名称
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getFirmName(){
+        return LONGIN_ENT_HOLDER.get() == null ? null : LONGIN_ENT_HOLDER.get().getFirmName();
+    }
+
+    /**
+     * 资料审批状态(1通过、0未审批、2未通过、3审批中)
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Integer getApproval(){
+        return LONGIN_ENT_HOLDER.get() == null ? null : LONGIN_ENT_HOLDER.get().getApproval();
+    }
+
+    /**
+     * 用户状态(-1删除、0正常、1已锁)
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Integer getStatus(){
+        return LONGIN_ENT_HOLDER.get() == null ? null : LONGIN_ENT_HOLDER.get().getStatus();
+    }
+
+    /**
+     * 专场标识
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getSpecial(){
+        return LONGIN_ENT_HOLDER.get() == null ? null : LONGIN_ENT_HOLDER.get().getSpecial();
+    }
+
+    /**
+     * 是否主平台(不属于专场)
+     * @author zk
+     * @Date 2023/12/17 0021
+     */
+    public static boolean getMainPlatform(){
+        String pecial = getSpecial();
+        return StringUtils.isNotBlank(pecial);
+    }
+
+}

+ 219 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/core/web/LoginUserHolder.java

@@ -0,0 +1,219 @@
+package com.sckw.ai.biz.core.web;
+
+
+import com.sckw.ai.biz.core.model.LoginUserInfo;
+import com.sckw.ai.biz.core.model.SystemTypeEnum;
+import org.springframework.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 当前登录用户的临时保存容器
+ *
+ * @Author zk
+ * @date 2020/04/13 0021
+ */
+public class LoginUserHolder {
+
+    private static final ThreadLocal<LoginUserInfo> LONGIN_USER_HOLDER = new ThreadLocal<LoginUserInfo>();
+
+    /**
+     * 赋值
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static void set(LoginUserInfo loginUserInfo) {
+        LONGIN_USER_HOLDER.set(loginUserInfo);
+    }
+
+    /**
+     * 取值
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static LoginUserInfo get() {
+        return LONGIN_USER_HOLDER == null ? null : LONGIN_USER_HOLDER.get();
+    }
+
+    /**
+     * 删除保存的用户
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static void remove() {
+        LONGIN_USER_HOLDER.remove();
+    }
+
+    /**
+     * 用户id
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Long getUserId() {
+        return LONGIN_USER_HOLDER.get() == null ? 1L : LONGIN_USER_HOLDER.get().getId();
+    }
+
+    /**
+     * 用户所属系统
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Integer getSystemType() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getSystemType();
+    }
+
+    /**
+     * 是否为平台运营端
+     *
+     * @return
+     */
+    public static Boolean isManager() {
+        return Objects.equals(getSystemType(), SystemTypeEnum.MANAGE.getCode());
+    }
+
+    /**
+     * 用户账号
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getAccount() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getAccount();
+    }
+
+    /**
+     * 用户姓名
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getUserName() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getUserName();
+    }
+
+    /**
+     * 用户电话
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getPhone() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getPhone();
+    }
+
+    /**
+     * 是否主账号
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Integer getIsMain() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getIsMain();
+    }
+
+    /**
+     * 用户账号状态(0正常/1锁定)
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Integer getStatus() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getStatus();
+    }
+
+    /**
+     * 用户所属企业
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static Long getEntId() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getEntId();
+    }
+
+    /**
+     * 用户所属企业
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getEntName() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getEntName();
+    }
+
+    /**
+     * 用户登录终端
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getClientType() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getClientType();
+    }
+
+    /**
+     * 用户机构
+     *
+     * @author zhaokang
+     * @Date 2020/04/13 0021
+     */
+    public static String getDeptIds() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getDeptIds();
+    }
+
+    public static Long getCurrentRoleId() {
+        return LONGIN_USER_HOLDER.get() == null ? null : LONGIN_USER_HOLDER.get().getUseRoleId();
+    }
+
+    /**
+     * 用户权限
+     */
+    public static List<Long> getAuthUserIdList() {
+        if (LONGIN_USER_HOLDER.get() == null) {
+            return new ArrayList<>();
+        }
+        List<Long> authUserIdList = LONGIN_USER_HOLDER.get().getAuthUserIdList();
+        if (CollectionUtils.isEmpty(authUserIdList)) {
+            return new ArrayList<>();
+        }
+
+        return authUserIdList;
+    }
+
+    /**
+     * 运营端客户经理(用户)关联企业
+     *
+     * @return 企业ID集合
+     */
+    public static List<Long> getAuthEntIdList() {
+        if (LONGIN_USER_HOLDER.get() == null) {
+            return new ArrayList<>();
+        }
+        List<Long> authUserIdList = LONGIN_USER_HOLDER.get().getAuthEntIdList();
+        if (CollectionUtils.isEmpty(authUserIdList)) {
+            return new ArrayList<>();
+        }
+
+        return authUserIdList;
+    }
+
+    public static List<Long> getChildEntList() {
+        if (LONGIN_USER_HOLDER.get() == null) {
+            return new ArrayList<>();
+        }
+        List<Long> childEntList = LONGIN_USER_HOLDER.get().getChildEntList();
+        if (CollectionUtils.isEmpty(childEntList)) {
+            return new ArrayList<>();
+        }
+
+        return childEntList;
+    }
+
+}

+ 70 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/entity/AiGuess.java

@@ -0,0 +1,70 @@
+package com.sckw.ai.biz.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * 猜你想问
+ *
+ * @author xucaiqin
+ * @date 2025-12-05 09:56:23
+ */
+@Schema(description = "猜你想问")
+@Getter
+@Setter
+@TableName(value = "ai_guess")
+public class AiGuess {
+    /**
+     * id
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @Schema(description = "id")
+    private Long id;
+
+    /**
+     * 问题
+     */
+    @TableField(value = "question")
+    @Schema(description = "问题")
+    private String question;
+
+    /**
+     * 创建人ID
+     */
+    @TableField(value = "create_by")
+    @Schema(description = "创建人ID")
+    private Long createBy;
+
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time")
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    /**
+     * 更新人ID
+     */
+    @TableField(value = "update_by")
+    @Schema(description = "更新人ID")
+    private Long updateBy;
+
+    /**
+     * 更新时间
+     */
+    @TableField(value = "update_time")
+    @Schema(description = "更新时间")
+    private LocalDateTime updateTime;
+
+    /**
+     * 是否删除(0未删除,1删除)
+     */
+    @TableField(value = "del_flag")
+    @TableLogic(value = "0", delval = "1")
+    @Schema(description = "是否删除(0未删除,1删除)")
+    private Integer delFlag;
+}

+ 48 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/interceptor/AuthenticationInterceptor.java

@@ -0,0 +1,48 @@
+package com.sckw.ai.biz.interceptor;
+
+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.pojo.Global;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 登录认证拦截器,用于从请求头中获取登录用户信息并设置到上下文中
+ */
+@Slf4j
+public class AuthenticationInterceptor implements HandlerInterceptor {
+    @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);
+        LoginUserInfo loginUserInfo = null;
+        LoginEntInfo loginEntInfo = null;
+        if(StringUtils.isNotBlank(userInfoStrEncode)){
+            String userInfoStr = URLDecoder.decode(userInfoStrEncode, StandardCharsets.UTF_8);
+            loginUserInfo = JSON.parseObject(userInfoStr, LoginUserInfo.class);
+        }
+        if(StringUtils.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();
+    }
+}

+ 13 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/mapper/AiGuessMapper.java

@@ -0,0 +1,13 @@
+package com.sckw.ai.biz.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.sckw.ai.biz.entity.AiGuess;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+* @date 2025-12-05 09:56:23
+* @author xucaiqin
+*/
+@Mapper
+public interface AiGuessMapper extends BaseMapper<AiGuess> {
+}

+ 33 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/AiGuessVo.java

@@ -0,0 +1,33 @@
+package com.sckw.ai.biz.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * 猜你想问
+* @date 2025-12-05 09:56:23
+* @author xucaiqin
+*/
+@Schema(description="猜你想问")
+@Getter
+@Setter
+public class AiGuessVo {
+    /**
+     * id
+     */
+    @Schema(description="id")
+    private Long id;
+
+    /**
+     * 问题
+     */
+    @Schema(description="问题")
+    private String question;
+}

+ 34 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/ConversionVo.java

@@ -0,0 +1,34 @@
+package com.sckw.ai.biz.pojo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "会话列表")
+public class ConversionVo {
+	@Schema(description = "会话 ID")
+	private String id;
+
+	@Schema(description = "会话名称")
+	private String name;
+
+	@Schema(description = "用户输入参数")
+	private Object inputs;
+
+	@Schema(description = "会话状态")
+	private String status;
+
+	@Schema(description = "开场白")
+	private String introduction;
+
+	@Schema(description = "创建时间")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+	private String createdAt;
+
+	@Schema(description = "更新时间")
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
+	private String updatedAt;
+}

+ 28 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/FeedbackVo.java

@@ -0,0 +1,28 @@
+package com.sckw.ai.biz.pojo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "反馈信息")
+public class FeedbackVo {
+    @Schema(description = "来源", example = "user")
+    private String fromSource;
+    @Schema(description = "更新时间", example = "user")
+    private String updatedAt;
+    private String fromEndUserId;
+    @Schema(description = "会话id")
+    private String conversationId;
+    @Schema(description = "点赞或点踩")
+    private String rating;
+    @Schema(description = "创建时间", example = "user")
+    private String createdAt;
+    private String messageId;
+    @Schema(description = "id")
+    private String id;
+    @Schema(description = "应用id")
+    private String appId;
+    private Object fromAccountId;
+    @Schema(description = "内容", example = "速度慢")
+    private String content;
+}

+ 17 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/FilesItem.java

@@ -0,0 +1,17 @@
+package com.sckw.ai.biz.pojo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "文件列表")
+public class FilesItem{
+	@Schema(description = "传递方式 remote_url: 文件地址 local_file:上传文件")
+	private String transferMethod;
+	@Schema(description = "文件类型 txt,jpg,video,jpeg等值")
+	private String type;
+	@Schema(description = "文件地址")
+	private String url;
+}

+ 483 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/Global.java

@@ -0,0 +1,483 @@
+package com.sckw.ai.biz.pojo;
+
+
+/**
+ * All rights Reserved, Designed By www.51wph.com
+ *
+ * @version V1.0
+ * @ProjectName: wph-platform
+ * @Package com.wph.common.consts
+ * @Description:全局配置类/系统常用常量
+ * @author: zk
+ * @date: 2019-05-28 17:31
+ * @Copyright: 2019 www.51wph.com Inc. All rights reserved.
+ * 注意:本内容仅限于汇链科技有限公司内部传阅,禁止外泄以及用于其他的商业目的
+ */
+public class Global {
+
+    /**
+     * pc-background token有效期为2小时
+     */
+    public static final int PC_BACKGROUND_TOKEN_EXPIRE = 60 * 60 * 2;
+
+    /**
+     * app token有效期为30天
+     */
+    public static final int APP_TOKEN_EXPIRE = 30 * 24 * 60 * 60;
+
+    /**pc token有效期为2小时*/
+    public static final int PC_TOKEN_EXPIRE = 2 * 60 * 60;
+
+    /**通用有效期为2小时*/
+    public static final int COMMON_EXPIRE = 2 * 60 * 60;
+
+    /**
+     * API调用token有效时间
+     */
+    public static final int API_EXPIRE = 120 * 60;
+
+    /**保存全局属性值*/
+//    private static Map<String, String> map = Maps.newHashMap();
+
+    /**
+     * 显示/隐藏
+     */
+    public static final int SHOW = 1;
+    public static final int HIDE = 0;
+    public static final int MINUS_ONE = -1;
+    /**
+     * 正常/删除
+     */
+    public static final int DELETED = 1;
+    public static final int UN_DELETED = 0;
+    /**
+     * 是/否
+     */
+    public static final int YES = 1;
+    public static final int NO = 0;
+    public static final String BE = "是";
+    public static final String NAY = "否";
+
+    /**
+     * 对/错
+     */
+    public static final boolean TRUE = true;
+    public static final boolean FALSE = false;
+
+    /**
+     * 字符
+     */
+    public static final String CODING_GBK = "GBK";
+    public static final String CODING_UTF8 = "UTF-8";
+
+    /**
+     * 消息强弱提醒 强提醒:Y,否则:N
+     */
+    public static final String Y = "Y";
+    public static final String N = "N";
+
+    /**
+     * 初始金额
+     */
+    public static final Double AMOUNT = 0.0;
+
+    /**
+     * 初始数值
+     */
+    public static final int NUMERICAL_ZERO = 0;
+    public static final int NUMERICAL_ONE = 1;
+    public static final int NUMERICAL_TWO = 2;
+    public static final int NUMERICAL_THREE = 3;
+    public static final int NUMERICAL_FOUR = 4;
+    public static final int NUMERICAL_EIGHT = 8;
+    public static final int NUMERICAL_TWELVE = 12;
+    public static final int NUMERICAL_SIXTEEN = 16;
+
+    /**
+     * pid等于0的
+     */
+    public static final String PID = "0";
+
+    /**
+     * or
+     */
+    public static final String OR = "OR";
+
+    /**
+     * and
+     */
+    public static final String AND = "AND";
+
+    /**
+     * ok
+     */
+    public static final String OK = "OK";
+
+    /**
+     * 忘记密码获取验证码
+     */
+    public static final String FORGET_SMS = "forget:";
+
+    /**
+     * 忘记密码获取验证码
+     */
+    public static final String REPEAT_SUBMIT = "repeatSubmit:";
+
+    /**
+     * 用户登录验证码
+     */
+    public static final String USER_LOGIN_CAPTCHA = "userLoginCaptcha:";
+
+    /**
+     * 请求头用户信息键名
+     */
+    public static final String USER_INFO_STR_ENCODE = "userInfoStrEncode";
+
+    /**
+     * 请求头企业信息键名
+     */
+    public static final String ENT_INFO_STR_ENCODE = "entInfoStrEncode";
+
+    /**
+     * redis用户信息前缀
+     */
+    public static final String REDIS_USER_LOGIN_PREFIX = "userLoginInfo:";
+    public static final String REDIS_USER_PREFIX = "userInfo:";
+    public static final String REDIS_USER_TOKEN_PREFIX = "userToken:";
+    //客户经理redisKey
+    public static final String REDIS_CUSTOMER_MANAGER_USER_LOGIN_PREFIX = "customerManagerUserLoginInfo:";
+
+    /**
+     * redis企业信息前缀
+     */
+    public static final String REDIS_ENTERPRISE_PREFIX = "enterpriseInfo:";
+
+    /**
+     * redis区域信息前缀
+     */
+    public static final String REDIS_AREA_PREFIX = "areaInfo:";
+
+    /**
+     * redis区域信息前缀
+     */
+    public static final String REDIS_GROUP_AREA_PREFIX = "areaInfo:group:";
+
+    /**
+     * redis用户菜单信息前缀
+     */
+    public static final String REDIS_SYS_MENU_PREFIX = "sysMenu:";
+
+    /**
+     * redis字典信息前缀
+     */
+    public static final String REDIS_SYS_DICT_TYPE_PREFIX = "dictInfo:type:";
+    public static final String REDIS_SYS_DICT_PREFIX = "dictInfo:dict:";
+    public static final String REDIS_SYS_DICT_GROUP_PREFIX = "dictInfo:group:";
+
+    /**
+     * redis用户车辆gps信息
+     */
+    public static final String REDIS_CACHE_GPS_CAR_PREFIX = "cacheGpsCar:";
+
+    /**
+     * redis运营活动奖品前缀
+     */
+    public static final String REDIS_ACTIVITY_PRIZE_PREFIX = "activityPrize:";
+
+    /**
+     * redis用户运营活动抽奖信息前缀
+     */
+    public static final String ACTIVITY_JOIN = "activityJoin:";
+
+    /**
+     * redis用户运营活动信息前缀
+     */
+    public static final String ACTIVITY = "activity:";
+
+    /**
+     * 汇链用户专场编号
+     */
+    public static final String PHONE = "400-030-5677";
+
+    /**
+     * 系统初始密码
+     */
+    public static final String PASSWORD = "123456";
+
+    /**
+     * 逗号-英文
+     */
+    public static final String COMMA = ",";
+
+    /**
+     * 逗号-中文
+     */
+    public static final String COMMA1 = ",";
+    /**
+     * 右斜杠
+     */
+    public static final String RIGHT_SLASH = "/";
+
+    /**
+     * 点
+     */
+    public static final String DOT = ".";
+
+    /**
+     * 冒号
+     */
+    public static final String COLON = ":";
+
+    /**
+     * 下划线拼接
+     */
+    public static final char UNDERLINE = '_';
+
+    /**
+     * 空字符串
+     */
+    public static final String EMPTY_STRING = "";
+
+    /**
+     * 等号
+     **/
+    public static final String EQUAL_SIGN = "=";
+
+    /**
+     * 美元符号
+     **/
+    public static final String DOLLAR = "$";
+
+    /**
+     * +符号
+     **/
+    public static final String PLUS = "+";
+
+    /**
+     * -符号
+     */
+    public static final String MINUS_SIGN = "-";
+
+    /**
+     * #符号
+     **/
+    public static final String POUND = "#";
+
+    /**
+     * 下划线拼接
+     */
+    public static final String BRACKET = "()";
+    public static final String BRACKET_LEFT = "(";
+    public static final String BRACKET_RIGHT = ")";
+
+    /**
+     * 大括号
+     */
+    public static final String BIG_BRACKETS = "{}";
+    public static final String BIG_BRACKETS_LEFT = "{";
+    public static final String BIG_BRACKETS_RIGHT = "}";
+
+    /**
+     * 星号拼接
+     */
+    public static final String ASTERISK1 = "*";
+    public static final String ASTERISK2 = "**";
+
+    /**
+     * 括号
+     */
+    public static final String BRACKETS = "[]";
+    public static final String BRACKETS_LEFT = "[";
+    public static final String BRACKETS_RIGHT = "]";
+
+    /**
+     * 排序
+     */
+    public static final String ASC = "asc";
+    public static final String DESC = "desc";
+
+    /**
+     * 请求成功值
+     */
+    public static final int SUCCEED = 200;
+
+    /**
+     * 短信请求返回参数名
+     */
+    public static final String SUCCESS = "success";
+
+    /**
+     * pageNum当前页数/pageSize每页显示条数
+     */
+    public static final String PAGE_NUM = "pageNum";
+    public static final String PAGE_SIZE = "pageSize";
+
+    /**
+     * 可调用EncryUtil.generatorPriKey()方法生成
+     */
+    public static final String PRI_KEY = "127f0400ff8a8b7a20d91dfc60a39725";
+
+    /**
+     * APP
+     */
+    public static final String APP = "APP";
+
+    /**
+     * API
+     */
+    public static final String API = "API";
+
+    /**
+     * API初始版本号
+     */
+    public static final String API_VERSION = "1.0.0";
+
+    /**
+     * 请求头token
+     */
+    public static final String ACCESS_TOKEN = "Access-Token";
+
+    /**
+     * 客户终端类型
+     */
+    public static final String CLIENT_TYPE = "Client-Type";
+
+    /**
+     * 系统类型
+     */
+    public static final String SYSTEM_TYPE = "System-Type";
+
+    /**
+     * 请求代理
+     */
+    public static final String USER_AGENT = "user-agent";
+
+    /**
+     * 请求格式
+     */
+    public static final String CONTENT_TYPE_AJ = "application/json";
+
+    /**
+     * 请求方法
+     */
+    public static final String METHOD_POST = "POST";
+    public static final String METHOD_GET = "GET";
+
+    /**
+     * 错误图片
+     */
+    public static final String WRONG_USER_HEAD = "data:,";
+
+    /**
+     * 运输订单号前缀
+     */
+    public static final String TRAN_NUM_PREFIX = "LTO";
+
+    /**
+     * 委托单单号前缀
+     */
+    public static final String ENTR_NUM_PREFIX = "LD";
+
+    /**
+     * 车辆运单单号前缀
+     */
+    public static final String TASK_NUM_PREFIX = "LTL";
+
+    /**
+     * 装货地点
+     */
+    public static final Integer ADDRESS_LOAD = 1;
+
+    /**
+     * 卸货地点
+     */
+    public static final Integer ADDRESS_UNLOAD = 2;
+
+    /**
+     * 时间周期-天/周/月/年
+     */
+    public static final String CYCLE_DAY = "天";
+    public static final String CYCLE_WEEK = "周";
+    public static final String CYCLE_MONTH = "月";
+    public static final String CYCLE_YEAR = "年";
+
+    /**
+     * 重量单位
+     */
+    public static final String UNIT_TON = "吨";
+    public static final String UNIT_KG = "千克";
+
+    /**
+     * 未读
+     */
+    public static final Integer UN_READ = 0;
+    /**
+     * 管理员角色名
+     */
+    public static final String MANAGE_NAME = "系统管理员";
+
+    /**
+     * 管理员机构名
+     */
+    public static final String MANAGE_DEPT_NAME = "系统管理员部门";
+
+    /**
+     * 已读
+     */
+    public static final Integer READ = 1;
+
+
+    /**
+     * 完整的用户登录信息key
+     */
+    public static String getFullUserLoginKey(Integer systemType, Long userId, String clientType) {
+        return REDIS_USER_LOGIN_PREFIX + systemType + COLON + userId + COLON + clientType;
+    }
+
+    /**
+     * 完整的用户登录信息key
+     */
+    public static String getFullUserLoginKey(Integer systemType, Long userId) {
+        return REDIS_USER_LOGIN_PREFIX + systemType + COLON + userId;
+    }
+
+//    /**
+//     * 完整的用户登录tokenkey
+//     */
+//    public static String getFullUserTokenKey(String clientType, Long userId) {
+//        String builder = REDIS_USER_TOKEN_PREFIX + (StringUtils.isNotNull(clientType) ? clientType + COLON : "") + (Objects.nonNull(userId) ? userId : "");
+//        return builder;
+//    }
+
+    /**
+     * 完整的用户企业信息key
+     */
+    public static String getFullUserEntKey(Long entId) {
+        return REDIS_ENTERPRISE_PREFIX + entId;
+    }
+
+    /**
+     * 完整的用户菜单信息key
+     */
+    public static String getFullUserMenuKey(Integer systemType, Long userId) {
+        return REDIS_SYS_MENU_PREFIX + systemType + COLON + userId;
+    }
+
+    /**
+     * 完整的忘记密码获取验证码key
+     */
+    public static String getForgetKey(Integer systemType, String account) {
+        return FORGET_SMS + systemType + COLON + account;
+    }
+
+    /**
+     * 完整的校验重复请求key
+     */
+    public static String getRepeatSubmitKey(Long userId, String url) {
+        return REPEAT_SUBMIT + userId + COLON + url;
+    }
+
+    public static String getCustomerManagerUserLoginKey(Integer systemType, Long userId) {
+        return REDIS_CUSTOMER_MANAGER_USER_LOGIN_PREFIX + systemType + COLON + userId;
+    }
+}

+ 38 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/MessageVo.java

@@ -0,0 +1,38 @@
+package com.sckw.ai.biz.pojo;
+
+import com.sckw.ai.biz.pojo.dto.FeedBackDto;
+import com.sckw.ai.biz.pojo.dto.MessageFileDto;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@Schema(description = "消息")
+public class MessageVo {
+    @Schema(description = "消息ID")
+    private String id;
+    @Schema(description = "会话id")
+    private String conversationId;
+
+    @Schema(description = "用户输入参数")
+    private Object inputs;
+
+    @Schema(description = "用户输入 / 提问内容")
+    private String query;
+
+    @Schema(description = "消息文件")
+    private List<MessageFileDto> messageFiles;
+
+    @Schema(description = "回答消息内容")
+    private String answer;
+
+    @Schema(description = "创建时间")
+    private String createdAt;
+
+    @Schema(description = "反馈信息")
+    private FeedBackDto feedback;
+
+}

+ 44 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/OrderDto.java

@@ -0,0 +1,44 @@
+package com.sckw.ai.biz.pojo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-02 19:41:55
+ */
+@Getter
+@Setter
+public class OrderDto {
+    private Integer status;
+    private String statusLabel;
+    @JsonProperty("tOrderNo")
+    private String tOrderNo;
+    /**
+     * 采购企业
+     */
+    private String buyEntName;
+    /**
+     * 供应企业
+     */
+    private String sellEntName;
+    private String goodsName;
+    private String goodsType;
+    private String goodsTypeLabel;
+    private BigDecimal unitPrice;
+    private Integer unit;
+    private String unitLabel;
+    private BigDecimal amount;
+    private BigDecimal price;
+    private BigDecimal loadAmount;
+    private BigDecimal unloadAmount;
+    private Integer chargeType;
+    private String chargeTypeLabel;
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+}

+ 23 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/ApiPage.java

@@ -0,0 +1,23 @@
+package com.sckw.ai.biz.pojo.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-08 09:14:26
+ */
+@Getter
+@Setter
+@Schema(name = "分页对象")
+public class ApiPage<T> {
+    @Schema(description = "是否还有更多")
+    private boolean hasMore;
+    @Schema(description = "返回条数")
+    private int limit;
+    @Schema(description = "返回条数")
+    private List<T> data;
+}

+ 34 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/ConversionDto.java

@@ -0,0 +1,34 @@
+package com.sckw.ai.biz.pojo.dto;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "会话列表")
+public class ConversionDto {
+    @Schema(description = "id")
+    private String id;
+
+    @Schema(description = "会话名称")
+    private String name;
+
+    @Schema(description = "用户输入参数")
+    private Object inputs;
+
+    @Schema(description = "会话状态")
+    private String status;
+
+    @Schema(description = "开场白")
+    private String introduction;
+
+    @JSONField(name = "created_at")
+    @Schema(description = "创建时间")
+    private Long createdAt;
+
+    @Schema(description = "更新时间")
+    @JSONField(name = "updated_at")
+    private Long updatedAt;
+}

+ 18 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/FeedBackDto.java

@@ -0,0 +1,18 @@
+package com.sckw.ai.biz.pojo.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-08 10:18:00
+ */
+@Getter
+@Setter
+@Schema(description = "反馈")
+public class FeedBackDto {
+    @Schema(description = "点赞 like / 点踩 dislike")
+    private String rating ;
+
+}

+ 35 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/MessageDto.java

@@ -0,0 +1,35 @@
+package com.sckw.ai.biz.pojo.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@Schema(description = "消息列表")
+public class MessageDto {
+    @Schema(description = "消息ID")
+    private String id;
+    @Schema(description = "会话id")
+    private String conversationId;
+
+    @Schema(description = "用户输入参数")
+    private Object inputs;
+
+    @Schema(description = "用户输入 / 提问内容")
+    private String query;
+
+    @Schema(description = "消息文件")
+    private List<MessageFileDto> messageFiles;
+
+    @Schema(description = "回答消息内容")
+    private String answer;
+
+    @Schema(description = "创建时间")
+    private Long createdAt;
+
+    @Schema(description = "反馈信息")
+    private FeedBackDto feedback;
+}

+ 20 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/dto/MessageFileDto.java

@@ -0,0 +1,20 @@
+package com.sckw.ai.biz.pojo.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-08 10:18:00
+ */
+@Getter
+@Setter
+public class MessageFileDto {
+    @Schema(description = "消息ID")
+    private String id;
+    @Schema(description = "类型")
+    private String type;
+    @Schema(description = "文件预览地址,使用文件预览 API")
+    private String url;
+}

+ 41 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/ChatPara.java

@@ -0,0 +1,41 @@
+package com.sckw.ai.biz.pojo.para;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.sckw.ai.biz.pojo.FilesItem;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+@Getter
+@Setter
+@Schema(description = "聊天参数")
+public class ChatPara implements Serializable {
+    @Serial
+    private static final long serialVersionUID = -621415158085036877L;
+    @Schema(description = "允许传入 App 定义的各变量值", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+    private JSONObject inputs;
+
+    @Schema(description = "会话id,需要基于之前的聊天记录继续对话,必须传之前消息的 conversation_id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+    @JsonProperty("conversation_id")
+    private String conversationId;
+
+    @NotBlank(message = "提问不能为空")
+    @Schema(description = "用户输入/提问内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好")
+    private String query;
+
+    @Schema(description = "文件列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+    private List<FilesItem> files;
+
+    @Schema(description = "用户id", hidden = true)
+    private String user;
+
+    @Schema(description = "响应模式", defaultValue = "streaming", requiredMode = Schema.RequiredMode.REQUIRED)
+    @JsonProperty("response_mode")
+    private String responseMode = "streaming";
+}

+ 14 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/ConversionPara.java

@@ -0,0 +1,14 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "查询会话列表参数")
+public class ConversionPara {
+    @Schema(description = "当前页最后面一条记录的 ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+    private String lastId;
+
+}

+ 21 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/FeedbackPara.java

@@ -0,0 +1,21 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "消息反馈参数")
+public class FeedbackPara {
+    @Schema(description = "消息id", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "消息id不能为空")
+    private String messageId;
+    @Schema(description = "操作 点赞 like, 点踩 dislike", requiredMode = Schema.RequiredMode.REQUIRED, example = "like")
+    @NotBlank(message = "操作不能为空")
+    private String rating;
+    @Schema(description = "反馈内容", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "响应较慢")
+    private String content;
+
+}

+ 16 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/MessagePara.java

@@ -0,0 +1,16 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "查询历史消息参数")
+public class MessagePara {
+    @Schema(description = "会话id", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
+    @NotBlank(message = "会话id不能为空")
+    private String conversionId;
+
+}

+ 21 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/OrderPara.java

@@ -0,0 +1,21 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-02 19:41:55
+ */
+@Getter
+@Setter
+@Schema(description = "贸易订单查询参数")
+public class OrderPara {
+    @Schema(description = "开始时间")
+    private String startTime;
+    @Schema(description = "结束时间")
+    private String endTime;
+    @Schema(description = "用户id")
+    private Long userId;
+}

+ 31 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/PagePara.java

@@ -0,0 +1,31 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author xucaiqin
+ * @date 2023-07-07 16:58:41
+ */
+@Getter
+@Setter
+@Schema(description = "分页查询参数")
+public class PagePara implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1881051859375157819L;
+    /**
+     * 当前页
+     */
+    @Schema(description = "当前页", defaultValue = "1", requiredMode = Schema.RequiredMode.REQUIRED)
+    private int page = 1;
+    /**
+     * 每页数量
+     */
+    @Schema(description = "每页数量", defaultValue = "20", requiredMode = Schema.RequiredMode.REQUIRED)
+    private int pageSize = 20;
+
+}

+ 16 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/pojo/para/StopChatPara.java

@@ -0,0 +1,16 @@
+package com.sckw.ai.biz.pojo.para;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Schema(description = "停止聊天参数")
+public class StopChatPara {
+    @Schema(description = "任务id", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotBlank(message = "任务id不能为空")
+    private String taskId;
+
+}

+ 34 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiApiEnum.java

@@ -0,0 +1,34 @@
+package com.sckw.ai.biz.service;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-04 16:12:59
+ */
+@Getter
+@AllArgsConstructor
+public enum AiApiEnum {
+    NEW_CHAT("/chat-messages", "POST", "创建会话消息"),
+    LIKE_CHAT("/messages/%s/feedbacks", "POST", "消息点赞"),//参数 message_id
+    CHAT_STOP("/chat-messages/%s/stop", "GET", "停止聊天"),//参数 task_id
+    CONVERSION_LIST("/conversations", "GET", "获取当前用户的会话列表,默认返回最近的 20 条"),
+    MESSAGES("/messages", "GET", "滚动加载形式返回历史聊天记录"),
+    DELETE_CONVERSION("/conversations/%s", "DELETE", "删除会话"),//会话id
+    FILE_PREVIEW("/files/%s/preview", "GET", "文件预览"),//文件id
+    FEEDBACKS("/app/feedbacks", "GET", "获取应用的终端用户反馈、点赞。"),
+    ;
+    private final String url;
+    private final String method;
+    private final String name;
+
+    public String formatUrl(Object... args) {
+        if (args.length == 0) {
+            return this.url;
+        }
+        return String.format(this.url, args);
+    }
+
+
+}

+ 88 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiApiInvoker.java

@@ -0,0 +1,88 @@
+package com.sckw.ai.biz.service;
+
+import cn.hutool.core.util.StrUtil;
+import com.sckw.ai.biz.config.ChatProperties;
+import com.sckw.ai.biz.core.exception.BusinessException;
+import com.sckw.ai.biz.util.OkHttpUtils;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-05 09:06:09
+ */
+@Component
+@Slf4j
+public class AiApiInvoker {
+    private static ChatProperties properties;
+    @Resource
+    private ChatProperties chatProperties;
+
+    @PostConstruct
+    public void init() {
+        AiApiInvoker.properties = chatProperties;
+    }
+
+    public static String invoke(AiApiEnum apiEnum, Object[] pathParams, Map<String, String> para) {
+        return invoke(apiEnum, pathParams, null, para);
+    }
+
+    public static String invoke(AiApiEnum apiEnum, Object[] pathParams, String requestBody) {
+        return invoke(apiEnum, pathParams, requestBody, null);
+    }
+
+    /**
+     * 统一调用 AI 接口
+     *
+     * @param apiEnum     API 枚举
+     * @param pathParams  路径参数(用于 %s 替换),可为空
+     * @param requestBody 请求体(POST/PUT 时使用),可为 String 或 Object(需序列化)
+     * @return 响应字符串
+     */
+    public static String invoke(AiApiEnum apiEnum, Object[] pathParams, String requestBody, Map<String, String> para) {
+        String url = pathParams != null && pathParams.length > 0
+                ? apiEnum.formatUrl(pathParams)
+                : apiEnum.getUrl();
+
+        OkHttpUtils builder = OkHttpUtils.builder()
+                .url(properties.getUrl() + url)
+                .addHeader("Authorization", properties.getHeaderPrefix() + " " + properties.getHeader())
+                .addHeader("Content-Type", "application/json");
+
+        String method = apiEnum.getMethod().toUpperCase();
+        try {
+            switch (method) {
+                case "GET" -> {
+                    for (Map.Entry<String, String> map : para.entrySet()) {
+                        builder.addPara(map.getKey(), map.getValue());
+                    }
+                    return builder.get().sync();
+                }
+                case "POST" -> {
+                    if (StrUtil.isNotBlank(requestBody)) {
+                        builder.addBodyJsonStr(requestBody);
+                    }
+                    return builder.post(true).sync();
+                }
+                case "DELETE" -> {
+                    return builder.delete().sync();
+                }
+                case "PUT" -> {
+                    if (StrUtil.isNotBlank(requestBody)) {
+                        builder.addBodyJsonStr(requestBody);
+                    }
+                    return builder.put().sync();
+                }
+                default -> throw new UnsupportedOperationException("Unsupported HTTP method: " + method);
+            }
+        } catch (Exception e) {
+            log.error("调用 AI 接口失败: {} - {}", apiEnum.getName(), e.getMessage(), e);
+            throw new BusinessException("AI API 调用异常: " + e.getMessage(), e);
+        }
+    }
+
+}

+ 13 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/AiGuessService.java

@@ -0,0 +1,13 @@
+package com.sckw.ai.biz.service;
+
+import com.sckw.ai.biz.pojo.AiGuessVo;
+
+import java.util.List;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-05 09:57:23
+ */
+public interface AiGuessService {
+    List<AiGuessVo> randomList(Integer size);
+}

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

@@ -0,0 +1,117 @@
+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.sckw.ai.biz.config.ChatProperties;
+import com.sckw.ai.biz.core.CommonResult;
+import com.sckw.ai.biz.core.web.LoginUserHolder;
+import com.sckw.ai.biz.pojo.ConversionVo;
+import com.sckw.ai.biz.pojo.dto.ApiPage;
+import com.sckw.ai.biz.pojo.dto.ConversionDto;
+import com.sckw.ai.biz.pojo.para.ChatPara;
+import com.sckw.ai.biz.pojo.para.ConversionPara;
+import com.sckw.ai.biz.pojo.para.StopChatPara;
+import com.sckw.core.exception.BusinessException;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-04 15:38:01
+ */
+@Service
+@Slf4j
+public class ChatService {
+    @Resource
+    private ChatProperties chatProperties;
+    private WebClient webClient;
+
+    @PostConstruct
+    public void init() {
+        webClient = WebClient.builder().baseUrl(chatProperties.getUrl()).build();
+    }
+
+    public Flux<String> chat(ChatPara chatPara) {
+        Long userId = LoginUserHolder.getUserId();
+        if (Objects.isNull(userId)) {
+            throw new BusinessException("未登录,请先登录");
+        }
+        chatPara.setUser(String.valueOf(userId));
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("userId",userId);
+        chatPara.setInputs(jsonObject);
+        log.info("开启会话 {}", JSONObject.toJSONString(chatPara));
+        return webClient.post().uri(AiApiEnum.NEW_CHAT.getUrl()) // 后端 SSE 路径
+                .bodyValue(chatPara)
+                .header("Authorization", chatProperties.getHeaderPrefix() + " " + chatProperties.getHeader())
+                .header("Content-Type", "application/json")
+                .retrieve().bodyToFlux(String.class) // 按行或按 chunk 流式读取
+                .doOnNext(data -> log.info("Forwarding: " + data)) // 可选:日志
+                .doOnError(error -> log.error("SSE Proxy Error: " + error.getMessage()));
+    }
+
+    public CommonResult<Object> stopChat(StopChatPara stopChatPara) {
+        return CommonResult.ok("", AiApiInvoker.invoke(AiApiEnum.CHAT_STOP, new Object[]{stopChatPara.getTaskId()}, ""));
+    }
+
+    /**
+     * 获取当前用户的会话列表
+     *
+     * @param conversionPara
+     * @return
+     */
+    public CommonResult<ApiPage<ConversionVo>> conversation(ConversionPara conversionPara) {
+        Long userId = LoginUserHolder.getUserId();
+        if (Objects.isNull(userId)) {
+            throw new BusinessException("未登录,请先登录");
+        }
+        HashMap<String, String> para = new HashMap<>();
+        para.put("user", String.valueOf(userId));
+        if (StrUtil.isNotBlank(conversionPara.getLastId())) {
+            para.put("last_id", conversionPara.getLastId());
+        }
+
+        String invoke = AiApiInvoker.invoke(AiApiEnum.CONVERSION_LIST, new Object[]{}, para);
+        log.info("对话列表 {}", invoke);
+        ApiPage<ConversionDto> res = JSONObject.parseObject(invoke, new TypeReference<>() {
+        });
+        if (CollUtil.isEmpty(res.getData())) {
+            ApiPage<ConversionVo> objectApiPage = new ApiPage<>();
+            objectApiPage.setData(new ArrayList<>());
+            objectApiPage.setLimit(res.getLimit());
+            objectApiPage.setHasMore(res.isHasMore());
+            return CommonResult.ok("", objectApiPage);
+        }
+        List<ConversionVo> collect = res.getData().stream().map(d -> {
+            ConversionVo bean = BeanUtil.toBean(d, ConversionVo.class);
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+                    .withZone(ZoneId.of("Asia/Shanghai")); // 或 ZoneId.systemDefault()
+            String formattedDate = formatter.format(Instant.ofEpochSecond(d.getCreatedAt()));
+            bean.setCreatedAt(formattedDate);
+            String update = formatter.format(Instant.ofEpochSecond(d.getCreatedAt()));
+            bean.setUpdatedAt(update);
+            return bean;
+        }).collect(Collectors.toList());
+        ApiPage<ConversionVo> objectApiPage = new ApiPage<>();
+        objectApiPage.setData(collect);
+        objectApiPage.setLimit(res.getLimit());
+        objectApiPage.setHasMore(res.isHasMore());
+        return CommonResult.ok("", objectApiPage);
+    }
+}

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

@@ -0,0 +1,130 @@
+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.sckw.ai.biz.core.CommonResult;
+import com.sckw.ai.biz.core.exception.BusinessException;
+import com.sckw.ai.biz.core.web.LoginUserHolder;
+import com.sckw.ai.biz.pojo.FeedbackVo;
+import com.sckw.ai.biz.pojo.MessageVo;
+import com.sckw.ai.biz.pojo.dto.ApiPage;
+import com.sckw.ai.biz.pojo.dto.MessageDto;
+import com.sckw.ai.biz.pojo.para.FeedbackPara;
+import com.sckw.ai.biz.pojo.para.MessagePara;
+import com.sckw.ai.biz.pojo.para.PagePara;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-04 15:38:01
+ */
+@Service
+@Slf4j
+public class MessageService {
+
+    /**
+     * 反馈
+     *
+     * @param feedbackPara
+     * @return
+     */
+    public CommonResult<Object> feedbacks(FeedbackPara feedbackPara) {
+        Long userId = LoginUserHolder.getUserId();
+        if (Objects.isNull(userId)) {
+            throw new BusinessException("未登录,请先登录");
+        }
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("content", feedbackPara.getContent());
+        jsonObject.put("rating", feedbackPara.getRating());
+        jsonObject.put("user", userId);
+        return CommonResult.ok("反馈成功", AiApiInvoker.invoke(AiApiEnum.LIKE_CHAT, new Object[]{feedbackPara.getMessageId()}, jsonObject.toJSONString()));
+    }
+
+    /**
+     * 获取当前会话的历史聊天
+     *
+     * @param messagePara
+     * @return
+     */
+    public CommonResult<ApiPage<MessageVo>> list(MessagePara messagePara) {
+        Long userId = LoginUserHolder.getUserId();
+        if (Objects.isNull(userId)) {
+            throw new com.sckw.core.exception.BusinessException("未登录,请先登录");
+        }
+        HashMap<String, String> para = new HashMap<>();
+        if (StrUtil.isNotBlank(messagePara.getConversionId())) {
+            para.put("conversation_id", messagePara.getConversionId());
+        }
+        para.put("user", String.valueOf(userId));
+
+        String invoke = AiApiInvoker.invoke(AiApiEnum.MESSAGES, new Object[]{}, para);
+        ApiPage<MessageDto> res = JSONObject.parseObject(invoke, new TypeReference<>() {
+        });
+        if (CollUtil.isEmpty(res.getData())) {
+            ApiPage<MessageVo> objectApiPage = new ApiPage<>();
+            objectApiPage.setData(new ArrayList<>());
+            objectApiPage.setLimit(res.getLimit());
+            objectApiPage.setHasMore(res.isHasMore());
+            return CommonResult.ok("", objectApiPage);
+        }
+        List<MessageVo> collect = res.getData().stream().map(d -> {
+            MessageVo bean = BeanUtil.toBean(d, MessageVo.class);
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+                    .withZone(ZoneId.of("Asia/Shanghai")); // 或 ZoneId.systemDefault()
+            String formattedDate = formatter.format(Instant.ofEpochSecond(d.getCreatedAt()));
+            bean.setCreatedAt(formattedDate);
+            return bean;
+        }).collect(Collectors.toList());
+        ApiPage<MessageVo> objectApiPage = new ApiPage<>();
+        objectApiPage.setData(collect);
+        objectApiPage.setLimit(res.getLimit());
+        objectApiPage.setHasMore(res.isHasMore());
+        return CommonResult.ok("", objectApiPage);
+    }
+
+    public CommonResult<ApiPage<FeedbackVo>> feedbackList(PagePara pagePara) {
+        Long userId = LoginUserHolder.getUserId();
+        if (Objects.isNull(userId)) {
+            throw new com.sckw.core.exception.BusinessException("未登录,请先登录");
+        }
+        HashMap<String, String> para = new HashMap<>();
+        para.put("page", String.valueOf(pagePara.getPage()));
+        para.put("limit", String.valueOf(pagePara.getPageSize()));
+
+        String invoke = AiApiInvoker.invoke(AiApiEnum.FEEDBACKS, new Object[]{}, para);
+        ApiPage<FeedbackVo> res = JSONObject.parseObject(invoke, new TypeReference<>() {
+        });
+        if (CollUtil.isEmpty(res.getData())) {
+            ApiPage<FeedbackVo> objectApiPage = new ApiPage<>();
+            objectApiPage.setData(new ArrayList<>());
+            objectApiPage.setLimit(res.getLimit());
+            objectApiPage.setHasMore(res.isHasMore());
+            return CommonResult.ok("", objectApiPage);
+        }
+        res.getData().forEach(d -> {
+            LocalDateTime dt = LocalDateTime.parse(d.getCreatedAt());
+            dt.atZone(ZoneOffset.UTC);
+            d.setCreatedAt(dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+
+            LocalDateTime dt2 = LocalDateTime.parse(d.getUpdatedAt());
+            dt2.atZone(ZoneOffset.UTC);
+            d.setUpdatedAt(dt2.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+        });
+        return CommonResult.ok("查询成功", res);
+    }
+}

+ 41 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/service/impl/AiGuessServiceImpl.java

@@ -0,0 +1,41 @@
+package com.sckw.ai.biz.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.sckw.ai.biz.entity.AiGuess;
+import com.sckw.ai.biz.mapper.AiGuessMapper;
+import com.sckw.ai.biz.pojo.AiGuessVo;
+import com.sckw.ai.biz.service.AiGuessService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author xucaiqin
+ * @date 2025-12-05 09:57:23
+ */
+@Service
+@RequiredArgsConstructor
+public class AiGuessServiceImpl implements AiGuessService {
+    private final AiGuessMapper aiGuessMapper;
+
+    @Override
+    public List<AiGuessVo> randomList(Integer size) {
+        if (Objects.isNull(size)) {
+            size = 4;
+        }
+        List<AiGuess> aiGuesses = aiGuessMapper.selectList(new LambdaQueryWrapper<AiGuess>());
+        if (CollUtil.isNotEmpty(aiGuesses)) {
+            Collections.shuffle(aiGuesses);
+            List<AiGuess> aiGuesses1 = RandomUtil.randomEleList(aiGuesses, size);
+            return aiGuesses1.stream().map(d -> BeanUtil.toBean(d, AiGuessVo.class)).collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+}

+ 500 - 0
sckw-ai-biz/src/main/java/com/sckw/ai/biz/util/OkHttpUtils.java

@@ -0,0 +1,500 @@
+package com.sckw.ai.biz.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.sckw.core.utils.UUIDUtils;
+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;
+
+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;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author xcq
+ * @date 2023-02-21 10:42:20
+ **/
+@Slf4j
+public class OkHttpUtils {
+    private static volatile OkHttpClient okHttpClient = null;
+    private static volatile Semaphore semaphore = null;
+    private Map<String, String> headerMap;
+    private Map<String, String> paraMap;
+    private Map<String, String> bodyParaMap;
+    //json字符串
+    private String bodyParaString;
+    private String url;
+    private Request.Builder request;
+
+    /**
+     * 初始化okHttpClient,并且允许https访问
+     */
+    private OkHttpUtils() {
+        if (okHttpClient == null) {
+            synchronized (OkHttpUtils.class) {
+                if (okHttpClient == null) {
+                    TrustManager[] trustManagers = buildTrustManagers();
+                    okHttpClient = new OkHttpClient.Builder()
+                            .connectTimeout(15, TimeUnit.SECONDS)
+                            .writeTimeout(20, TimeUnit.SECONDS)
+                            .readTimeout(20, TimeUnit.SECONDS)
+                            .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
+                            .hostnameVerifier((hostName, session) -> true)
+                            .retryOnConnectionFailure(true)
+                            .build();
+                    addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
+                }
+            }
+        }
+    }
+
+    /**
+     * 用于异步请求时,控制访问线程数,返回结果
+     *
+     * @return
+     */
+    private static Semaphore getSemaphoreInstance() {
+        //只能1个线程同时访问
+        synchronized (OkHttpUtils.class) {
+            if (semaphore == null) {
+                semaphore = new Semaphore(0);
+            }
+        }
+        return semaphore;
+    }
+
+    /**
+     * 创建OkHttpUtils
+     *
+     * @return
+     */
+    public static OkHttpUtils builder() {
+        return new OkHttpUtils();
+    }
+
+    /**
+     * 添加url
+     *
+     * @param url
+     * @return
+     */
+    public OkHttpUtils url(String url) {
+        this.url = url;
+        return this;
+    }
+
+    /**
+     * 添加参数
+     *
+     * @param key   参数名
+     * @param value 参数值
+     * @return
+     */
+    public OkHttpUtils addPara(String key, String value) {
+        if (paraMap == null) {
+            paraMap = new LinkedHashMap<>(16);
+        }
+        paraMap.put(key, value);
+        return this;
+    }
+
+    /**
+     * 添加参数
+     *
+     * @param key   参数名
+     * @param value 参数值
+     * @return
+     */
+    public OkHttpUtils addBodyPara(String key, String value) {
+        if (bodyParaMap == null) {
+            bodyParaMap = new LinkedHashMap<>(16);
+        }
+        bodyParaMap.put(key, value);
+        return this;
+    }
+
+    public OkHttpUtils addBodyJsonStr(String string) {
+        bodyParaString = string;
+        return this;
+    }
+
+    /**
+     * 添加请求头
+     *
+     * @param key   参数名
+     * @param value 参数值
+     * @return
+     */
+    public OkHttpUtils addHeader(String key, String value) {
+        if (headerMap == null) {
+            headerMap = new LinkedHashMap<>(16);
+        }
+        headerMap.put(key, value);
+        return this;
+    }
+
+    /**
+     * 初始化get方法
+     *
+     * @return
+     */
+    public OkHttpUtils get() {
+        request = new Request.Builder().get();
+        request.url(buildUrl());
+        return this;
+    }
+    public OkHttpUtils delete() {
+        request = new Request.Builder().delete();
+        request.url(buildUrl());
+        return this;
+    }
+    public OkHttpUtils put() {
+        request = new Request.Builder().delete();
+        request.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方法
+     *
+     * @param isJsonPost true等于json的方式提交数据,类似postman里post方法的raw
+     *                   false等于普通的表单提交
+     * @return
+     */
+    public OkHttpUtils post(boolean isJsonPost) {
+        RequestBody requestBody;
+        if (isJsonPost) {
+            String json;
+            if (StringUtils.isNotBlank(bodyParaString)) {
+                json = bodyParaString;
+            } else if (!CollectionUtils.isEmpty(bodyParaMap)) {
+                json = JSONObject.toJSONString(bodyParaMap);
+            } else {
+                json = "{}";
+            }
+            log.info("请求参数:{}", json);
+            requestBody = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
+        } else {
+            FormBody.Builder formBody = new FormBody.Builder();
+            if (bodyParaMap != null) {
+                bodyParaMap.forEach(formBody::add);
+            }
+            requestBody = formBody.build();
+        }
+        // params参数
+        request = new Request.Builder().post(requestBody).url(buildUrl());
+        return this;
+    }
+
+    private String buildUrl() {
+        StringBuilder urlBuilder = new StringBuilder(url);
+        if (paraMap != null) {
+            urlBuilder.append("?");
+            try {
+                for (Map.Entry<String, String> entry : paraMap.entrySet()) {
+                    urlBuilder.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8)).
+                            append("=").
+                            append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).
+                            append("&");
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            urlBuilder.deleteCharAt(urlBuilder.length() - 1);
+        }
+        return urlBuilder.toString();
+    }
+
+    /**
+     * 文件下载同步请求
+     *
+     * @return
+     */
+    public byte[] fileSync() {
+        setHeader(request);
+        try (Response response = okHttpClient.newCall(request.build()).execute()) {
+            if (response.isSuccessful()) {
+                assert response.body() != null;
+                return response.body().bytes();
+            }
+            return new byte[0];
+        } catch (IOException e) {
+            e.printStackTrace();
+            return new byte[0];
+        }
+    }
+
+    /**
+     * 同步请求
+     *
+     * @return
+     */
+    public String sync() {
+        setHeader(request);
+        try (Response response = okHttpClient.newCall(request.build()).execute()) {
+            assert response.body() != null;
+            return response.body().string();
+        } catch (IOException e) {
+            e.printStackTrace();
+            return "请求失败:" + e.getMessage();
+        }
+    }
+
+    /**
+     * 异步请求,有返回值
+     */
+    public String async() {
+        StringBuilder buffer = new StringBuilder();
+        setHeader(request);
+        okHttpClient.newCall(request.build()).enqueue(new Callback() {
+            @Override
+            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+                assert response.body() != null;
+                buffer.append(response.body().string());
+                getSemaphoreInstance().release();
+            }
+
+            @Override
+            public void onFailure(@NotNull Call call, @NotNull IOException e) {
+                buffer.append("请求出错:").append(e.getMessage());
+
+            }
+        });
+        try {
+            getSemaphoreInstance().acquire();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * 异步请求,带有接口回调
+     *
+     * @param callBack
+     */
+    public void async(ICallBack callBack) {
+        setHeader(request);
+        okHttpClient.newCall(request.build()).enqueue(new Callback() {
+
+            @Override
+            public void onFailure(@NotNull Call call, @NotNull IOException e) {
+                callBack.onFailure(call, e.getMessage());
+            }
+
+            @Override
+            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+                assert response.body() != null;
+                callBack.onSuccessful(call, response.body().string());
+            }
+        });
+    }
+
+    /**
+     * 为request添加请求头
+     *
+     * @param request
+     */
+    private void setHeader(Request.Builder request) {
+        if (headerMap != null) {
+            try {
+                for (Map.Entry<String, String> entry : headerMap.entrySet()) {
+                    request.addHeader(entry.getKey(), entry.getValue());
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+    /**
+     * 生成安全套接字工厂,用于https请求的证书跳过
+     *
+     * @return
+     */
+    private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustAllCerts) {
+        SSLSocketFactory ssfFactory = null;
+        try {
+            SSLContext sc = SSLContext.getInstance("SSL");
+            sc.init(null, trustAllCerts, new SecureRandom());
+            ssfFactory = sc.getSocketFactory();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ssfFactory;
+    }
+
+    private static TrustManager[] buildTrustManagers() {
+        return new TrustManager[]{
+                new X509TrustManager() {
+                    @Override
+                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
+                    }
+
+                    @Override
+                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
+                    }
+
+                    @Override
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return new X509Certificate[]{};
+                    }
+                }
+        };
+    }
+
+    /**
+     * 自定义一个接口回调
+     */
+    public interface ICallBack {
+
+        void onSuccessful(Call call, String data);
+
+        void onFailure(Call call, String errorMsg);
+
+    }
+
+
+    /**
+     * 使用示例
+     *
+     * @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);
+            }
+        }
+    }
+
+
+    /**
+     * 封装请求头
+     *
+     * @param paramsHeads
+     * @param httpMethod
+     */
+    private static void packageHeader(Map<String, String> paramsHeads, HttpRequestBase httpMethod) {
+        if (null != paramsHeads && paramsHeads.size() > 0) {
+            Set<Map.Entry<String, String>> entrySet = paramsHeads.entrySet();
+            for (Map.Entry<String, String> entry : entrySet) {
+                httpMethod.setHeader(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+}

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

@@ -27,3 +27,9 @@ spring:
           - dataId: sckw-common.yml
             group: sckw-ng-service-platform
             refresh: true
+
+chat:
+  url: http://10.10.10.120/v1
+  header: app-TW6xYVuiz1aCCFGiT7pQRzL8
+  headerPrefix: Bearer
+

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

@@ -0,0 +1,35 @@
+spring:
+  cloud:
+    nacos:
+      discovery:
+        # 服务注册地址
+        server-addr: @nacos.server@
+        # 命名空间
+        namespace: @nacos.namespace@
+        # 共享配置
+        group: sckw-ng-service-platform
+      config:
+        # 配置中心地址
+        server-addr: @nacos.server@
+        # 命名空间
+        namespace: @nacos.namespace@
+        # 共享配置
+        group: sckw-ng-service-platform
+        # 配置文件格式
+        file-extension: yaml
+        shared-configs:
+          - data-id: sckw-common.yml
+            group: sckw-ng-common
+            refresh: true
+
+        #可以读多个配置文件 需要在同一个命名空间下面可以是不同的组
+        extension-configs:
+          - dataId: sckw-common.yml
+            group: sckw-ng-service-platform
+            refresh: true
+
+chat:
+  url: http://10.10.10.120/v1
+  header: app-TW6xYVuiz1aCCFGiT7pQRzL8
+  headerPrefix: Bearer
+

+ 19 - 0
sckw-ai-biz/src/main/resources/mapper/AiGuessMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sckw.ai.biz.mapper.AiGuessMapper">
+  <resultMap id="BaseResultMap" type="com.sckw.ai.biz.entity.AiGuess">
+    <!--@mbg.generated-->
+    <!--@Table ai_guess-->
+    <id column="id" jdbcType="BIGINT" property="id" />
+    <result column="question" jdbcType="VARCHAR" property="question" />
+    <result column="create_by" jdbcType="BIGINT" property="createBy" />
+    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
+    <result column="update_by" jdbcType="BIGINT" property="updateBy" />
+    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
+    <result column="del_flag" jdbcType="INTEGER" property="delFlag" />
+  </resultMap>
+  <sql id="Base_Column_List">
+    <!--@mbg.generated-->
+    id, question, create_by, create_time, update_by, update_time, del_flag
+  </sql>
+</mapper>