Administrator
5 hours ago 0325d413502474062e1d400df319bfd390e94956
feat(ai): 新增 AI 陪练相关功能

- 新增 AiMemberAnswerService 接口及实现类,用于处理成员答案相关操作
- 新增 AiMemberPointService 接口及实现类,用于处理成员积分相关操作
- 新增 AiMemberService 接口及实现类,用于处理成员信息相关操作
- 新增 AiMemberTalkService 接口及实现类,用于处理成员对话相关操作
- 新增 AiMemberTalkStreamService接口及实现类,用于处理成员对话流相关操作
- 新增 AiProductCategoryService 接口及实现类,用于处理产品分类相关操作
- 新增 AiServiceImpl 类,用于实现 AI 服务相关功能
29 files modified
6 files added
3 files renamed
763 ■■■■ changed files
src/main/java/cc/mrbird/febs/ai/controller/TestController.java 8 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/member/ApiMemberController.java 34 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java 16 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/productCategory/ApiProductCategoryController.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java 17 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/entity/AiMemberTalk.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamPageDto.java 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamPracticeDto.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamStudyDto.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/AiTalkAnswerStreamDto.java 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/ApiMemberTalkReportSavaDto.java 27 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/productCategory/ApiProductCategoryPageDto.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamPageVo.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamPracticeVo.java 26 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamStudyVo.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkMemberAnswerSavaVo.java 17 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkStreamVoOld.java 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/memberTalkStream/ApiMemberTalkStreamVo.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberPointService.java 7 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberService.java 10 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java 4 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java 4 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkStreamService.java 4 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductCategoryService.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberPointServiceImpl.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberServiceImpl.java 205 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java 7 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java 24 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkStreamServiceImpl.java 115 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductCategoryServiceImpl.java 23 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java 18 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/strategy/param/LlmStrategyDto.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/vo/MallMemberVo.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/TestController.java
@@ -4,7 +4,7 @@
import cc.mrbird.febs.ai.enumerates.AiPromptEnum;
import cc.mrbird.febs.ai.mapper.AiMemberMapper;
import cc.mrbird.febs.ai.req.talk.AiTalkAnswerStream;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVoOld;
import cc.mrbird.febs.ai.service.AiService;
import cc.mrbird.febs.ai.strategy.enumerates.LlmStrategyEnum;
import cc.mrbird.febs.ai.strategy.LlmStrategyFactory;
@@ -106,7 +106,7 @@
    @ApiOperation("提问AI(流式)V2")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answer-streamV2")
    public Flux<FebsResponse> answerStreamV2(@RequestBody @Validated AiTalkAnswerStream dto) {
@@ -146,7 +146,7 @@
    @ApiOperation("提问AI(流式)V3")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answerStreamV3")
    public Flux<FebsResponse> answerStreamV3(@RequestBody @Validated AiTalkAnswerStream dto) {
@@ -229,7 +229,7 @@
    @ApiOperation("提问AI(非流式响应)V4")
    @ApiResponses({
            @ApiResponse(code = 200, message = "非流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "非流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answerStreamV4")
    public FebsResponse answerStreamV4(@RequestBody @Validated AiTalkAnswerStream dto) {
src/main/java/cc/mrbird/febs/ai/controller/member/ApiMemberController.java
@@ -1,9 +1,11 @@
package cc.mrbird.febs.ai.controller.member;
import cc.mrbird.febs.ai.req.member.ApiMemberPageDto;
import cc.mrbird.febs.ai.req.memberPoint.ApiMemberPointDto;
import cc.mrbird.febs.ai.res.member.ApiMemberPageVo;
import cc.mrbird.febs.ai.res.memberAnswer.ApiMemberAnswerVoV2;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPageDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPracticeDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamStudyDto;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamPageVo;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamPracticeVo;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamStudyVo;
import cc.mrbird.febs.ai.service.AiMemberService;
import cc.mrbird.febs.common.entity.FebsResponse;
import io.swagger.annotations.Api;
@@ -33,11 +35,31 @@
    @ApiOperation(value = "我的团队", notes = "我的团队")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiMemberPageVo.class)
            @ApiResponse(code = 200, message = "success", response = ApiMemberTeamPageVo.class)
    })
    @PostMapping(value = "/myTeam")
    public FebsResponse myTeam(@RequestBody @Validated ApiMemberPageDto dto) {
    public FebsResponse myTeam(@RequestBody @Validated ApiMemberTeamPageDto dto) {
        return aiMemberService.myTeam(dto);
    }
    @ApiOperation(value = "练习次数", notes = "练习次数")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiMemberTeamPracticeVo.class)
    })
    @PostMapping(value = "/practice")
    public FebsResponse practice(@RequestBody @Validated ApiMemberTeamPracticeDto dto) {
        return aiMemberService.practice(dto);
    }
    @ApiOperation(value = "学习时长", notes = "学习时长")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiMemberTeamStudyVo.class)
    })
    @PostMapping(value = "/study")
    public FebsResponse study(@RequestBody @Validated ApiMemberTeamStudyDto dto) {
        return aiMemberService.study(dto);
    }
}
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java
@@ -3,7 +3,7 @@
import cc.mrbird.febs.ai.req.memberTalk.*;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkItemVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkReloadVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVoOld;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkVo;
import cc.mrbird.febs.ai.service.AiMemberTalkService;
import cc.mrbird.febs.common.entity.FebsResponse;
@@ -15,9 +15,7 @@
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
/**
 * @author Administrator
@@ -106,7 +104,7 @@
    // 修改接口定义
    @ApiOperation("开始AI对话(流式)")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @GetMapping("/answer-stream")
    public Flux<FebsResponse> answerStream(@RequestParam String question) {
src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkStreamController.java
@@ -3,6 +3,8 @@
import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkItemPageDto;
import cc.mrbird.febs.ai.req.memberTalkStream.*;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkItemVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkMemberAnswerSavaVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVoOld;
import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkReloadStreamVo;
import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkStreamVo;
import cc.mrbird.febs.ai.service.AiMemberTalkStreamService;
@@ -58,6 +60,9 @@
    }
    @ApiOperation(value = "保存用户回答", notes = "保存答案")
    @ApiResponses({
            @ApiResponse(code = 200, message = "success", response = ApiMemberTalkMemberAnswerSavaVo.class)
    })
    @PostMapping(value = "/saveMemberAnswer")
    public FebsResponse saveMemberAnswer(@RequestBody @Validated ApiMemberTalkMemberAnswerSavaDto dto) {
@@ -66,7 +71,7 @@
    @ApiOperation("回答(流式)")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answer")
    public Flux<FebsResponse> answer(@RequestBody @Validated AiTalkAnswerStreamDto dto) {
@@ -78,7 +83,7 @@
    @ApiOperation("回答(流式)")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answerV2")
    public Flux<FebsResponse> answerV2(@RequestBody @Validated AiTalkAnswerStreamDto dto) {
@@ -95,4 +100,11 @@
        return aiMemberTalkStreamService.saveAnswer(dto);
    }
    @ApiOperation(value = "保存AI陪练总结报告", notes = "保存AI陪练总结报告")
    @PostMapping(value = "/saveReport")
    public FebsResponse saveReport(@RequestBody @Validated ApiMemberTalkReportSavaDto dto) {
        return aiMemberTalkStreamService.saveReport(dto);
    }
}
src/main/java/cc/mrbird/febs/ai/controller/productCategory/ApiProductCategoryController.java
@@ -73,6 +73,6 @@
    @PostMapping(value = "/list")
    public FebsResponse list(@RequestBody @Validated ApiProductCategoryPageDto dto) {
        return aiProductCategoryService.categoryList(dto);
        return aiProductCategoryService.categoryChildList(dto);
    }
}
src/main/java/cc/mrbird/febs/ai/controller/talk/ApiAiTalkController.java
@@ -1,20 +1,14 @@
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.AiTalkAnswerStream;
import cc.mrbird.febs.ai.req.talk.ApiTalkDto;
import cc.mrbird.febs.ai.req.talk.ApiTalkItemPageDto;
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.memberTalk.ApiMemberTalkStreamVoOld;
import cc.mrbird.febs.ai.res.talk.ApiTalkItemPageVo;
import cc.mrbird.febs.ai.res.talk.ApiTalkPageVo;
import cc.mrbird.febs.ai.res.talk.ApiTalkQuestionVo;
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 cn.hutool.core.util.StrUtil;
@@ -23,10 +17,7 @@
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
@@ -83,7 +74,7 @@
    @ApiOperation("提问AI(流式)")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @GetMapping("/answer-stream")
    public Flux<FebsResponse> answerStream(@RequestParam String question) {
@@ -93,7 +84,7 @@
    @ApiOperation("提问AI(流式)V2")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answer-streamV2")
    public Flux<FebsResponse> answerStreamV2(@RequestBody @Validated AiTalkAnswerStream dto) {
@@ -105,7 +96,7 @@
    @ApiOperation("提问AI(流式带思考过程)V3")
    @ApiResponses({
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVo.class),
            @ApiResponse(code = 200, message = "流式响应", response = ApiMemberTalkStreamVoOld.class),
    })
    @PostMapping("/answer-streamV3")
    public Flux<FebsResponse> answerStreamV3(@RequestBody @Validated AiTalkAnswerStream dto) {
src/main/java/cc/mrbird/febs/ai/entity/AiMemberTalk.java
@@ -28,4 +28,24 @@
     * 用户ID (UUID)
     */
    private String memberId;
    /**
     * 状态 0-进行中 1-已结束
     */
    private Integer state;
    /**
     * 分析结果
     */
    private String analysis;
    /**
     * 题目数量
     */
    private Integer questionCnt;
    /**
     * 作答数量
     */
    private Integer doneCnt;
}
src/main/java/cc/mrbird/febs/ai/enumerates/AiTypeEnum.java
@@ -10,6 +10,17 @@
public enum AiTypeEnum {
    /**
     * ai陪练的消息记录
     */
    MESSAGES(1,"MESSAGES"),
    AI_MEMBER_ANSWER_STATE_ING(0,"题目练习进行中"),
    AI_MEMBER_ANSWER_STATE_DONE(1,"题目练习已完成"),
    AI_MEMBER_TALK_STATE_ING(0,"AI陪练进行中"),
    AI_MEMBER_TALK_STATE_DONE(1,"AI陪练已完成"),
    /**
     * 1:用户提问
     * 2:AI回答
     */
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamPageDto.java
File was renamed from src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberPageDto.java
@@ -7,8 +7,8 @@
import javax.validation.constraints.NotNull;
@Data
@ApiModel(value = "ApiMemberPageDto", description = "参数")
public class ApiMemberPageDto {
@ApiModel(value = "ApiMemberTeamPageDto", description = "参数")
public class ApiMemberTeamPageDto {
    @NotNull(message = "页码不能为空")
    @ApiModelProperty(value = "页码", example = "1")
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamPracticeDto.java
New file
@@ -0,0 +1,16 @@
package cc.mrbird.febs.ai.req.member;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel(value = "ApiMemberTeamPracticeDto", description = "参数")
public class ApiMemberTeamPracticeDto {
    @NotBlank(message = "用户ID不能为空")
    @ApiModelProperty(value = "用户ID")
    private String memberUuid;
}
src/main/java/cc/mrbird/febs/ai/req/member/ApiMemberTeamStudyDto.java
New file
@@ -0,0 +1,16 @@
package cc.mrbird.febs.ai.req.member;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel(value = "ApiMemberTeamStudyDto", description = "参数")
public class ApiMemberTeamStudyDto {
    @NotBlank(message = "用户ID不能为空")
    @ApiModelProperty(value = "用户ID")
    private String memberUuid;
}
src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/AiTalkAnswerStreamDto.java
@@ -12,6 +12,10 @@
public class AiTalkAnswerStreamDto {
    @ApiModelProperty(value = "状态:1-总结 0-回答问题", example = "10")
    private Integer state;
    @ApiModelProperty(value = "类型 1:亮点 2:建议 3:参考答案 4:知识点总结", example = "10")
    private Integer type;
src/main/java/cc/mrbird/febs/ai/req/memberTalkStream/ApiMemberTalkReportSavaDto.java
New file
@@ -0,0 +1,27 @@
package cc.mrbird.febs.ai.req.memberTalkStream;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(value = "ApiMemberTalkReportSavaDto", description = "参数")
public class ApiMemberTalkReportSavaDto {
    /**
     * 用户对话ID (UUID)
     */
    @NotBlank(message = "会话ID不能为空")
    @ApiModelProperty(value = "会话ID", example = "10")
    private String memberTalkId;
    @NotBlank(message = "回复内容不能为空")
    @ApiModelProperty(value = "回复内容", example = "10")
    private String content;
    @NotNull(message = "类型ID不能为空")
    @ApiModelProperty(value = "类型 1:亮点 2:建议 3:参考答案 4:知识点总结", example = "10")
    private Integer type;
}
src/main/java/cc/mrbird/febs/ai/req/productCategory/ApiProductCategoryPageDto.java
@@ -14,15 +14,6 @@
@ApiModel(value = "ApiProductCategoryPageDto", description = "参数")
public class ApiProductCategoryPageDto {
    @NotNull(message = "页码不能为空")
    @ApiModelProperty(value = "页码", example = "1")
    private Integer pageNow;
    @NotNull(message = "每页数量不能为空")
    @ApiModelProperty(value = "每页数量", example = "10")
    private Integer pageSize;
    @NotBlank(message = "父分类不能为空")
    @ApiModelProperty(value = "父分类ID", example = "123")
    private String parentId;
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamPageVo.java
File was renamed from src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberPageVo.java
@@ -5,11 +5,14 @@
import lombok.Data;
@Data
@ApiModel(value = "ApiMemberPageVo", description = "参数")
public class ApiMemberPageVo {
@ApiModel(value = "ApiMemberTeamPageVo", description = "参数")
public class ApiMemberTeamPageVo {
    @ApiModelProperty(value = "用户ID")
    private String memberUuid;
    @ApiModelProperty(value = "类型 1-本人 0-不是本人")
    private Integer type;
    @ApiModelProperty(value = "微信名")
    private String nickName;
@@ -20,6 +23,6 @@
    @ApiModelProperty(value = "联系次数")
    private Integer practiceCnt;
    @ApiModelProperty(value = "学习时长")
    private Integer studyTime;
    @ApiModelProperty(value = "学习时长(分钟)")
    private String studyTime;
}
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamPracticeVo.java
New file
@@ -0,0 +1,26 @@
package cc.mrbird.febs.ai.res.member;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel(value = "ApiMemberTeamPracticeVo", description = "参数")
public class ApiMemberTeamPracticeVo {
    @ApiModelProperty(value = "名称")
    private String name;
    @ApiModelProperty(value = "目标")
    private String target;
    @ApiModelProperty(value = "题目练习次数")
    private Integer answerCnt;
    @ApiModelProperty(value = "AI陪练次数")
    private Integer talkCnt;
}
src/main/java/cc/mrbird/febs/ai/res/member/ApiMemberTeamStudyVo.java
New file
@@ -0,0 +1,18 @@
package cc.mrbird.febs.ai.res.member;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "ApiMemberTeamStudyVo", description = "参数")
public class ApiMemberTeamStudyVo {
    @ApiModelProperty(value = "名称")
    private String title;
    @ApiModelProperty(value = "总时长分钟")
    private String totalTime;
}
src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkMemberAnswerSavaVo.java
New file
@@ -0,0 +1,17 @@
package cc.mrbird.febs.ai.res.memberTalk;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(value = "ApiMemberTalkMemberAnswerSavaVo", description = "参数")
public class ApiMemberTalkMemberAnswerSavaVo {
    @ApiModelProperty(value = "题目数量")
    private Integer questionCnt;
    @ApiModelProperty(value = "作答数量")
    private Integer doneCnt;
}
src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkStreamVoOld.java
File was renamed from src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkStreamVo.java
@@ -8,8 +8,8 @@
 * @author Administrator
 */
@Data
@ApiModel(value = "ApiMemberTalkStreamVo", description = "参数")
public class ApiMemberTalkStreamVo {
@ApiModel(value = "ApiMemberTalkStreamVoOld", description = "参数")
public class ApiMemberTalkStreamVoOld {
    @ApiModelProperty(value = "消息")
    private String content;
src/main/java/cc/mrbird/febs/ai/res/memberTalkStream/ApiMemberTalkStreamVo.java
@@ -36,4 +36,10 @@
    @ApiModelProperty(value = "内容亮点、建议、参考答案、核心知识点雷达图表数据(数据对象)")
    private String report;
    @ApiModelProperty(value = "题目数量")
    private Integer questionCnt;
    @ApiModelProperty(value = "作答数量")
    private Integer doneCnt;
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java
@@ -36,4 +36,8 @@
    FebsResponse myWork(ApiMemberProductWorkPageDto dto);
    void updateMemberAnswerUpdateTime(String memberAnswerId, Date updateTime);
    List<AiMemberAnswer> getListByCompanyId(String companyId);
    List<AiMemberAnswer> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberPointService.java
@@ -5,14 +5,17 @@
import cc.mrbird.febs.common.entity.FebsResponse;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface AiMemberPointService extends IService<AiMemberPoint> {
    AiMemberPoint getById(String id);
    AiMemberPoint add(String memberUuid, String productPointId, String companyId, Integer totalTime);
    FebsResponse saveTime(ApiMemberPointDto dto);
    List<AiMemberPoint> getListByCompanyId(String companyId);
    List<AiMemberPoint> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberService.java
@@ -1,7 +1,9 @@
package cc.mrbird.febs.ai.service;
import cc.mrbird.febs.ai.entity.AiMember;
import cc.mrbird.febs.ai.req.member.ApiMemberPageDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPageDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPracticeDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamStudyDto;
import cc.mrbird.febs.common.entity.FebsResponse;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -9,5 +11,9 @@
    AiMember getById(String id);
    FebsResponse myTeam(ApiMemberPageDto dto);
    FebsResponse myTeam(ApiMemberTeamPageDto dto);
    FebsResponse practice(ApiMemberTeamPracticeDto dto);
    FebsResponse study(ApiMemberTeamStudyDto dto);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java
@@ -24,7 +24,9 @@
    AiMemberTalkItem add(String memberUuid, String id, String companyId, int type, String resContext, Date createdTime);
    AiMemberTalkItem getByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery);
    AiMemberTalkItem getOneByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery);
    List<AiMemberTalkItem> getListByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery);
    IPage<ApiMemberTalkItemVo> historyPage(ApiMemberTalkItemPageDto dto);
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java
@@ -35,11 +35,13 @@
    FebsResponse answer(ApiMemberTalkAnswerDto dto);
    AiMemberTalk add(String memberUuid,String companyId, String productId, Date nowTime);
    AiMemberTalk add(String memberUuid,String companyId, String productId, Date nowTime,Integer questionCount);
    FebsResponse historyPage(ApiMemberTalkItemPageDto dto);
    Flux<FebsResponse> answerStream(String question);
    List<AiMemberTalk> getListByCompanyId(String companyId);
    List<AiMemberTalk> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkStreamService.java
@@ -29,5 +29,7 @@
    FebsResponse saveAnswer(ApiMemberTalkAnswerSavaDto dto);
    void updateMemberTalkUpdateTime(String memberTalkId, Date updateTime);
    void updateMemberTalkUpdateTime(Integer state,Integer doneCnt,String memberTalkId, Date updateTime);
    FebsResponse saveReport(ApiMemberTalkReportSavaDto dto);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductCategoryService.java
@@ -31,7 +31,7 @@
    FebsResponse hotV2(ApiProductCategoryHotDto dto);
    FebsResponse categoryList(ApiProductCategoryPageDto dto);
    FebsResponse categoryChildList(ApiProductCategoryPageDto dto);
    String getDefaultProductCategoryId();
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java
@@ -1,6 +1,7 @@
package cc.mrbird.febs.ai.service.impl;
import cc.mrbird.febs.ai.entity.*;
import cc.mrbird.febs.ai.enumerates.AiTypeEnum;
import cc.mrbird.febs.ai.mapper.AiMemberAnswerMapper;
import cc.mrbird.febs.ai.req.memberAnswer.*;
import cc.mrbird.febs.ai.res.memberAnswer.*;
@@ -406,4 +407,23 @@
                .eq(AiMemberAnswer::getId,memberAnswerId)
        );
    }
    @Override
    public List<AiMemberAnswer> getListByCompanyId(String companyId) {
        return aiMemberAnswerMapper.selectList(
                Wrappers.lambdaQuery(AiMemberAnswer.class)
                .eq(AiMemberAnswer::getCompanyId, companyId)
                .eq(AiMemberAnswer::getState, AiTypeEnum.AI_MEMBER_ANSWER_STATE_DONE.getCode())
        );
    }
    @Override
    public List<AiMemberAnswer> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid) {
        return aiMemberAnswerMapper.selectList(
                Wrappers.lambdaQuery(AiMemberAnswer.class)
                        .eq(AiMemberAnswer::getCompanyId, companyId)
                        .eq(AiMemberAnswer::getMemberId, memberUuid)
                        .eq(AiMemberAnswer::getState, AiTypeEnum.AI_MEMBER_ANSWER_STATE_DONE.getCode())
        );
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberPointServiceImpl.java
@@ -15,6 +15,7 @@
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Slf4j
@Service
@@ -51,7 +52,7 @@
        AiMemberPoint aiMemberPoint = this.getById(productPointId);
        if (ObjectUtil.isNull(aiMemberPoint)){
            aiMemberPoint = this.add(memberUuid, productPointId, companyId, totalTime);
            this.add(memberUuid, productPointId, companyId, totalTime);
        }else{
            Integer oldTotalTime = aiMemberPoint.getTotalTime();
            aiMemberPointMapper.update(
@@ -64,4 +65,21 @@
        }
        return new FebsResponse().success();
    }
    @Override
    public List<AiMemberPoint> getListByCompanyId(String companyId) {
        return aiMemberPointMapper.selectList(
                Wrappers.lambdaQuery(AiMemberPoint.class)
                .eq(AiMemberPoint::getCompanyId, companyId)
        );
    }
    @Override
    public List<AiMemberPoint> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid) {
        return aiMemberPointMapper.selectList(
                Wrappers.lambdaQuery(AiMemberPoint.class)
                        .eq(AiMemberPoint::getCompanyId, companyId)
                        .eq(AiMemberPoint::getMemberId, memberUuid)
        );
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberServiceImpl.java
@@ -1,17 +1,22 @@
package cc.mrbird.febs.ai.service.impl;
import cc.mrbird.febs.ai.entity.AiMember;
import cc.mrbird.febs.ai.entity.AiMemberTalk;
import cc.mrbird.febs.ai.entity.*;
import cc.mrbird.febs.ai.mapper.AiMemberMapper;
import cc.mrbird.febs.ai.req.member.ApiMemberPageDto;
import cc.mrbird.febs.ai.res.member.ApiMemberPageVo;
import cc.mrbird.febs.ai.service.AiMemberService;
import cc.mrbird.febs.ai.service.AiMemberTalkService;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPageDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamPracticeDto;
import cc.mrbird.febs.ai.req.member.ApiMemberTeamStudyDto;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamPageVo;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamPracticeVo;
import cc.mrbird.febs.ai.res.member.ApiMemberTeamStudyVo;
import cc.mrbird.febs.ai.res.productPoint.ApiProductPointListVo;
import cc.mrbird.febs.ai.service.*;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.utils.LoginUserUtil;
import cc.mrbird.febs.mall.entity.MallMember;
import cc.mrbird.febs.mall.mapper.MallMemberMapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -20,10 +25,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -37,16 +39,25 @@
    private final AiMemberMapper aiMemberMapper;
    private final MallMemberMapper mallMemberMapper;
    private final AiMemberTalkService aiMemberTalkService;
    private final AiMemberAnswerService aiMemberAnswerService;
    private final AiMemberPointService aiMemberPointService;
    private final AiProductService aiProductService;
    private final AiProductPointService aiProductPointService;
    @Override
    public AiMember getById(String id) {
        return aiMemberMapper.selectById( id);
    }
    @Override
    public FebsResponse myTeam(ApiMemberPageDto dto) {
        List<ApiMemberPageVo> objects = new ArrayList<>();
    public FebsResponse myTeam(ApiMemberTeamPageDto dto) {
        List<ApiMemberTeamPageVo> objects = new ArrayList<>();
        String companyId = LoginUserUtil.getLoginUser().getCompanyId();
        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
        Integer checkOrder = LoginUserUtil.getLoginUser().getCheckOrder();
        if (1 != checkOrder){
            throw new FebsException("非管理员,无权限查看");
        }
        // 创建分页对象,传入当前页和每页大小
        Page<AiMember> page = new Page<>(dto.getPageNow(), dto.getPageSize());
@@ -56,12 +67,12 @@
        Page<AiMember> pageListByQuery = aiMemberMapper.selectPage(page, queryWrapper);
        List<AiMember> records = pageListByQuery.getRecords();
        if (CollUtil.isNotEmpty( records)){
            objects = buildMemberPages(companyId,records,objects);
            objects = buildMemberPages(memberUuid,companyId,records,objects);
        }
        return new FebsResponse().success().data(objects);
    }
    private List<ApiMemberPageVo> buildMemberPages(String companyId,List<AiMember> records,List<ApiMemberPageVo> objects) {
    private List<ApiMemberTeamPageVo> buildMemberPages(String memberUuid, String companyId, List<AiMember> records, List<ApiMemberTeamPageVo> objects) {
        /**
         * 获取用户信息
         */
@@ -78,24 +89,176 @@
        Map<String, MallMember> mallMemberMap = mallMembers.stream().collect(Collectors.toMap(MallMember::getMemberUuid, mallMember -> mallMember));
        /**
         * 获取用户联系次数
         * 获取用户练习次数
         *  ai陪练
         *  ai答题
         */
        List<AiMemberTalk> aiMemberTalks = aiMemberTalkService.getListByCompanyId(companyId);
        //Stream流操作aiMemberTalks,获取一个map<memberUuid,aiMemberTalk>的对象
        //stream流操作aiMemberTalks,获取一个Map<memberId,Integer>的对象,integer代表按照memberId分组的数量
        Map<String, Integer> memberIdToCntMapTalk = aiMemberTalks.stream()
                .collect(Collectors.groupingBy(
                        AiMemberTalk::getMemberId,
                        Collectors.summingInt(t -> 1)
                ));
        List<AiMemberAnswer> aiMemberAnswers = aiMemberAnswerService.getListByCompanyId(companyId);
        //stream流操作aiMemberAnswers,获取一个Map<memberId,Integer>的对象,integer代表按照memberId分组的数量
        Map<String, Integer> memberIdToCntMapAnswer = aiMemberAnswers.stream()
                .collect(Collectors.groupingBy(
                        AiMemberAnswer::getMemberId,
                        Collectors.summingInt(a -> 1)
                ));
        /**
         * 用户学习总时长
         */
        List<AiMemberPoint> aiMemberPoints = aiMemberPointService.getListByCompanyId(companyId);
        Map<String, Integer> memberStudyTimeMap = aiMemberPoints.stream().collect(Collectors.groupingBy(
                AiMemberPoint::getMemberId,
                Collectors.summingInt(AiMemberPoint::getTotalTime)
        ));
        for (AiMember aiMember : records){
            ApiMemberPageVo apiMemberPageVo = new ApiMemberPageVo();
            apiMemberPageVo.setMemberUuid(aiMember.getId());
            String aiMemberId = aiMember.getId();
            ApiMemberTeamPageVo apiMemberTeamPageVo = new ApiMemberTeamPageVo();
            apiMemberTeamPageVo.setType(0);
            apiMemberTeamPageVo.setMemberUuid(aiMemberId);
            if (memberUuid.equals(aiMemberId)){
                apiMemberTeamPageVo.setType(1);
            }
            //判断mallMemberMap中是否存在该会员
            apiMemberPageVo.setMemberName(mallMemberMap.containsKey(aiMember.getId()) ? mallMemberMap.get(aiMember.getId()).getName() : "");
            apiMemberPageVo.setNickName(mallMemberMap.containsKey(aiMember.getId()) ? mallMemberMap.get(aiMember.getId()).getName() : "");
            apiMemberTeamPageVo.setNickName(mallMemberMap.containsKey(aiMemberId) ? mallMemberMap.get(aiMemberId).getName() : "");
            apiMemberTeamPageVo.setMemberName(mallMemberMap.containsKey(aiMemberId) ? mallMemberMap.get(aiMemberId).getName() : "");
            Integer practiceCnt = 0;
            if (memberIdToCntMapTalk.containsKey(aiMemberId)){
                practiceCnt = memberIdToCntMapTalk.get(aiMemberId);
            }
            if (memberIdToCntMapAnswer.containsKey(aiMemberId)){
                practiceCnt = memberIdToCntMapAnswer.get(aiMemberId) + practiceCnt;
            }
            apiMemberTeamPageVo.setPracticeCnt(practiceCnt);
            objects.add(apiMemberPageVo);
            Integer studyTime = 0;
            if (memberStudyTimeMap.containsKey(aiMemberId)){
                studyTime = memberStudyTimeMap.get(aiMemberId);
            }
            apiMemberTeamPageVo.setStudyTime(DateUtil.secondToTime(studyTime));
            objects.add(apiMemberTeamPageVo);
        }
        return objects;
    }
    @Override
    public FebsResponse practice(ApiMemberTeamPracticeDto dto) {
        List<ApiMemberTeamPracticeVo> vos = new ArrayList<>();
        String companyId = LoginUserUtil.getLoginUser().getCompanyId();
        String memberUuid = dto.getMemberUuid();
        List<AiMemberTalk> aiMemberTalks = aiMemberTalkService.getListByCompanyIdAndMemberUuid(companyId, memberUuid);
        Map<String, List<AiMemberTalk>> aiMemberTalkMap = new HashMap<>();
        if (CollUtil.isNotEmpty(aiMemberTalks)){
            //stream操作aiMemberTalks,返回一个根据productId分组的集合
            aiMemberTalkMap = aiMemberTalks.stream().collect(Collectors.groupingBy(AiMemberTalk::getProductId));
        }
        List<AiMemberAnswer> aiMemberAnswers = aiMemberAnswerService.getListByCompanyIdAndMemberUuid(companyId, memberUuid);
        Map<String, List<AiMemberAnswer>> aiMemberAnswerMap = new HashMap<>();
        if (CollUtil.isNotEmpty(aiMemberAnswers)){
            aiMemberAnswerMap = aiMemberAnswers.stream().collect(Collectors.groupingBy(AiMemberAnswer::getProductId));
        }
        // 获取aiMemberTalkMap和aiMemberAnswerMap的全部的key,并且去重
        Set<String> productIds = new HashSet<>();
        if (aiMemberTalkMap != null) {
            productIds.addAll(aiMemberTalkMap.keySet());
        }
        if (aiMemberAnswerMap != null) {
            productIds.addAll(aiMemberAnswerMap.keySet());
        }
        if (CollUtil.isEmpty(productIds)){
            return new FebsResponse().success().data(vos);
        }
        List<AiProduct> aiProducts = aiProductService.getProductListByQuery(
                Wrappers.lambdaQuery(AiProduct.class)
                        .select(AiProduct::getId,AiProduct::getName, AiProduct::getTarget)
                .in(AiProduct::getId, productIds)
        );
        if (CollUtil.isEmpty(aiProducts)){
            return new FebsResponse().success().data(vos);
        }
        for (AiProduct aiProduct : aiProducts){
            String productId = aiProduct.getId();
            ApiMemberTeamPracticeVo vo = new ApiMemberTeamPracticeVo();
            vo.setName(aiProduct.getName());
            vo.setTarget(aiProduct.getTarget());
            Integer answerCnt = 0;
            if (aiMemberAnswerMap.containsKey(productId)){
                answerCnt = aiMemberAnswerMap.get(productId).size();
            }
            vo.setAnswerCnt(answerCnt);
            Integer talkCnt = 0;
            if (aiMemberTalkMap.containsKey(productId)){
                talkCnt = aiMemberTalkMap.get(productId).size();
            }
            vo.setTalkCnt(talkCnt);
            vos.add( vo);
        }
        return new FebsResponse().success().data(vos);
    }
    @Override
    public FebsResponse study(ApiMemberTeamStudyDto dto) {
        List<ApiMemberTeamStudyVo> vos = new ArrayList<>();
        String companyId = LoginUserUtil.getLoginUser().getCompanyId();
        String memberUuid = dto.getMemberUuid();
        List<AiMemberPoint> aiMemberPoints = aiMemberPointService.getListByCompanyIdAndMemberUuid(companyId, memberUuid);
        if (CollUtil.isEmpty(aiMemberPoints)){
            return new FebsResponse().success().data(vos);
        }
        Map<String, AiMemberPoint> aiMemberPointMap = new HashMap<>();
        if (CollUtil.isNotEmpty(aiMemberPoints)){
            aiMemberPoints.forEach(aiMemberPoint -> aiMemberPointMap.put(aiMemberPoint.getProductPointId(),aiMemberPoint));
        }
        //stream流操作aiMemberPoints,返回一个productPointId的set集合
        Set<String> productPointIdSet = aiMemberPoints.stream().map(AiMemberPoint::getProductPointId).collect(Collectors.toSet());
        List<AiProductPoint> aiProductPoints = aiProductPointService.getBaseMapper().selectList(
                Wrappers.lambdaQuery(AiProductPoint.class)
                        .select(AiProductPoint::getId,AiProductPoint::getTitle)
                        .in(AiProductPoint::getId, productPointIdSet)
        );
        if (CollUtil.isEmpty(aiProductPoints)){
            return new FebsResponse().success().data(vos);
        }
        for (AiProductPoint aiProductPoint : aiProductPoints){
            ApiMemberTeamStudyVo vo = new ApiMemberTeamStudyVo();
            vo.setTitle(aiProductPoint.getTitle());
            Integer totalTime = 0;
            if (aiMemberPointMap.containsKey(aiProductPoint.getId())){
                totalTime = aiMemberPointMap.get(aiProductPoint.getId()).getTotalTime();
            }
            vo.setTotalTime(DateUtil.secondToTime(totalTime));
            vos.add(vo);
        }
        return new FebsResponse().success().data(vos);
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java
@@ -53,11 +53,16 @@
    }
    @Override
    public AiMemberTalkItem getByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery) {
    public AiMemberTalkItem getOneByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery) {
        return aiMemberTalkItemMapper.selectOne(memberTalkItemQuery);
    }
    @Override
    public List<AiMemberTalkItem> getListByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery) {
        return aiMemberTalkItemMapper.selectList(memberTalkItemQuery);
    }
    @Override
    public IPage<ApiMemberTalkItemVo> historyPage(ApiMemberTalkItemPageDto dto) {
        // 创建分页对象,传入当前页和每页大小
        Page<ApiMemberTalkItemVo> page = new Page<>(dto.getPageNow(), dto.getPageSize());
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java
@@ -80,7 +80,7 @@
        query.last("limit 1");
        AiMemberTalk aiMemberTalk = this.getByQuery(query);
        if (ObjectUtil.isNull(aiMemberTalk)){
            aiMemberTalk = this.add(memberUuid,companyId,productId,nowTime);
            aiMemberTalk = this.add(memberUuid,companyId,productId,nowTime,5);
        }
        List<AiMessage> aiMessageDtoList = aiMemberTalkItemService.getQuestionUpDownContext(aiMemberTalk.getId(),AiTypeEnum.QUESTION.getCode());
@@ -112,7 +112,7 @@
        queryWrapper.eq(AiMemberTalkItem::getType,AiTypeEnum.QUESTION_ANSWER.getCode());
        queryWrapper.orderByDesc(AiMemberTalkItem::getCreatedTime);
        queryWrapper.last("limit 1");
        AiMemberTalkItem byQuery = aiMemberTalkItemService.getByQuery(queryWrapper);
        AiMemberTalkItem byQuery = aiMemberTalkItemService.getOneByQuery(queryWrapper);
        if (ObjectUtil.isNotNull(byQuery)){
            apiMemberTalkReloadVo.setContext(byQuery.getContext());
            apiMemberTalkReloadVo.setMemberTalkId(memberTalkId);
@@ -155,7 +155,7 @@
                query.last("limit 1");
                AiMemberTalk aiMemberTalk = this.getByQuery(query);
                if (ObjectUtil.isNull(aiMemberTalk)) {
                    aiMemberTalk = this.add(memberUuid,companyId, productId, nowTime);
                    aiMemberTalk = this.add(memberUuid,companyId, productId, nowTime,5);
                }
                try {
                    if (aiResponse.getCode().equals("200")) {
@@ -236,7 +236,7 @@
        memberTalkItemQuery.eq(AiMemberTalkItem::getType,1);
        memberTalkItemQuery.orderByDesc(AiMemberTalkItem::getCreatedTime);
        memberTalkItemQuery.last("limit 1");
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getByQuery(memberTalkItemQuery);
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getOneByQuery(memberTalkItemQuery);
        aiMemberTalkItemService.add(memberUuid,aiMemberTalk.getId(),companyId,2,reqContext,new Date());
        String format = StrUtil.format(ANSWER_FORMAT, aiMemberTalkItem.getContext(), reqContext);
@@ -268,13 +268,16 @@
        return new FebsResponse().success().data(apiMemberTalkVo);
    }
    @Override
    public AiMemberTalk add(String memberUuid,String companyId, String productId, Date nowTime) {
    public AiMemberTalk add(String memberUuid,String companyId, String productId, Date nowTime,Integer questionCount) {
        AiMemberTalk aiMemberTalk = new AiMemberTalk();
        aiMemberTalk.setId(UUID.getSimpleUUIDString());
        aiMemberTalk.setCompanyId(companyId);
        aiMemberTalk.setCreatedTime(nowTime);
        aiMemberTalk.setMemberId(memberUuid);
        aiMemberTalk.setProductId(productId);
        aiMemberTalk.setQuestionCnt(questionCount);
        aiMemberTalk.setDoneCnt(0);
        aiMemberTalk.setState(AiTypeEnum.AI_MEMBER_TALK_STATE_ING.getCode());
        aiMemberTalkMapper.insert(aiMemberTalk);
        return aiMemberTalk;
    }
@@ -298,6 +301,17 @@
        return aiMemberTalkMapper.selectList(
                Wrappers.lambdaQuery(AiMemberTalk.class)
                .eq(AiMemberTalk::getCompanyId, companyId)
                .eq(AiMemberTalk::getState, AiTypeEnum.AI_MEMBER_TALK_STATE_DONE.getCode())
        );
    }
    @Override
    public List<AiMemberTalk> getListByCompanyIdAndMemberUuid(String companyId, String memberUuid) {
        return aiMemberTalkMapper.selectList(
                Wrappers.lambdaQuery(AiMemberTalk.class)
                        .eq(AiMemberTalk::getCompanyId, companyId)
                        .eq(AiMemberTalk::getMemberId, memberUuid)
                        .eq(AiMemberTalk::getState, AiTypeEnum.AI_MEMBER_TALK_STATE_DONE.getCode())
        );
    }
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkStreamServiceImpl.java
@@ -6,6 +6,7 @@
import cc.mrbird.febs.ai.mapper.AiMemberTalkMapper;
import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkItemPageDto;
import cc.mrbird.febs.ai.req.memberTalkStream.*;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkMemberAnswerSavaVo;
import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkReloadStreamVo;
import cc.mrbird.febs.ai.res.memberTalkStream.ApiMemberTalkStreamVo;
import cc.mrbird.febs.ai.service.*;
@@ -15,6 +16,7 @@
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.utils.LoginUserUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
@@ -25,6 +27,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
@@ -44,6 +47,7 @@
public class AiMemberTalkStreamServiceImpl extends ServiceImpl<AiMemberTalkMapper, AiMemberTalk> implements AiMemberTalkStreamService {
    private final AiMemberTalkMapper aiMemberTalkMapper;
    private final AiProductService aiProductService;
    private final AiProductRoleLinkService aiProductRoleLinkService;
    private final AiProductRoleService aiProductRoleService;
    private final AiMemberTalkService aiMemberTalkService;
@@ -54,6 +58,7 @@
    @Override
    @Transactional
    public FebsResponse start(ApiMemberTalkStreamDto dto) {
        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
@@ -66,16 +71,20 @@
            throw new FebsException("产品AI陪练不存在");
        }
        AiProduct aiProduct = aiProductService.getById(productId);
        Integer questionCount = aiProduct.getQuestionCount();
        String companyId = aiProductRoleLink.getCompanyId();
        Date nowTime = new Date();
        LambdaQueryWrapper<AiMemberTalk> query = Wrappers.lambdaQuery(AiMemberTalk.class);
        query.eq(AiMemberTalk::getMemberId,memberUuid);
        query.eq(AiMemberTalk::getProductId,productId);
        query.eq(AiMemberTalk::getState,AiTypeEnum.AI_MEMBER_TALK_STATE_ING.getCode());
        query.last("limit 1");
        AiMemberTalk aiMemberTalk = aiMemberTalkService.getByQuery(query);
        if (ObjectUtil.isNull(aiMemberTalk)){
            aiMemberTalk = aiMemberTalkService.add(memberUuid,companyId,productId,nowTime);
            aiMemberTalk = aiMemberTalkService.add(memberUuid,companyId,productId,nowTime,questionCount);
        }
        ApiMemberTalkStreamVo apiMemberTalkVo = new ApiMemberTalkStreamVo();
@@ -90,6 +99,8 @@
        apiMemberTalkVo.setMemberTalkId(aiMemberTalk.getId());
        apiMemberTalkVo.setType(1);
        apiMemberTalkVo.setContext(title);
        apiMemberTalkVo.setQuestionCnt(aiMemberTalk.getQuestionCnt());
        apiMemberTalkVo.setDoneCnt(aiMemberTalk.getDoneCnt() + 1);
        return new FebsResponse().success().data(apiMemberTalkVo);
    }
@@ -104,7 +115,7 @@
        queryWrapper.eq(AiMemberTalkItem::getType,AiTypeEnum.QUESTION_ANSWER.getCode());
        queryWrapper.orderByDesc(AiMemberTalkItem::getCreatedTime);
        queryWrapper.last("limit 1");
        AiMemberTalkItem byQuery = aiMemberTalkItemService.getByQuery(queryWrapper);
        AiMemberTalkItem byQuery = aiMemberTalkItemService.getOneByQuery(queryWrapper);
        if (ObjectUtil.isNotNull(byQuery)){
            apiMemberTalkReloadVo.setContext(byQuery.getContext());
            apiMemberTalkReloadVo.setMemberTalkId(memberTalkId);
@@ -121,6 +132,7 @@
    }
    @Override
    @Transactional
    public FebsResponse saveMemberAnswer(ApiMemberTalkMemberAnswerSavaDto dto) {
        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
        String memberTalkId = dto.getMemberTalkId();
@@ -130,11 +142,18 @@
        if (ObjectUtil.isNull(aiMemberTalk)){
            throw new FebsException("对话不存在");
        }
        this.updateMemberTalkUpdateTime(aiMemberTalk.getId(), new Date());
        int doneCnt = aiMemberTalk.getDoneCnt() + 1;
        Integer state = aiMemberTalk.getState();
        Integer questionCnt = aiMemberTalk.getQuestionCnt();
        this.updateMemberTalkUpdateTime(state,doneCnt,aiMemberTalk.getId(), new Date());
        String companyId = aiMemberTalk.getCompanyId();
        aiMemberTalkItemService.add(memberUuid,aiMemberTalk.getId(),companyId,2,content,new Date());
        return new FebsResponse().success();
        ApiMemberTalkMemberAnswerSavaVo apiMemberTalkMemberAnswerSavaVo = new ApiMemberTalkMemberAnswerSavaVo();
        apiMemberTalkMemberAnswerSavaVo.setQuestionCnt(questionCnt);
        apiMemberTalkMemberAnswerSavaVo.setDoneCnt(doneCnt);
        return new FebsResponse().success().data(apiMemberTalkMemberAnswerSavaVo);
    }
    @Override
@@ -169,7 +188,7 @@
        memberTalkItemQuery.eq(AiMemberTalkItem::getType,1);
        memberTalkItemQuery.orderByDesc(AiMemberTalkItem::getCreatedTime);
        memberTalkItemQuery.last("limit 1");
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getByQuery(memberTalkItemQuery);
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getOneByQuery(memberTalkItemQuery);
        String question = aiMemberTalkItem.getContext();
        String prompt = this.buildPrompt(question,reqContext,aiProductRole.getPromptHead(), aiProductRole.getPromptTemplate(), type);
@@ -194,6 +213,7 @@
        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
        Integer type = dto.getType();
        String memberTalkId = dto.getId();
        Integer state = ObjectUtil.defaultIfNull(dto.getState(),0);
        AiMemberTalk aiMemberTalk = this.getById(memberTalkId);
        if (ObjectUtil.isNull(aiMemberTalk)){
            throw new FebsException("对话不存在");
@@ -218,7 +238,7 @@
        memberTalkItemQuery.eq(AiMemberTalkItem::getType,1);
        memberTalkItemQuery.orderByDesc(AiMemberTalkItem::getCreatedTime);
        memberTalkItemQuery.last("limit 1");
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getByQuery(memberTalkItemQuery);
        AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getOneByQuery(memberTalkItemQuery);
        String question = aiMemberTalkItem.getContext();
        String promptHead = aiProductRole.getPromptHead();
@@ -233,9 +253,48 @@
        llmStrategyDtoList.add(llmStrategyDto);
        llmStrategyDto = this.buildLlmStrategyDtoList(String.valueOf(type), 4);
        llmStrategyDtoList.add(llmStrategyDto);
        LlmStrategyDto llmStrategyDtoMessage = buildMessages(state, memberTalkId);
        llmStrategyDtoList.add(llmStrategyDtoMessage);
        String modelName = LlmStrategyEnum.getName(aiService.getSystemSetAiType());
        return llmStrategyFactory.getCalculationStrategyMap().get(modelName).llmInvokeStreamingNoThink(llmStrategyDtoList);
    }
    private LlmStrategyDto buildMessages(Integer state, String memberTalkId) {
        LlmStrategyDto message = new LlmStrategyDto();
        if (1!=  state){
            return message;
        }
        LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery = Wrappers.lambdaQuery(AiMemberTalkItem.class);
        memberTalkItemQuery.eq(AiMemberTalkItem::getMemberTalkId,memberTalkId);
        memberTalkItemQuery.orderByAsc(AiMemberTalkItem::getCreatedTime);
        List<AiMemberTalkItem> aiMemberTalkItems = aiMemberTalkItemService.getListByQuery(memberTalkItemQuery);
        if (CollUtil.isEmpty(aiMemberTalkItems)){
            return message;
        }
        List<LlmStrategyDto> messages = new ArrayList<>();
        for (AiMemberTalkItem aiMemberTalkItem : aiMemberTalkItems){
            LlmStrategyDto llmStrategyDto = new LlmStrategyDto();
            if (aiMemberTalkItem.getType() == 1){
                llmStrategyDto.setRole(Role.ASSISTANT.getValue());
            }
            if (aiMemberTalkItem.getType() == 2){
                llmStrategyDto.setRole(Role.USER.getValue());
            }
            if (aiMemberTalkItem.getType() == 3){
                llmStrategyDto.setRole(Role.ASSISTANT.getValue());
            }
            llmStrategyDto.setContent(aiMemberTalkItem.getContext());
            messages.add(llmStrategyDto);
        }
        message.setRole(AiTypeEnum.MESSAGES.getName());
        message.setMessages(messages);
        return message;
    }
    private String buildPrompt(String question,String answer,String promptHead, String promptTemplate,Integer type){
@@ -280,7 +339,7 @@
            throw new FebsException("对话不存在");
        }
        this.updateMemberTalkUpdateTime(aiMemberTalk.getId(), new Date());
        this.updateMemberTalkUpdateTime(aiMemberTalk.getState(),aiMemberTalk.getDoneCnt(),aiMemberTalk.getId(), new Date());
        String companyId = aiMemberTalk.getCompanyId();
        Integer type = dto.getType();
@@ -312,12 +371,48 @@
    }
    @Override
    public void updateMemberTalkUpdateTime(String memberTalkId, Date updateTime) {
    public void updateMemberTalkUpdateTime(Integer state,Integer doneCnt,String memberTalkId, Date updateTime) {
        aiMemberTalkMapper.update(null,
                Wrappers.lambdaUpdate(AiMemberTalk.class)
                .set(AiMemberTalk::getUpdatedTime,updateTime)
                .eq(AiMemberTalk::getId,memberTalkId)
                        .set(AiMemberTalk::getUpdatedTime,updateTime)
                        .set(AiMemberTalk::getDoneCnt,doneCnt)
                        .set(AiMemberTalk::getState,state)
                        .eq(AiMemberTalk::getId,memberTalkId)
                );
    }
    @Override
    public FebsResponse saveReport(ApiMemberTalkReportSavaDto dto) {
        String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid();
        String memberTalkId = dto.getMemberTalkId();
        String content = dto.getContent();
        AiMemberTalk aiMemberTalk = this.getById(memberTalkId);
        if (ObjectUtil.isNull(aiMemberTalk)){
            throw new FebsException("对话不存在");
        }
        this.updateMemberTalkUpdateTime(AiTypeEnum.AI_MEMBER_TALK_STATE_DONE.getCode(), aiMemberTalk.getDoneCnt(),aiMemberTalk.getId(), new Date());
        Integer type = dto.getType();
        String contentByCode = AiTalkOutputEnum.HIGH_LIGHT.getCodeByType(type);
        String analysis = aiMemberTalk.getAnalysis();
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        if(StrUtil.isEmpty(analysis)){
            stringStringHashMap.put(contentByCode,content);
        }else{
            stringStringHashMap = JSONUtil.toBean(analysis, HashMap.class);
            stringStringHashMap.put(contentByCode,content);
        }
        aiMemberTalkMapper.update(
                null,
                Wrappers.lambdaUpdate(AiMemberTalk.class)
                        .set(AiMemberTalk::getAnalysis,JSONUtil.toJsonStr(stringStringHashMap))
                        .eq(AiMemberTalk::getId,aiMemberTalk.getId())
        );
        return new FebsResponse().success();
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductCategoryServiceImpl.java
@@ -132,11 +132,24 @@
    }
    @Override
    public FebsResponse categoryList(ApiProductCategoryPageDto dto) {
        // 创建分页对象,传入当前页和每页大小
        Page<ApiProductCategoryVo> page = new Page<>(dto.getPageNow(), dto.getPageSize());
        Page<ApiProductCategoryVo> pageListByQuery = this.getPageListByQuery(page, dto);
        return new FebsResponse().success().data(pageListByQuery);
    public FebsResponse categoryChildList(ApiProductCategoryPageDto dto) {
        List<ApiProductCategoryVo> vos = new ArrayList<>();
        List<AiProductCategory> aiProductCategories = aiProductCategoryMapper.selectList(
                Wrappers.lambdaQuery(AiProductCategory.class)
                        .eq(AiProductCategory::getParentId, dto.getParentId())
                        .eq(AiProductCategory::getState, 1)
                .orderByAsc(AiProductCategory::getSort)
        );
        if (CollUtil.isNotEmpty(aiProductCategories)){
            for (AiProductCategory aiProductCategory : aiProductCategories) {
                ApiProductCategoryVo vo = new ApiProductCategoryVo();
                vo.setId(aiProductCategory.getId());
                vo.setName(aiProductCategory.getName());
                vos.add(vo);
            }
        }
        return new FebsResponse().success().data(vos);
    }
    @Override
src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java
@@ -9,7 +9,7 @@
import cc.mrbird.febs.ai.res.ai.AiResponse;
import cc.mrbird.febs.ai.res.ai.RadarDataItem;
import cc.mrbird.febs.ai.res.ai.Report;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVo;
import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkStreamVoOld;
import cc.mrbird.febs.ai.service.AiProductRoleService;
import cc.mrbird.febs.ai.service.AiService;
import cc.mrbird.febs.ai.service.AiTalkItemService;
@@ -433,18 +433,18 @@
                    }
                    ApiMemberTalkStreamVo apiMemberTalkStreamVo = new ApiMemberTalkStreamVo();
                    ApiMemberTalkStreamVoOld apiMemberTalkStreamVoOld = new ApiMemberTalkStreamVoOld();
                    // 判断是否触发深度思考,触发则打印模型输出的思维链内容
                    ChatMessage message = choice.getMessage();
                    if (message.getReasoningContent()!= null &&!message.getReasoningContent().isEmpty()) {
                        apiMemberTalkStreamVo.setReasoningContent(message.getReasoningContent());
                        apiMemberTalkStreamVoOld.setReasoningContent(message.getReasoningContent());
//                        System.out.print(message.getReasoningContent());
                    }
                    String content = message.getContent() == null ? "" : message.getContent().toString();
                    apiMemberTalkStreamVo.setContent(content);
                    apiMemberTalkStreamVoOld.setContent(content);
                    System.out.print(content);
                    return new FebsResponse().success().data(apiMemberTalkStreamVo);
                    return new FebsResponse().success().data(apiMemberTalkStreamVoOld);
                })
                .onErrorResume(throwable -> {
                    log.error("流式调用AI服务失败,问题输入: {}", question, throwable);
@@ -513,12 +513,12 @@
                    }
                    ChatMessage message = choice.getMessage();
                    ApiMemberTalkStreamVo apiMemberTalkStreamVo = new ApiMemberTalkStreamVo();
                    ApiMemberTalkStreamVoOld apiMemberTalkStreamVoOld = new ApiMemberTalkStreamVoOld();
                    // 处理 reasoning content
                    String reasoningContent = message.getReasoningContent();
                    if (StrUtil.isNotEmpty(reasoningContent)) {
                        apiMemberTalkStreamVo.setReasoningContent(reasoningContent);
                        apiMemberTalkStreamVoOld.setReasoningContent(reasoningContent);
                        log.debug("Reasoning Content: {}", reasoningContent);
                    }
@@ -527,11 +527,11 @@
                    if (message.getContent() != null) {
                        content = message.getContent().toString();
                    }
                    apiMemberTalkStreamVo.setContent(content);
                    apiMemberTalkStreamVoOld.setContent(content);
                    System.out.print(content);
                    log.debug("Content: {}", content);
                    return new FebsResponse().success().data(apiMemberTalkStreamVo);
                    return new FebsResponse().success().data(apiMemberTalkStreamVoOld);
                })
                .onErrorResume(throwable -> {
                    log.error("流式调用AI服务失败,问题输入: {}", question, throwable);
src/main/java/cc/mrbird/febs/ai/strategy/Impl/AliApplicationLlmStrategyServiceImpl.java
@@ -1,5 +1,6 @@
package cc.mrbird.febs.ai.strategy.Impl;
import cc.mrbird.febs.ai.enumerates.AiTypeEnum;
import cc.mrbird.febs.ai.strategy.LlmStrategyService;
import cc.mrbird.febs.ai.strategy.enumerates.LlmApplicationAppIdEnum;
import cc.mrbird.febs.ai.strategy.enumerates.LlmStrategyContextEnum;
@@ -53,6 +54,23 @@
            }
        }
        return bizParamsMap;
    }
    private List<Message> getMessages(List<LlmStrategyDto> dto) {
        List<Message> messages = new ArrayList<>();
        for (LlmStrategyDto dtoItem : dto){
            if (StrUtil.equals(AiTypeEnum.MESSAGES.getName(), dtoItem.getRole())){
                List<LlmStrategyDto> messages1 = dtoItem.getMessages();
                for (LlmStrategyDto dtoItem1 : messages1){
                    messages.add(Message.builder()
                            .role(dtoItem1.getRole())
                            .content(dtoItem1.getContent())
                            .build());
                }
                break;
            }
        }
        return messages;
    }
    private String getQuery(List<LlmStrategyDto> dto) {
@@ -160,6 +178,7 @@
            throw new FebsException("百炼工作流初始化异常");
        }
        HashMap prompt = getPrompt(dto);
        List<Message> messages = getMessages(dto);
        String query = getQuery(dto);
        String appId = getAppId(dto);
        if (prompt == null || prompt.size() == 0){
@@ -178,6 +197,7 @@
                .appId(appId) //替换为实际的应用 ID
                .flowStreamMode(FlowStreamMode.MESSAGE_FORMAT)
                .prompt(query)
                .messages( messages)
                .bizParams(JsonUtils.toJsonObject( prompt))
                .build();
src/main/java/cc/mrbird/febs/ai/strategy/param/LlmStrategyDto.java
@@ -17,4 +17,6 @@
    
    private String content;
    private List<LlmStrategyDto> messages;
}
src/main/java/cc/mrbird/febs/mall/vo/MallMemberVo.java
@@ -121,7 +121,7 @@
    @ApiModelProperty(value = "是否是医生")
    private Integer doctorState;
    @ApiModelProperty(value = "是否是核销员 0-否 1-是")
    @ApiModelProperty(value = "是否能查看我的团队 0-否 1-是")
    private Integer checkOrder;
    @ApiModelProperty(value = "是否是自提核销员 0-否 1-是")