一:引入Maven依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>broadscope-bailian-sdk-java</artifactId>
<version>1.2.0</version>
</dependency>
二:编写阿里云百炼Client类
配置文件加入如下配置
相关参数请从阿里云百炼官网获取
yun:
bailian:
accessKeyId: **************************
accessKeySecret: **************************
agentKey: **************************
appId: **************************
@Component
public class BaiLianClient {
@Resource
private AlbaiLianConfig baiLianConfig;
private AccessTokenClient accessTokenClient;
@Resource
private ChatCustomerInfoMapper chatCustomerInfoMapper;
@PostConstruct
public void init(){
String accessKeyId = baiLianConfig.getAccessKeyId();
String agentKey = baiLianConfig.getAgentKey();
String accessKeySecret = baiLianConfig.getAccessKeySecret();
accessTokenClient = new AccessTokenClient(accessKeyId,accessKeySecret,agentKey);
}
public Flux<CompletionsResponse> createStreamCompletion(ChatCustomerInfo chatCustomerInfo) {
String content = chatCustomerInfo.getInfoContent();
String token = accessTokenClient.getToken();
BaiLianConfig config = new BaiLianConfig()
.setApiKey(token);
CompletionsRequest request = new CompletionsRequest()
.setAppId(baiLianConfig.getAppId())
.setPrompt(content)
.setStream(true);
ApplicationClient client = new ApplicationClient(config);
chatCustomerInfoMapper.sendInfo(chatCustomerInfo.getUserId(),chatCustomerInfo.getInfoContent(),false);
Flux<CompletionsResponse> flux = client.streamCompletions(request);
return client.streamCompletions(request);
}
}
三:编写Controller层
为了更快实现功能,本人只是简单调用回复消息接口,并将历史消息保存到数据库表中,具体复杂API请参考阿里云百炼官网手册,后端流式输入采用FLux实现,
@RestController
@Slf4j
@Api(tags = "智能助手接口")
@RequestMapping("/chatCustomerInfo")
public class ChatCustomerInfoController {
@Resource
private ChatCustomerInfoService chatCustomerInfoService;
@Resource
private BaiLianClient baiLianClient;
@Resource
private ChatCustomerInfoMapper chatCustomerInfoMapper;
private String responseText;
private Long userId;
@ApiOperation(value = "获取历史信息",notes = "获取历史信息")
@PostMapping("/getInfoList")
public Result<List<ChatCustomerInfo>> getInfoList(@RequestBody ChatCustomerInfo chatCustomerInfo) {
return Result.success(chatCustomerInfoService.getInfoByUserId(chatCustomerInfo.getUserId()));
}
@ApiOperation(value = "发送消息",notes = "发送消息")
@PostMapping(value = "/completions", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Result<String>> complete(@RequestBody ChatCustomerInfo chatCustomerInfo,HttpServletResponse response) {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("X-Accel-Buffering", "no");
userId = chatCustomerInfo.getUserId();
Flux<CompletionsResponse> flux = baiLianClient.createStreamCompletion(chatCustomerInfo);
return flux.onBackpressureBuffer()
.map(this::convertResponse)
.doOnComplete(this::handleComplete);
}
private void handleComplete() {
log.info(responseText);
chatCustomerInfoMapper.sendInfo(userId,responseText,true);
}
private Result<String> convertResponse(CompletionsResponse response) {
this.responseText = response.getData().getText();
return Result.success(response.getData().getText());
}
}
四:前端调用接口并流式接受响应内容
<template>
<div class="chat-container">
<div class="chat-header">
<h2>智能助手</h2>
</div>
<div class="chat-messages">
<div v-for="message in list" :key="message.id" class="message">
<div v-if="!message.isSelf" style="margin-left: 560px;color: grey;white-space: pre-wrap">
{{message.createTime != null ? message.createTime + '\n' : null}}
</div>
<div v-else style="white-space: pre-wrap;color: grey">
{{message.createTime != null ? message.createTime + '\n' : null}}
</div>
<div class="user" v-if="!message.isSelf" >
<div class="customer-message">
{{ message.infoContent }}
</div>
<div class="customer-message-img">
<img :src="$target +'public/imgs/yonghu.jpg'" alt="" style="width: 100%; height: 100%">
</div>
</div>
<div class="agent" v-else>
<div class="agent-message-img">
<img :src="$target +'public/imgs/chat.png'" alt="" style="width: 100%; height: 100%">
</div>
<div class="agent-message">{{ message.infoContent}}</div>
</div>
</div>
</div>
<div class="chat-input">
<input type="text" v-model="text" @keyup.enter="onSend" placeholder="输入消息..." />
<button @click="onSend">发送</button>
</div>
<van-image width="45px" height="45px" round src="https://img01.yzcdn.cn/vant/cat.jpeg" />
</div>
</template>
<script>
import { fetchEventSource } from "@microsoft/fetch-event-source";
export default {
name: 'ChatCustomer',
data() {
return {
see: '',
text: '',
list: [],
gettime: ''
}
},
mounted() {
},
created() {
this.init()
},
activated() {
// 获取消息数据
this.$axios
.post("/api/chatCustomerInfo/getInfoList", {
userId: this.$store.getters.getUser.id
})
.then(res => {
if (res.data.code === "001") {
this.list = res.data.data;
}
this.getCurrentTime();
let chuList = {
isSelf: 1,
infoContent: '您好,我是悦选市集智能助手小悦,快开始向我提问吧!',
createTime: this.gettime
}
this.list.push(chuList)
})
.catch(err => {
return Promise.reject(err);
});
},
methods: {
getCurrentTime() {
//获取当前时间并打印
let yy = new Date().getFullYear();
let mm = new Date().getMonth() + 1;
let dd = new Date().getDate();
let hh = new Date().getHours();
let mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes();
let ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds();
this.gettime = ' ' + yy + '/' + mm + '/' + dd + ' ' + hh + ':' + mf + ':' + ss + ' ' + '\n';
},
onSend: function () {
let infoContent = this.text
this.text = ''
let newList = {
isSelf: 0,
infoContent: infoContent,
}
this.list.push(newList)
let newList1 = {
isSelf: 1,
infoContent: '......'
}
this.list.push(newList1)
let s = {"infoContent": infoContent,"userId": this.$store.getters.getUser.id}
//
fetchEventSource("/api/chatCustomerInfo/completions", {
body: JSON.stringify(s),
headers: {
'Content-Type': 'application/json'
},
method: "POST",
onmessage: (event) => {
let s = JSON.parse(event.data);
this.list.splice(this.list.length - 1,1,{isSelf: 1,infoContent: s.data})
},
onclose() {
},
});
}
},
}
</script>
<style>
.chat-container {
width: 800px;
height: 800px;
margin: 0 auto;
border: 1px solid #ccc;
border-radius: 5px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.chat-header {
background-color: #f2f2f2;
padding: 10px;
text-align: center;
border-bottom: 1px solid #ccc;
}
.chat-messages {
max-height: 650px;
overflow-y: auto;
padding: 10px;
}
.message {
margin-bottom: 10px;
height: 100%;
padding: 15px 16px;
display: flex;
flex-direction: column;
}
.user {
align-self: flex-end;
display: flex;
}
.agent {
display: flex;
}
.customer-message {
background-color: #e6f7ff;
padding: 20px;
border-radius: 5px;
width: fit-content;
max-width: 500px; /* 聊天气泡的最大宽度 */
word-wrap: break-word; /* 在单词边界处换行 */
white-space: pre-wrap;
}
.customer-message-img {
width: 60px;
height: 60px;
padding: 0 0 0 10px;
border-radius: 5px;
}
.agent-message-img {
width: 60px;
height: 60px;
padding: 0 10px 0 0;
border-radius: 5px;
}
.agent-message {
background-color: #f2f2f2;
padding: 20px;
border-radius: 5px;
width: fit-content;
max-width: 500px; /* 聊天气泡的最大宽度 */
word-wrap: break-word; /* 在单词边界处换行 */
white-space: pre-wrap;
}
.chat-input {
display: flex;
align-items: center;
padding: 10px;
}
input {
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
margin-right: 10px;
}
button {
padding: 8px 15px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
具体功能实现参考基于SpringBoot + Vue的商城购物系统实战-CSDN博客
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_65408235/article/details/139618719