计算机毕业设计260—基于Springboot+Vue3+Ai对话的非遗传承管理系统(源代码+数据库+2万字论文)

毕设所有选题:
https://blog.csdn.net/2303_76227485/article/details/131104075

基于Springboot+Vue3+Ai对话的非遗传承管理系统(源代码+数据库+2万字论文)

项目编号:260

一、系统介绍

本项目前后端分离,分为用户、管理员2种角色。

1、用户:

  • 登录、注册、热门作品查询、作品搜索、最新作品查询、活动报名、非遗课程学习、传承人查询
  • 非遗手办商城、订单提交、支付、取消、确认收货
  • 修改密码、个人信息修改、收货地址维护、默认地址设置、非遗作品发布
  • AI智能助手模块:创建会话、会话列表查询、会话消息历史查询、流式对话、非流式对话、更新会话标题、删除会话

2、管理员:

  • 首页大屏数据图表统计:用户数、订单数、销售额、非遗作品数、订单趋势曲线图、销售额柱状图、订单状态分布环状图、非遗类别饼状图
  • 用户管理
  • 非遗作品管理:非遗作品增删改查、作品发布、作品下架
  • 课程管理:课程管理、章节管理
  • 活动管理:报名审核、活动签到、报名列表查询
  • 商城管理:商品增删改查、商品上架、商品下架、商品库存更新、商品分类管理
  • 订单管理(查看、发货)
  • 传承人管理:传承人增删改查、传承人与作品关联管理

4、亮点:

  • 实现ai对话优化用户体验
  • 后台首页大屏使用echarts图表统计,更直观的看出系统的运行数据

二、所用技术

后端技术栈:

  • Springboot3
  • mybatisPlus
  • Jwt
  • Spring Security
  • Mysql
  • Maven

前端技术栈:

  • Vue3
  • Vue-router
  • axios
  • elementPlus
  • echarts

三、环境介绍

基础环境 :IDEA/eclipse, JDK17或以上, Mysql5.7及以上, Maven3.6, node14, navicat, 通义千问apikey

所有项目以及源代码本人均调试运行无问题 可支持远程调试运行

四、页面截图

文档截图:


1、用户:






























2、管理员:





















五、浏览地址

前台地址:http://localhost:8800

  • 用户账号密码:test/123456

  • 管理员账户密码:admin/123456

六、部署教程

  1. 使用Navicat或者其它工具,在mysql中创建对应名称的数据库,并执行项目的sql文件

  2. 使用IDEA/Eclipse导入springboot项目,若为maven项目请选择maven,等待依赖下载完成

  3. 修改application.yml里面的数据库配置和数据库配置,还有通义千问的apikey配置,

    src/main/java/org/example/springboot/SpringbootApplication.java启动后端项目

  4. vscode或idea打开vue3项目

  5. 在编译器中打开terminal,执行npm install 依赖下载完成后执行 npm run serve,执行成功后会显示访问地址

七、ai对话部分代码

java 复制代码
    /**
 * 流式聊天接口(SSE)
 */
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@Valid @RequestBody ChatRequest request,
        HttpServletResponse response) {
        response.setHeader("X-Accel-Buffering", "no"); // 禁用Nginx缓冲
        response.setHeader("Cache-Control", "no-cache, no-store"); // 禁用浏览器缓冲
        response.setHeader("Pragma", "no-cache");
        response.setCharacterEncoding("UTF-8");
        Long userId = JwtTokenUtils.getCurrentUserId();
        // 验证权限
        if (!sessionService.isSessionOwnedByUser(request.getSessionId(), userId)) {
        return Flux.just("data: 无权访问此会话\n\n");
        }
        log.info("开始流式对话,sessionId: {}, userId: {}", request.getSessionId(), userId);
        return tongyiQianwenService.chatStream(request.getSessionId(), request.getUserMessage())
        .map(chatResponse -> "data: " + JSON.toJSONString(chatResponse) + "\n\n");
        }

/**
 * 流式聊天(SSE实时返回)
 */
public Flux<ChatResponse> chatStream(String sessionId, String prompt) {
        log.info("开始AI对话,sessionId: {}, userMessage: {}", sessionId, prompt);

        // 保存用户消息
        sessionService.saveMessage(sessionId, "user", prompt);
        // 1. 获取会话上下文
        SessionContext context = sessionManager.getSessionContext(sessionId);
        if (context == null) {
        context = new SessionContext(sessionId); // 初始化空上下文
        sessionManager.saveSessionContext(context);
        }
        String finalSessionId = context.getSessionId();
        // 2. 构建请求体
        Map<String, Object> requestBody = buildRequestBody(context, prompt, true);
        // 定义原子变量累加完整响应文本
        AtomicReference<String> fullAnswer = new AtomicReference<>("");
        // 3. 流式调用API
        return WebClient.create()
        .post()
        .uri(apiUrl)
        .headers(headers -> headers.addAll(buildHeaders()))
        .bodyValue(requestBody)
        .retrieve()
        // 禁用WebClient的响应缓冲(关键!)
        .bodyToFlux(String.class)
        // 逐个处理通义千问返回的流式片段
        .doOnNext(line -> log.info("通义千问原生片段:{}", line)) // 打印原生片段,验证是否逐行返回
        // 步骤1:先过滤空行(提前剔除无效数据)
        .filter(line -> line != null && !line.trim().isEmpty())
        .scan(new Accumulator(null, ""), (acc, line) -> {
        // 空行处理:保留原有累加文本,line设为null
        if (line == null || line.isEmpty()) {
        return new Accumulator(null, acc.getFullAnswer());
        }

        // 解析SSE数据前缀
        String data = line.startsWith("data: ") ? line.substring(6) : line;
        // 解析流式片段
        TongyiQianwenResponse fragment = JSON.parseObject(data, TongyiQianwenResponse.class);
        // 1. 流结束标识:返回完整响应
        if ("stop".equals(fragment.getOutput().getFinish_reason())) {
        return new Accumulator(data, acc.getFullAnswer());
        }

        // 解析失败:保留原有累加文本,line设为null
        if (fragment == null || fragment.getOutput() == null) {
        return new Accumulator(null, acc.getFullAnswer());
        }

        // 正常片段:累加文本
        String newFullAnswer = acc.getFullAnswer() + fragment.getOutput().getText();
        // 返回新的累加器(当前行 + 最新累加文本)
        return new Accumulator(line, newFullAnswer);
        })
        // 步骤3:过滤累加器中line为null的无效数据(替代map里返回null)
        .filter(acc -> acc.getLine() != null)
        .map(acc -> {
        String line = acc.getLine();
        String currentFullAnswer = acc.getFullAnswer();
        ChatResponse response = new ChatResponse();

        // 空行/解析失败的累加器直接返回null(后续filter过滤)
        if (line == null) {
        return null;
        }

        // 解析SSE数据前缀
        String data = line.startsWith("data: ") ? line.substring(6) : line;
        // 2. 解析流式片段
        TongyiQianwenResponse fragment = JSON.parseObject(data, TongyiQianwenResponse.class);
        // 1. 流结束标识:返回完整响应
        if ("stop".equals(fragment.getOutput().getFinish_reason())) {
        response.setSuccess(true);
        response.setSessionId(finalSessionId);
        response.setDone(true);
        response.setAnswer(currentFullAnswer); // 完整回答
        // 保存完整回答到会话
        sessionService.saveMessage(finalSessionId, "assistant", currentFullAnswer);
        log.info("AI对话完成,sessionId: {}, 响应长度: {}", sessionId, currentFullAnswer.length());
        return response;
        }
        if (fragment == null || fragment.getOutput() == null) {
        return null;
        }

        // 3. 异常处理(非stop结束)
        if (fragment.getOutput().getFinish_reason() != null && !"null".equals(fragment.getOutput().getFinish_reason())
        && !"stop".equals(fragment.getOutput().getFinish_reason())) {
        response.setSuccess(false);
        response.setMessage(fragment.getOutput().getFinish_reason());
        response.setRequestId(fragment.getRequest_id());
        return response;
        }

        // 4. 正常片段:返回单条流式响应
        response.setSuccess(true);
        response.setSessionId(finalSessionId);
        response.setAnswer(fragment.getOutput().getText()); // 单片段文本
        response.setDone(false);
        response.setRequestId(fragment.getRequest_id());
        // 设置token用量
        response.setUsage(Map.of(
        "inputTokens", fragment.getUsage().getInput_tokens(),
        "outputTokens", fragment.getUsage().getOutput_tokens(),
        "totalTokens", fragment.getUsage().getTotal_tokens()
        ));
        return response;
        })
        // 过滤掉null响应(空行/解析失败的情况)
        .filter(res -> res != null);
        }
相关推荐
Yiii_x2 小时前
Object类与包装类
java·经验分享·笔记·课程设计·ai编程
trayvontang3 小时前
SpringBoot自动配置原理
spring boot·自动配置·spi·自动配置原理
Java水解4 小时前
springboot: Spring Boot 启动流程详解
spring boot·后端
Craaaayon5 小时前
深入浅出 Spring Event:原理剖析与实战指南
java·spring boot·后端·spring
Kings906 小时前
Spring-AI 结合自定义 mcp server 实现飞书智能机器人
spring boot·ai编程·mcp
qq_12498707536 小时前
基于springboot的智能任务管理助手小程序设计与实现(源码+论文+部署+安装)
spring boot·后端·信息可视化·微信小程序·小程序·毕业设计·计算机毕业设计
计算机学姐6 小时前
基于SpringBoot的智能家教服务平台【2026最新】
java·spring boot·后端·mysql·spring·java-ee·intellij-idea
ZLZQ_Yuan7 小时前
Spring Boot JPA
spring boot