基于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

相关推荐
Uranus^43 分钟前
深入解析Spring Boot与JUnit 5的集成测试实践
spring boot·单元测试·集成测试·junit 5·mockito
tmacfrank1 小时前
网络编程中的直接内存与零拷贝
java·linux·网络
weixin_472339462 小时前
Maven 下载安装与配置教程
java·maven
Magnum Lehar3 小时前
3d游戏引擎EngineTest的系统实现3
java·开发语言·游戏引擎
就叫飞六吧3 小时前
Spring Security 集成指南:避免 CORS 跨域问题
java·后端·spring
Mcworld8573 小时前
java集合
java·开发语言·windows
天黑请闭眼3 小时前
IDEA:程序编译报错:java: Compilation failed: internal java compiler error
java·intellij-idea
fatesunlove3 小时前
知识图谱重构电商搜索:下一代AI搜索引擎的底层逻辑
搜索引擎·ai·知识图谱
苍煜4 小时前
Maven构建流程详解:如何正确管理微服务间的依赖关系-当依赖的模块更新后,我应该如何重新构建主项目
java·微服务·maven
冼紫菜4 小时前
[特殊字符]CentOS 7.6 安装 JDK 11(适配国内服务器环境)
java·linux·服务器·后端·centos