From 33a883b0d7edbf633dcf142d2e3c5bf2e334b121 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 25 Aug 2025 16:10:09 +0800
Subject: [PATCH] feat(ai): 添加 AI 灵通相关功能

---
 src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkVo.java                  |   19 +++
 src/main/java/cc/mrbird/febs/ai/entity/AiTalk.java                       |    5 
 src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java  |   28 ++++
 src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java |   68 +++++++++++
 src/main/java/cc/mrbird/febs/ai/mapper/AiTalkMapper.java                 |    7 +
 src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkPageDto.java             |   26 ++++
 src/main/resources/mapper/modules/AiTalkMapper.xml                       |   12 ++
 src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkPageVo.java              |   26 ++++
 src/main/java/cc/mrbird/febs/ai/entity/AiTalkItem.java                   |    2 
 src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java      |   83 +++++++++++++
 src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java           |   11 +
 src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java               |   21 +++
 src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkDto.java                 |   27 ++++
 src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java               |    7 +
 14 files changed, 341 insertions(+), 1 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java b/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
new file mode 100644
index 0000000..a8d47bd
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
@@ -0,0 +1,68 @@
+package cc.mrbird.febs.ai.controller.talk;
+
+import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkAnswerDto;
+import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkDto;
+import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkItemPageDto;
+import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
+import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
+import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkItemVo;
+import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
+import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkVo;
+import cc.mrbird.febs.ai.res.talk.ApiTalkPageVo;
+import cc.mrbird.febs.ai.res.talk.ApiTalkVo;
+import cc.mrbird.febs.ai.service.AiMemberTalkService;
+import cc.mrbird.febs.ai.service.AiTalkService;
+import cc.mrbird.febs.common.entity.FebsResponse;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import reactor.core.publisher.Flux;
+
+import java.io.IOException;
+
+/**
+ * @author Administrator
+ */
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping(value = "/api/ai/talk")
+@Api(value = "ApiAiTalkController", tags = "AI-灵通")
+public class ApiAiTalkController {
+
+    private final AiTalkService aiTalkService;
+
+    @ApiOperation(value = "用户AI问答", notes = "用户AI问答")
+    @ApiResponses({
+            @ApiResponse(code = 200, message = "success", response = ApiTalkVo.class)
+    })
+    @PostMapping(value = "/talkOpen")
+    public FebsResponse talkOpen(@RequestBody @Validated ApiTalkDto dto) {
+
+        return aiTalkService.talkOpen(dto);
+    }
+
+    @ApiOperation(value = "用户AI问答记录分页查询", notes = "用户AI问答记录分页查询")
+    @ApiResponses({
+            @ApiResponse(code = 200, message = "success", response = ApiTalkPageVo.class)
+    })
+    @PostMapping(value = "/talkList")
+    public FebsResponse talkList(@RequestBody @Validated ApiTalkPageDto dto) {
+
+        return aiTalkService.talkList(dto);
+    }
+
+    @ApiOperation("提问AI(流式)")
+    @ApiResponses({
+            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
+    })
+    @GetMapping("/answer-stream")
+    public Flux<FebsResponse> answerStream(@RequestParam String question) {
+
+        return aiTalkService.answerStream(question);
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/entity/AiTalk.java b/src/main/java/cc/mrbird/febs/ai/entity/AiTalk.java
index c804e18..b324bb2 100644
--- a/src/main/java/cc/mrbird/febs/ai/entity/AiTalk.java
+++ b/src/main/java/cc/mrbird/febs/ai/entity/AiTalk.java
@@ -19,6 +19,11 @@
     private String companyId;
 
     /**
+     * 主要问题
+     */
+    private String question;
+
+    /**
      * 用户ID (UUID)
      */
     private String memberId;
diff --git a/src/main/java/cc/mrbird/febs/ai/entity/AiTalkItem.java b/src/main/java/cc/mrbird/febs/ai/entity/AiTalkItem.java
index 15e95d3..8526911 100644
--- a/src/main/java/cc/mrbird/febs/ai/entity/AiTalkItem.java
+++ b/src/main/java/cc/mrbird/febs/ai/entity/AiTalkItem.java
@@ -25,7 +25,7 @@
     /**
      * 用户对话ID (UUID)
      */
-    private String memberTalkId;
+    private String talkId;
 
     /**
      * 类型 1-用户提问 2-AI回答
diff --git a/src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java b/src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java
index d66f6b0..18ae5fd 100644
--- a/src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java
+++ b/src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java
@@ -10,6 +10,13 @@
 public enum AiTypeEnum {
 
     /**
+     * 1:用户提问
+     * 2:AI回答
+     */
+    MEMBER_QUESTION(1,"用户提问"),
+    AI_ANSWER(2,"AI回答"),
+
+    /**
      * 1:AI提问
      * 2:用户回答
      * 3:生成答案解析
diff --git a/src/main/java/cc/mrbird/febs/ai/mapper/AiTalkMapper.java b/src/main/java/cc/mrbird/febs/ai/mapper/AiTalkMapper.java
index 9196dcf..c6d1872 100644
--- a/src/main/java/cc/mrbird/febs/ai/mapper/AiTalkMapper.java
+++ b/src/main/java/cc/mrbird/febs/ai/mapper/AiTalkMapper.java
@@ -1,7 +1,14 @@
 package cc.mrbird.febs.ai.mapper;
 
 import cc.mrbird.febs.ai.entity.AiTalk;
+import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
+import cc.mrbird.febs.ai.res.talk.ApiTalkPageVo;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
 
 public interface AiTalkMapper extends BaseMapper<AiTalk> {
+
+    Page<ApiTalkPageVo> getPageTalkListByQuery(Page<ApiTalkPageVo> page, @Param("record")ApiTalkPageDto dto);
+
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkDto.java b/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkDto.java
new file mode 100644
index 0000000..f5f200e
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkDto.java
@@ -0,0 +1,27 @@
+package cc.mrbird.febs.ai.req.talk;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Administrator
+ */
+@Data
+@ApiModel(value = "ApiTalkDto", description = "参数")
+public class ApiTalkDto {
+
+    @NotBlank(message = "内容不能为空")
+    @ApiModelProperty(value = "内容", example = "10")
+    private String context;
+
+    @NotNull(message = "类型不能为空")
+    @ApiModelProperty(value = "类型 1-用户提问 2-AI回答", example = "10")
+    private Integer type;
+
+    @ApiModelProperty(value = "会话ID", example = "10")
+    private String talkId;
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkPageDto.java b/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkPageDto.java
new file mode 100644
index 0000000..e47db90
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/req/talk/ApiTalkPageDto.java
@@ -0,0 +1,26 @@
+package cc.mrbird.febs.ai.req.talk;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Administrator
+ */
+@Data
+@ApiModel(value = "ApiTalkPageDto", description = "参数")
+public class ApiTalkPageDto {
+
+    @NotNull(message = "页码不能为空")
+    @ApiModelProperty(value = "页码", example = "1")
+    private Integer pageNow;
+
+    @NotNull(message = "每页数量不能为空")
+    @ApiModelProperty(value = "每页数量", example = "10")
+    private Integer pageSize;
+
+    @ApiModelProperty(hidden = true)
+    private String memberUuid;
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkPageVo.java b/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkPageVo.java
new file mode 100644
index 0000000..5526373
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkPageVo.java
@@ -0,0 +1,26 @@
+package cc.mrbird.febs.ai.res.talk;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @author Administrator
+ */
+@Data
+@ApiModel(value = "ApiTalkPageVo", description = "参数")
+public class ApiTalkPageVo {
+
+    @ApiModelProperty(value = "会话ID")
+    private String talkId;
+
+    @ApiModelProperty(value = "题目")
+    private String question;
+
+    @ApiModelProperty(value = "时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkVo.java b/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkVo.java
new file mode 100644
index 0000000..22d61aa
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/res/talk/ApiTalkVo.java
@@ -0,0 +1,19 @@
+package cc.mrbird.febs.ai.res.talk;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author Administrator
+ */
+@Data
+@ApiModel(value = "ApiTalkVo", description = "参数")
+public class ApiTalkVo {
+
+    /**
+     * 视频号 id,以"sph"开头的id,可在视频号助手获取
+     */
+    @ApiModelProperty(value = "会话ID")
+    private String talkId;
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java b/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java
new file mode 100644
index 0000000..229f2b6
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiTalkItemService.java
@@ -0,0 +1,11 @@
+package cc.mrbird.febs.ai.service;
+
+import cc.mrbird.febs.ai.entity.AiTalkItem;
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface AiTalkItemService extends IService<AiTalkItem> {
+
+    void add(String id, int code, String context, String memberUuid, DateTime date);
+
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java b/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java
new file mode 100644
index 0000000..d89047d
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiTalkService.java
@@ -0,0 +1,21 @@
+package cc.mrbird.febs.ai.service;
+
+import cc.mrbird.febs.ai.entity.AiTalk;
+import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
+import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
+import cc.mrbird.febs.common.entity.FebsResponse;
+import com.baomidou.mybatisplus.extension.service.IService;
+import reactor.core.publisher.Flux;
+
+import java.util.Date;
+
+public interface AiTalkService extends IService<AiTalk> {
+
+    FebsResponse talkOpen(ApiTalkDto dto);
+
+    AiTalk add(String memberUuid, String question, Date date);
+
+    Flux<FebsResponse> answerStream(String question);
+
+    FebsResponse talkList(ApiTalkPageDto dto);
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java
new file mode 100644
index 0000000..7a4a121
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkItemServiceImpl.java
@@ -0,0 +1,28 @@
+package cc.mrbird.febs.ai.service.impl;
+
+import cc.mrbird.febs.ai.entity.AiTalkItem;
+import cc.mrbird.febs.ai.mapper.AiTalkItemMapper;
+import cc.mrbird.febs.ai.service.AiTalkItemService;
+import cc.mrbird.febs.ai.utils.UUID;
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AiTalkItemServiceImpl extends ServiceImpl<AiTalkItemMapper, AiTalkItem> implements AiTalkItemService {
+    @Override
+    public void add(String id, int code, String context, String memberUuid, DateTime date) {
+        AiTalkItem aiTalkItem = new AiTalkItem();
+        aiTalkItem.setId(UUID.getSimpleUUIDString());
+        aiTalkItem.setMemberId(memberUuid);
+        aiTalkItem.setType(code);
+        aiTalkItem.setTalkId(id);
+        aiTalkItem.setCreatedTime(date);
+        aiTalkItem.setContext(context);
+        this.baseMapper.insert(aiTalkItem);
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java
new file mode 100644
index 0000000..9410135
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiTalkServiceImpl.java
@@ -0,0 +1,83 @@
+package cc.mrbird.febs.ai.service.impl;
+
+import cc.mrbird.febs.ai.entity.AiTalk;
+import cc.mrbird.febs.ai.mapper.AiTalkMapper;
+import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
+import cc.mrbird.febs.ai.req.talk.ApiTalkPageDto;
+import cc.mrbird.febs.ai.res.memberAnswer.ApiMemberProductWorkVo;
+import cc.mrbird.febs.ai.res.talk.ApiTalkPageVo;
+import cc.mrbird.febs.ai.res.talk.ApiTalkVo;
+import cc.mrbird.febs.ai.service.AiService;
+import cc.mrbird.febs.ai.service.AiTalkItemService;
+import cc.mrbird.febs.ai.service.AiTalkService;
+import cc.mrbird.febs.ai.utils.UUID;
+import cc.mrbird.febs.common.entity.FebsResponse;
+import cc.mrbird.febs.common.utils.LoginUserUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Flux;
+
+import java.util.Date;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AiTalkServiceImpl extends ServiceImpl<AiTalkMapper, AiTalk> implements AiTalkService {
+
+    private final AiTalkItemService aiTalkItemService;
+    private final AiService aiService;
+    @Override
+    public FebsResponse talkOpen(ApiTalkDto dto) {
+        String talkId = dto.getTalkId();
+        String context = dto.getContext();
+        Integer type = dto.getType();
+        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
+
+        AiTalk aiTalk = this.getById(talkId);
+        DateTime date = DateUtil.date();
+        if (StrUtil.isEmpty(talkId)){
+            aiTalk = this.add(memberUuid,context, date);
+        }
+
+        aiTalkItemService.add(aiTalk.getId(), type, context, memberUuid, date);
+
+        ApiTalkVo apiTalkVo = new ApiTalkVo();
+        apiTalkVo.setTalkId(aiTalk.getId());
+        return new FebsResponse().success().data(apiTalkVo);
+    }
+
+    @Override
+    public AiTalk add(String memberUuid, String question, Date date) {
+        AiTalk aiTalk = new AiTalk();
+        aiTalk.setId(UUID.getSimpleUUIDString());
+        aiTalk.setQuestion(question);
+        aiTalk.setCreatedTime(date);
+        aiTalk.setMemberId(memberUuid);
+        this.baseMapper.insert(aiTalk);
+        return aiTalk;
+    }
+
+    @Override
+    public Flux<FebsResponse> answerStream(String question) {
+
+        return aiService.answerStream(question);
+    }
+
+    @Override
+    public FebsResponse talkList(ApiTalkPageDto dto) {
+        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
+        dto.setMemberUuid(memberUuid);
+        // 创建分页对象,传入当前页和每页大小
+        Page<ApiTalkPageVo> page = new Page<>(dto.getPageNow(), dto.getPageSize());
+        Page<ApiTalkPageVo> pageListByQuery = this.getBaseMapper().getPageTalkListByQuery(page, dto);
+
+        return new FebsResponse().success().data(pageListByQuery);
+    }
+
+}
diff --git a/src/main/resources/mapper/modules/AiTalkMapper.xml b/src/main/resources/mapper/modules/AiTalkMapper.xml
index 22f7f0e..d2bdb52 100644
--- a/src/main/resources/mapper/modules/AiTalkMapper.xml
+++ b/src/main/resources/mapper/modules/AiTalkMapper.xml
@@ -1,4 +1,16 @@
 <?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="cc.mrbird.febs.ai.mapper.AiTalkMapper">
+
+
+    <select id="getPageTalkListByQuery" resultType="cc.mrbird.febs.ai.res.talk.ApiTalkPageVo">
+        select
+        a.id as talkId,
+        a.question as question,
+        a.CREATED_TIME as createdTime
+        from ai_talk a
+        where a.member_id = #{record.memberUuid}
+        order by a.CREATED_TIME desc
+    </select>
+
 </mapper>
\ No newline at end of file

--
Gitblit v1.9.1