本文使用deepseek API接口流式输出的文章。
环境要求 jdk17 spring boot 3.4
代码如下:
java
package com.example.controller;
import jakarta.annotation.PostConstruct;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.Disposable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@RestController
public class ChatController {
// 注入OpenAI聊天模型实例,已通过配置文件完成API密钥等参数配置
@Autowired
private OpenAiChatModel chatModel;
// 使用List维护对话上下文,包含系统消息和用户消息
private List<Message> chatHistory = new ArrayList<>();
@PostConstruct
public void init() {
// 添加系统消息定义AI行为准则
chatHistory.add(new SystemMessage("You are a helpful assistant."));
}
/**
* 流式聊天内容生成接口(Server-Sent Events 实现)
*
* @param message 用户输入的聊天消息
* @return SseEmitter 用于服务端推送事件的发射器对象
*/
@GetMapping("/generateStream")
public SseEmitter streamChat(@RequestParam String message) {
// 创建 SSE 发射器并设置 180 秒超时(可根据业务需求调整)
SseEmitter emitter = new SseEmitter(180_000L);
// 记录请求开始(实际项目建议使用 SLF4J 等日志框架)
System.out.println("SSE connection established for message: " + message);
// 构建对话请求参数
Prompt prompt = new Prompt(new UserMessage(message));
// 获取流式处理订阅对象(用于后续资源清理)
Disposable subscription = chatModel.stream(prompt)
.subscribe(
// 处理每个响应块
response -> {
try {
// 安全获取响应文本
String text = Optional.ofNullable(response)
.map(ChatResponse::getResult)
.map(Generation::getOutput)
.map(AssistantMessage::getText)
.orElse(""); // 空值处理
// 发送 SSE 事件(建议实际项目添加 JSON 包装)
emitter.send(SseEmitter.event()
.id(String.valueOf(System.currentTimeMillis())) // 事件ID用于客户端排序
.data(text) // 事件数据内容
.name("message")// 定义事件类型
.build());
// 打印到控制台用于调试
System.out.print(text);
} catch (Exception e) {
// 发送异常时关闭连接并记录错误
emitter.completeWithError(e);
System.err.println("Error sending SSE event: " + e.getMessage());
}
},
// 处理流式响应异常
error -> {
emitter.completeWithError(error);
System.err.println("Stream error: " + error.getMessage());
},
// 处理流式响应完成
() -> {
emitter.complete();
System.out.println("\nStream completed");
}
);
// 注册连接完成回调(客户端断开/正常结束)
emitter.onCompletion(() -> {
// 取消订阅释放资源
if (!subscription.isDisposed()) {
subscription.dispose();
}
System.out.println("SSE connection completed");
});
// 注册超时回调
emitter.onTimeout(() -> {
// 主动完成连接并释放资源
emitter.complete();
subscription.dispose();
System.out.println("SSE connection timed out");
});
return emitter;
}
}
配置key
text
spring.ai.openai.api-key=你申请的key
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.options.model=deepseek-chat
spring.ai.openai.chat.options.temperature=0.7
# The DeepSeek API doesn't support embeddings, so we need to disable it.
spring.ai.openai.embedding.enabled=false
Vue 3 使用指南
1. Vue 3 简介
Vue 3 是 Vue.js 框架的最新主要版本,于 2020 年 9 月正式发布。它带来了许多改进和新特性:
- 性能提升:更快的渲染速度,更小的包体积
- Composition API:新的代码组织方式
- 更好的 TypeScript 支持
- 新的响应式系统
- Fragment、Teleport、Suspense 等新特性
2. 创建 Vue 3 项目
使用 Vite 创建项目
bash
npm create vite@latest my-vue-app --template vue
使用 Vue CLI 创建项目
bash
npm install -g @vue/cli
vue create my-vue-app
# 选择 Vue 3 预设
3. Composition API
Vue 3 引入了 Composition API 作为 Options API 的补充,提供了更好的代码组织和复用。
基本示例
vue
<script setup>
import { ref, computed, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 计算属性
const doubleCount = computed(() => count.value * 2)
// 方法
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log('组件已挂载')
})
</script>
<template>
<button @click="increment">
计数: {{ count }}, 双倍: {{ doubleCount }}
</button>
</template>
响应式基础
ref()
: 用于基本类型reactive()
: 用于对象toRefs()
: 将 reactive 对象转换为 ref 集合
javascript
import { ref, reactive, toRefs } from 'vue'
const counter = ref(0)
const state = reactive({
name: 'Vue 3',
version: '3.x'
})
// 在模板中使用时不需要 .value
const { name, version } = toRefs(state)
4. 组件系统
组件定义
vue
<script setup>
// 使用 <script setup> 语法糖
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const message = ref('Hello from parent')
</script>
<template>
<ChildComponent :message="message" />
</template>
Props 和 Emits
vue
<script setup>
// ChildComponent.vue
const props = defineProps({
message: {
type: String,
required: true
}
})
const emit = defineEmits(['response'])
function sendResponse() {
emit('response', 'Hello from child')
}
</script>
<template>
<div>
{{ message }}
<button @click="sendResponse">Reply</button>
</div>
</template>
5. 生命周期钩子
Vue 3 生命周期钩子(Composition API 版本):
Options API | Composition API |
---|---|
beforeCreate | 不需要 (setup 替代) |
created | 不需要 (setup 替代) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
6. 新特性
Teleport
将子节点渲染到 DOM 中的其他位置:
vue
<template>
<button @click="open = true">打开模态框</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>模态框内容</p>
<button @click="open = false">关闭</button>
</div>
</Teleport>
</template>
Suspense
处理异步组件加载状态:
vue
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
</script>
7. 状态管理 (Pinia)
Vue 3 推荐使用 Pinia 作为状态管理库:
安装 Pinia
bash
npm install pinia
基本使用
javascript
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})
vue
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>{{ counter.count }}</div>
<div>{{ counter.doubleCount }}</div>
<button @click="counter.increment()">+</button>
</template>
8. 路由 (Vue Router 4)
安装
bash
npm install vue-router@4
基本配置
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
在组件中使用
vue
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
function goToAbout() {
router.push('/about')
}
</script>
<template>
<div>当前路由: {{ route.path }}</div>
<button @click="goToAbout">前往关于页面</button>
<router-view />
</template>
9. 最佳实践
- 优先使用 Composition API:特别是对于复杂组件
- 合理拆分组件:保持组件单一职责
- 使用
<script setup>
语法:简化代码 - 利用 TypeScript:Vue 3 对 TS 支持更好
- 按需引入:减少打包体积
- 使用 Vite:获得更好的开发体验
10. 资源
希望这篇文档能帮助你快速上手 Vue 3 开发!
Stream completed