计算机毕业设计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);
        }
相关推荐
Coder_Boy_8 小时前
基于SpringAI的在线考试系统-DDD业务领域模块设计思路
java·数据库·人工智能·spring boot·ddd
清风66666610 小时前
基于单片机的燃气热水器智能控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Voyager_411 小时前
StringRedisTemplate 和 RedisTemplate 的区别是什么?
java·spring boot
小北方城市网12 小时前
SpringBoot 全局异常处理与接口规范实战:打造健壮可维护接口
java·spring boot·redis·后端·python·spring·缓存
数说星榆18112 小时前
本科毕业设计流程图在线生成
论文阅读·毕业设计·流程图·论文笔记·毕设
Chan1612 小时前
【 微服务SpringCloud | 方案设计 】
java·spring boot·微服务·云原生·架构·intellij-idea
静听松涛13312 小时前
本科毕业论文流程图制作方法
论文阅读·毕业设计·流程图·论文笔记·毕设
hanqunfeng13 小时前
(三十三)Redisson 实战
java·spring boot·后端
HaiLang_IT13 小时前
2026年信息安全专业毕业设计选题指南,题目新颖毕业论文选题思路:从迷茫到清晰
课程设计
计算机毕设指导613 小时前
基于微信小程序的运动场馆服务系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea