From f7a1b1996d92d96004a7a1ab1bf289516732c7e4 Mon Sep 17 00:00:00 2001 From: Administrator <15274802129@163.com> Date: Tue, 05 Aug 2025 17:04:20 +0800 Subject: [PATCH] feat(ai): 添加 AI 用户陪练功能 --- src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java | 6 src/main/java/cc/mrbird/febs/mall/controller/HSController.java | 2 src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java | 18 + src/main/java/cc/mrbird/febs/ai/service/AiProductRoleLinkService.java | 5 src/main/java/cc/mrbird/febs/ai/res/ai/AiResponse.java | 16 + src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java | 137 +++++++++++++++ src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java | 54 ++++++ src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleLinkServiceImpl.java | 8 src/main/resources/application-test.yml | 8 src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkDto.java | 21 ++ src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java | 18 ++ src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java | 147 ++++++++++++++++ src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkAnswerDto.java | 26 ++ src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkVo.java | 35 +++ src/main/java/cc/mrbird/febs/ai/service/AiService.java | 14 + 15 files changed, 510 insertions(+), 5 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java b/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java new file mode 100644 index 0000000..66212da --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/controller/memberTalk/ApiMemberTalkController.java @@ -0,0 +1,54 @@ +package cc.mrbird.febs.ai.controller.memberTalk; + +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkAnswerDto; +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkDto; +import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkVo; +import cc.mrbird.febs.ai.service.AiMemberTalkService; +import cc.mrbird.febs.common.entity.FebsResponse; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Administrator + */ +@Slf4j +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/api/ai/memberTalk") +@Api(value = "ApiMemberTalkController", tags = "AI-用户陪练") +public class ApiMemberTalkController { + + private final AiMemberTalkService aiMemberTalkService; + + + @ApiOperation(value = "开始陪练", notes = "开始陪练") + @ApiResponses({ + @ApiResponse(code = 200, message = "success", response = ApiMemberTalkVo.class) + }) + @PostMapping(value = "/start") + public FebsResponse start(@RequestBody @Validated ApiMemberTalkDto dto) { + + return aiMemberTalkService.start(dto); + } + + + @ApiOperation(value = "回答", notes = "回答") + @ApiResponses({ + @ApiResponse(code = 200, message = "success", response = ApiMemberTalkVo.class) + }) + @PostMapping(value = "/answer") + public FebsResponse answer(@RequestBody @Validated ApiMemberTalkAnswerDto dto) { + + return aiMemberTalkService.answer(dto); + } +} diff --git a/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkAnswerDto.java b/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkAnswerDto.java new file mode 100644 index 0000000..09da7ba --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkAnswerDto.java @@ -0,0 +1,26 @@ +package cc.mrbird.febs.ai.req.memberTalk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberTalkAnswerDto", description = "参数") +public class ApiMemberTalkAnswerDto { + + + @NotBlank(message = "会话ID不能为空") + @ApiModelProperty(value = "会话ID", example = "10") + private String id; + + + @NotBlank(message = "回答不能为空") + @ApiModelProperty(value = "回答", example = "10") + private String reqContext; + +} diff --git a/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkDto.java b/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkDto.java new file mode 100644 index 0000000..761c995 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/req/memberTalk/ApiMemberTalkDto.java @@ -0,0 +1,21 @@ +package cc.mrbird.febs.ai.req.memberTalk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberTalkDto", description = "参数") +public class ApiMemberTalkDto { + + + @NotBlank(message = "ID不能为空") + @ApiModelProperty(value = "产品ID", example = "10") + private String id; + +} diff --git a/src/main/java/cc/mrbird/febs/ai/res/ai/AiResponse.java b/src/main/java/cc/mrbird/febs/ai/res/ai/AiResponse.java new file mode 100644 index 0000000..14cf671 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/res/ai/AiResponse.java @@ -0,0 +1,16 @@ +package cc.mrbird.febs.ai.res.ai; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "AiResponse", description = "参数") +public class AiResponse { + + private String code; + private String description; + private String resContext; +} diff --git a/src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkVo.java b/src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkVo.java new file mode 100644 index 0000000..a93dd7d --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/res/memberTalk/ApiMemberTalkVo.java @@ -0,0 +1,35 @@ +package cc.mrbird.febs.ai.res.memberTalk; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author Administrator + */ +@Data +@ApiModel(value = "ApiMemberTalkVo", description = "参数") +public class ApiMemberTalkVo { + + /** + * 用户对话ID (UUID) + */ + + @ApiModelProperty(value = "会话ID") + private String memberTalkId; + + /** + * 类型 1-AI提问 2-用户回答 3-AI分析结果 + */ + + @ApiModelProperty(value = "类型 1-AI提问 2-用户回答 3-AI分析结果") + private Integer type; + + /** + * 内容 + */ + + @ApiModelProperty(value = "内容") + private String context; + +} diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java b/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java index 9e23d1a..c3a151e 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java @@ -1,7 +1,10 @@ package cc.mrbird.febs.ai.service; import cc.mrbird.febs.ai.entity.AiMemberTalkItem; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; import java.util.List; /** @@ -13,4 +16,7 @@ public interface AiMemberTalkItemService extends IService<AiMemberTalkItem> { + void add(String memberUuid, String id, int type, String resContext, Date createdTime); + + AiMemberTalkItem getByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java b/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java index 4ad489a..1101850 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java @@ -1,8 +1,13 @@ package cc.mrbird.febs.ai.service; import cc.mrbird.febs.ai.entity.AiMemberTalk; +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkAnswerDto; +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkDto; +import cc.mrbird.febs.common.entity.FebsResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; -import java.util.List; + +import java.util.Date; /** * AI用户对话训练记录 Service接口 @@ -13,4 +18,15 @@ public interface AiMemberTalkService extends IService<AiMemberTalk> { + AiMemberTalk getById(String id); + + FebsResponse start(ApiMemberTalkDto dto); + + AiMemberTalk getByQuery(LambdaQueryWrapper<AiMemberTalk> query); + + void updateTimeUpdate(Date nowTime, String id); + + FebsResponse answer(ApiMemberTalkAnswerDto dto); + + AiMemberTalk add(String memberUuid, String productId, Date nowTime); } diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductRoleLinkService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductRoleLinkService.java index 40a8602..e3cdaf0 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/AiProductRoleLinkService.java +++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductRoleLinkService.java @@ -1,10 +1,15 @@ package cc.mrbird.febs.ai.service; +import cc.mrbird.febs.ai.entity.AiMemberTalk; import cc.mrbird.febs.ai.entity.AiProductRoleLink; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; /** * @author Administrator */ public interface AiProductRoleLinkService extends IService<AiProductRoleLink> { + + AiProductRoleLink getByQuery(LambdaQueryWrapper<AiProductRoleLink> query); + } diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiService.java b/src/main/java/cc/mrbird/febs/ai/service/AiService.java new file mode 100644 index 0000000..116094d --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/service/AiService.java @@ -0,0 +1,14 @@ +package cc.mrbird.febs.ai.service; + +import cc.mrbird.febs.ai.res.ai.AiResponse; + +/** + * @author Administrator + */ +public interface AiService { + + + AiResponse start(String productRoleId, String content); + + AiResponse question(String promptTemplate,String linkId,String content); +} diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java index fe9f1ab..c3dc6d5 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java @@ -3,6 +3,7 @@ import cc.mrbird.febs.ai.entity.AiMemberTalkItem; import cc.mrbird.febs.ai.mapper.AiMemberTalkItemMapper; import cc.mrbird.febs.ai.service.AiMemberTalkItemService; +import cc.mrbird.febs.ai.utils.UUID; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; @@ -10,6 +11,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Date; import java.util.List; /** @@ -26,4 +28,20 @@ private final AiMemberTalkItemMapper aiMemberTalkItemMapper; + @Override + public void add(String memberUuid, String id, int type, String resContext,Date createdTime) { + AiMemberTalkItem aiMemberTalkItem = new AiMemberTalkItem(); + aiMemberTalkItem.setId(UUID.getSimpleUUIDString()); + aiMemberTalkItem.setCreatedTime(createdTime); + aiMemberTalkItem.setMemberId(memberUuid); + aiMemberTalkItem.setMemberTalkId(id); + aiMemberTalkItem.setType(type); + aiMemberTalkItem.setContext(resContext); + aiMemberTalkItemMapper.insert(aiMemberTalkItem); + } + + @Override + public AiMemberTalkItem getByQuery(LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery) { + return aiMemberTalkItemMapper.selectOne(memberTalkItemQuery); + } } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java index 2008dae..ddec2bb 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java @@ -1,16 +1,30 @@ package cc.mrbird.febs.ai.service.impl; import cc.mrbird.febs.ai.entity.AiMemberTalk; +import cc.mrbird.febs.ai.entity.AiMemberTalkItem; +import cc.mrbird.febs.ai.entity.AiProductRoleLink; import cc.mrbird.febs.ai.mapper.AiMemberTalkMapper; +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkAnswerDto; +import cc.mrbird.febs.ai.req.memberTalk.ApiMemberTalkDto; +import cc.mrbird.febs.ai.res.ai.AiResponse; +import cc.mrbird.febs.ai.res.memberTalk.ApiMemberTalkVo; +import cc.mrbird.febs.ai.service.AiMemberTalkItemService; import cc.mrbird.febs.ai.service.AiMemberTalkService; +import cc.mrbird.febs.ai.service.AiProductRoleLinkService; +import cc.mrbird.febs.ai.service.AiService; +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.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import java.util.Date; /** * AI用户对话训练记录 Service实现类 @@ -24,6 +38,125 @@ public class AiMemberTalkServiceImpl extends ServiceImpl<AiMemberTalkMapper, AiMemberTalk> implements AiMemberTalkService { private final AiMemberTalkMapper aiMemberTalkMapper; + private final AiProductRoleLinkService aiProductRoleLinkService; + private final AiMemberTalkItemService aiMemberTalkItemService; + private final AiService aiService; + @Override + public AiMemberTalk getById(String id) { + return aiMemberTalkMapper.selectById( id); + } + + @Override + public FebsResponse start(ApiMemberTalkDto dto) { + + String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid(); + String productId = dto.getId(); + + LambdaQueryWrapper<AiProductRoleLink> productLinkQuery = Wrappers.lambdaQuery(AiProductRoleLink.class); + productLinkQuery.eq(AiProductRoleLink::getProductId,productId); + productLinkQuery.last("limit 1"); + AiProductRoleLink aiProductRoleLink = aiProductRoleLinkService.getByQuery(productLinkQuery); + if(ObjectUtil.isNull(aiProductRoleLink)){ + throw new RuntimeException("产品AI陪练不存在"); + } + + Date nowTime = new Date(); + LambdaQueryWrapper<AiMemberTalk> query = Wrappers.lambdaQuery(AiMemberTalk.class); + query.eq(AiMemberTalk::getMemberId,memberUuid); + query.eq(AiMemberTalk::getProductId,productId); + query.last("limit 1"); + AiMemberTalk aiMemberTalk = this.getByQuery(query); + if (ObjectUtil.isNull(aiMemberTalk)){ + aiMemberTalk = this.add(memberUuid,productId,nowTime); + } + + AiResponse aiResponse = aiService.start(aiProductRoleLink.getProductRoleId(),"开始出题"); + if(aiResponse.getCode().equals("200")){ + aiMemberTalkItemService.add(memberUuid,aiMemberTalk.getId(),1,aiResponse.getResContext(),nowTime); + this.updateTimeUpdate(nowTime,aiMemberTalk.getId()); + }else{ + throw new RuntimeException(aiResponse.getDescription()); + } + ApiMemberTalkVo apiMemberTalkVo = new ApiMemberTalkVo(); + apiMemberTalkVo.setMemberTalkId(aiMemberTalk.getId()); + apiMemberTalkVo.setType(1); + apiMemberTalkVo.setContext(aiResponse.getResContext()); + + return new FebsResponse().success().data(apiMemberTalkVo); + } + + @Override + public AiMemberTalk getByQuery(LambdaQueryWrapper<AiMemberTalk> query) { + return aiMemberTalkMapper.selectOne( query); + } + + @Override + public void updateTimeUpdate(Date nowTime, String id) { + aiMemberTalkMapper.update( + null, + Wrappers.lambdaUpdate(AiMemberTalk.class) + .set(AiMemberTalk::getUpdatedTime,nowTime) + .eq(AiMemberTalk::getId,id)); + } + + + public static final String ANSWER_FORMAT = "{}/n回答:{}/n请根据回答给出以下四个方面的总结,这个四个方面分别是亮点、建议、参考答案和核心知识点回顾。重点:四个方面的总结都是必须要有内容。"; + @Override + public FebsResponse answer(ApiMemberTalkAnswerDto dto) { + String memberUuid = LoginUserUtil.getLoginUser().getMemberUuid(); + String memberTalkId = dto.getId(); + AiMemberTalk aiMemberTalk = this.getById(memberTalkId); + if (ObjectUtil.isNull(aiMemberTalk)){ + throw new RuntimeException("产品AI陪练对话不存在"); + } + + LambdaQueryWrapper<AiProductRoleLink> productLinkQuery = Wrappers.lambdaQuery(AiProductRoleLink.class); + productLinkQuery.eq(AiProductRoleLink::getProductId,aiMemberTalk.getProductId()); + productLinkQuery.last("limit 1"); + AiProductRoleLink aiProductRoleLink = aiProductRoleLinkService.getByQuery(productLinkQuery); + if(ObjectUtil.isNull(aiProductRoleLink)){ + throw new RuntimeException("产品AI陪练不存在"); + } + + String reqContext = dto.getReqContext(); + + LambdaQueryWrapper<AiMemberTalkItem> memberTalkItemQuery = Wrappers.lambdaQuery(AiMemberTalkItem.class); + memberTalkItemQuery.eq(AiMemberTalkItem::getMemberId,memberUuid); + memberTalkItemQuery.eq(AiMemberTalkItem::getMemberTalkId,memberTalkId); + memberTalkItemQuery.eq(AiMemberTalkItem::getType,1); + memberTalkItemQuery.orderByDesc(AiMemberTalkItem::getCreatedTime); + memberTalkItemQuery.last("limit 1"); + AiMemberTalkItem aiMemberTalkItem = aiMemberTalkItemService.getByQuery(memberTalkItemQuery); + aiMemberTalkItemService.add(memberUuid,aiMemberTalk.getId(),2,reqContext,new Date()); + + String format = StrUtil.format(ANSWER_FORMAT, aiMemberTalkItem.getContext(), reqContext); + log.info("format:{}",format); + AiResponse aiResponse = aiService.start(aiProductRoleLink.getProductRoleId(), format); + if(aiResponse.getCode().equals("200")){ + Date nowTime = new Date(); + aiMemberTalkItemService.add(memberUuid,aiMemberTalk.getId(),3,aiResponse.getResContext(),nowTime); + this.updateTimeUpdate(nowTime,aiMemberTalk.getId()); + }else{ + throw new RuntimeException(aiResponse.getDescription()); + } + ApiMemberTalkVo apiMemberTalkVo = new ApiMemberTalkVo(); + apiMemberTalkVo.setMemberTalkId(aiMemberTalk.getId()); + apiMemberTalkVo.setType(1); + apiMemberTalkVo.setContext(aiResponse.getResContext()); + + return new FebsResponse().success().data(apiMemberTalkVo); + } + + @Override + public AiMemberTalk add(String memberUuid, String productId, Date nowTime) { + AiMemberTalk aiMemberTalk = new AiMemberTalk(); + aiMemberTalk.setId(UUID.getSimpleUUIDString()); + aiMemberTalk.setCreatedTime(nowTime); + aiMemberTalk.setMemberId(memberUuid); + aiMemberTalk.setProductId(productId); + aiMemberTalkMapper.insert(aiMemberTalk); + return aiMemberTalk; + } } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleLinkServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleLinkServiceImpl.java index 680f6c0..0568bb7 100644 --- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleLinkServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleLinkServiceImpl.java @@ -1,8 +1,10 @@ package cc.mrbird.febs.ai.service.impl; +import cc.mrbird.febs.ai.entity.AiMemberTalk; import cc.mrbird.febs.ai.entity.AiProductRoleLink; import cc.mrbird.febs.ai.mapper.AiProductRoleLinkMapper; import cc.mrbird.febs.ai.service.AiProductRoleLinkService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -15,4 +17,10 @@ @Service @RequiredArgsConstructor public class AiProductRoleLinkServiceImpl extends ServiceImpl<AiProductRoleLinkMapper, AiProductRoleLink> implements AiProductRoleLinkService { + + private final AiProductRoleLinkMapper aiProductRoleLinkMapper; + @Override + public AiProductRoleLink getByQuery(LambdaQueryWrapper<AiProductRoleLink> query) { + return aiProductRoleLinkMapper.selectOne( query); + } } diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java new file mode 100644 index 0000000..0ef2ae9 --- /dev/null +++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiServiceImpl.java @@ -0,0 +1,147 @@ +package cc.mrbird.febs.ai.service.impl; + +import cc.mrbird.febs.ai.entity.AiProductRole; +import cc.mrbird.febs.ai.res.ai.AiResponse; +import cc.mrbird.febs.ai.service.AiProductRoleService; +import cc.mrbird.febs.ai.service.AiService; +import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionChoice; +import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionRequest; +import com.volcengine.ark.runtime.model.completion.chat.ChatMessage; +import com.volcengine.ark.runtime.model.completion.chat.ChatMessageRole; +import com.volcengine.ark.runtime.service.ArkService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import okhttp3.ConnectionPool; +import okhttp3.Dispatcher; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author Administrator + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AiServiceImpl implements AiService { + + private static final String CODE_SUCCESS = "200"; + private static final String CODE_NOT_FOUND = "201"; + private static final String CODE_ERROR = "500"; + + private final AiProductRoleService aiProductRoleService; + + @Value("${ai.service.ak}") + private String ak; + + @Value("${ai.service.sk}") + private String sk; + + @Value("${ai.service.base-url}") + private String baseUrl; + + private ArkService service; + + @PostConstruct + public void init() { + ConnectionPool connectionPool = new ConnectionPool(10, 30, TimeUnit.SECONDS); + Dispatcher dispatcher = new Dispatcher(); + this.service = ArkService.builder() + .dispatcher(dispatcher) + .connectionPool(connectionPool) + .baseUrl(baseUrl) + .ak(ak) + .sk(sk) + .build(); + } + + @PreDestroy + public void destroy() { + if (service != null) { + service.shutdownExecutor(); + } + } + + @Override + public AiResponse start(String productRoleId, String content) { + if (!StringUtils.hasText(productRoleId)) { + log.warn("productRoleId 不能为空"); + return buildErrorResponse(CODE_NOT_FOUND, "AI陪练不存在"); + } + + AiProductRole aiProductRole = aiProductRoleService.getById(productRoleId); + if (aiProductRole == null) { + log.warn("未找到对应的角色配置,productRoleId: {}", productRoleId); + return buildErrorResponse(CODE_NOT_FOUND, "AI陪练不存在"); + } + + String promptTemplate = aiProductRole.getPromptTemplate(); + String linkId = aiProductRole.getLinkId(); + + if (!StringUtils.hasText(promptTemplate) || !StringUtils.hasText(linkId)) { + log.warn("角色配置不完整,promptTemplate 或 linkId 为空,productRoleId: {}", productRoleId); + return buildErrorResponse(CODE_ERROR, "角色配置不完整"); + } + + return question(promptTemplate, linkId, content); + } + + @Override + public AiResponse question(String promptTemplate, String linkId, String content) { + if (!StringUtils.hasText(promptTemplate) || !StringUtils.hasText(linkId) || !StringUtils.hasText(content)) { + log.warn("请求参数不完整,promptTemplate: {}, linkId: {}, content: {}", promptTemplate, linkId, content); + return buildErrorResponse(CODE_ERROR, "请求参数不完整"); + } + + final List<ChatMessage> messages = new ArrayList<>(); + final ChatMessage systemMessage = ChatMessage.builder().role(ChatMessageRole.SYSTEM).content(promptTemplate).build(); + final ChatMessage userMessage = ChatMessage.builder().role(ChatMessageRole.USER).content(content).build(); + messages.add(systemMessage); + messages.add(userMessage); + + ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() + .model(linkId) + .messages(messages) + .temperature(1.0) + .topP(0.7) + .maxTokens(4096) + .frequencyPenalty(0.0) + .build(); + + try { + List<ChatCompletionChoice> choices = service.createChatCompletion(chatCompletionRequest).getChoices(); + String result = choices.stream() + .map(choice -> choice.getMessage().getContent()) + .filter(contentObj -> contentObj != null) + .map(Object::toString) + .collect(Collectors.joining()); + + return buildSuccessResponse(result); + } catch (Exception e) { + log.error("调用AI服务失败,modelId: {}, content: {}", linkId, content, e); + return buildErrorResponse(CODE_ERROR, "AI服务调用失败"); + } + } + + private AiResponse buildErrorResponse(String code, String description) { + AiResponse response = new AiResponse(); + response.setCode(code); + response.setDescription(description); + return response; + } + + private AiResponse buildSuccessResponse(String result) { + AiResponse response = new AiResponse(); + response.setCode(CODE_SUCCESS); + response.setDescription("成功"); + response.setResContext(result); + return response; + } +} diff --git a/src/main/java/cc/mrbird/febs/mall/controller/HSController.java b/src/main/java/cc/mrbird/febs/mall/controller/HSController.java index 638717a..95235d0 100644 --- a/src/main/java/cc/mrbird/febs/mall/controller/HSController.java +++ b/src/main/java/cc/mrbird/febs/mall/controller/HSController.java @@ -18,7 +18,7 @@ // 从环境变量中获取您的Key鉴权。此为默认方式,您可根据需要进行修改 private static String ak = "AKLTZTQxZjMyZTUxMWJmNDEyNDkzNWExOGQ3ODllNzhhNmQ"; private static String sk = "TmpFeE1qZ3haREExTW1JeE5HRTBZVGc1WlRRNVlqWXpORGd5TWpsak5HWQ=="; - private static String ep_id = "ep-20250728114932-429wg"; + private static String ep_id = "ep-20250805124033-lhxbf"; // 此为默认路径,您可根据业务所在地域进行配置 static String baseUrl = "https://ark.cn-beijing.volces.com/api/v3"; static ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS); diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index 0d6fbb2..7fb08b0 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -89,4 +89,10 @@ path: /mnt/sdc/webresource/file/ system: - job: false \ No newline at end of file + job: false + +ai: + service: + ak: AKLTZTQxZjMyZTUxMWJmNDEyNDkzNWExOGQ3ODllNzhhNmQ + sk: TmpFeE1qZ3haREExTW1JeE5HRTBZVGc1WlRRNVlqWXpORGd5TWpsak5HWQ== + base-url: https://ark.cn-beijing.volces.com/api/v3 \ No newline at end of file -- Gitblit v1.9.1