基于spring boot 集成 deepseek 流式输出 的vue3使用指南

本文使用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. 最佳实践

  1. 优先使用 Composition API:特别是对于复杂组件
  2. 合理拆分组件:保持组件单一职责
  3. 使用 <script setup> 语法:简化代码
  4. 利用 TypeScript:Vue 3 对 TS 支持更好
  5. 按需引入:减少打包体积
  6. 使用 Vite:获得更好的开发体验

10. 资源

希望这篇文档能帮助你快速上手 Vue 3 开发!

Stream completed

相关推荐
追逐时光者2 分钟前
一款开源免费、通用的 WPF 主题控件包
后端·.net
挺菜的24 分钟前
【算法刷题记录(简单题)003】统计大写字母个数(java代码实现)
java·数据结构·算法
蜗牛沐雨25 分钟前
警惕 Rust 字符串的性能陷阱:`chars().nth()` 的深坑与高效之道
开发语言·后端·rust
&Sinnt&1 小时前
Git 版本控制完全指南:从入门到精通
git·后端
掘金-我是哪吒1 小时前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
亲爱的非洲野猪2 小时前
Kafka消息积压的多维度解决方案:超越简单扩容的完整策略
java·分布式·中间件·kafka
陈随易2 小时前
MoonBit助力前端开发,加密&性能两不误,斐波那契测试提高3-4倍
前端·后端·程序员
wfsm2 小时前
spring事件使用
java·后端·spring
微风粼粼2 小时前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo