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

相关推荐
橙序员小站3 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
怒放吧德德3 小时前
Netty 4.2 入门指南:从概念到第一个程序
java·后端·netty
雨中飘荡的记忆4 小时前
大流量下库存扣减的数据库瓶颈:Redis分片缓存解决方案
java·redis·后端
开心就好20256 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
悟空码字6 小时前
告别“屎山代码”:AI 代码整洁器让老项目重获新生
后端·aigc·ai编程
小码哥_常6 小时前
大厂不宠@Transactional,背后藏着啥秘密?
后端
奋斗小强6 小时前
内存危机突围战:从原理辨析到线上实战,彻底搞懂 OOM 与内存泄漏
后端
小码哥_常6 小时前
Spring Boot接口防抖秘籍:告别“手抖”,守护数据一致性
后端
心之语歌7 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
None3217 小时前
【NestJs】基于Redlock装饰器分布式锁设计与实现
后端·node.js