[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81692":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":11,"contributorsCount":11,"subscribersCount":11,"size":11,"stars1d":13,"stars7d":14,"stars30d":14,"stars90d":11,"forks30d":11,"starsTrendScore":15,"compositeScore":11,"rankGlobal":8,"rankLanguage":8,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":17,"hasPages":17,"topics":19,"createdAt":8,"pushedAt":8,"updatedAt":20,"readmeContent":21,"aiSummary":22,"trendingCount":11,"starSnapshotCount":11,"syncStatus":23,"lastSyncTime":24,"discoverSource":25},81692,"VideoMind","qmtn23\u002FVideoMind","qmtn23",null,"Java",42,0,22,6,20,18,"Apache License 2.0",false,"main",[],"2026-06-12 02:04:18","# VideoMind\n\n**AI 视频解析与知识库构建平台**\n\n一个覆盖 **用户鉴权、视频上传、视频解析与 AI 总结** 的视频内容理解平台；同时支持上传与解析其他类型文件，基于文件内容构建专属知识库，实现高效内容检索，并借助 **RAG（检索增强生成）** 显著降低大模型回答的幻觉问题。\n\n视频通过 **FFmpeg** 抽取音轨、阿里云 **DashScope Paraformer-v2（FunASR 系列）** 转写为文本后，进入与普通文档相同的 **分块入库与向量化** 流程；文档侧可选用 **MinerU** 提升复杂 PDF 解析质量（失败自动降级 **Apache Tika**），结合 **Elasticsearch** 关键词检索 + 向量语义检索的混合召回，并采用RRF算法初排 + Rerank 精排策略，最终为大模型提供高质量上下文。\n\n仓库：\u003Chttps:\u002F\u002Fgithub.com\u002Fqmtn23\u002FVideoMind>\n\n---\n\n## 技术亮点\n\n1. **上传\u002F解析全异步化** — 基于 **Kafka** 解耦文件上传、解析与向量化流程，长视频上传接口响应时间从 50s+ 压缩到 **45ms 以内**，解析过程异步执行不阻塞用户操作。\n2. **多模态知识库** — 接入阿里云 DashScope 大模型，对视频进行 **ASR + AI 总结**；同时使用 **Tika + MinerU** 对 PDF\u002FWord\u002FPPT\u002F图片等文档做精准、完整的内容解析，构建多模态专属知识库。\n3. **混合检索 + Rerank 精排** — 对用户查询进行 **查询改写**，通过 Elasticsearch 完成 **关键词检索 + 向量语义检索** 的混合召回，先用 **线性加权策略初排**，再交由 **Rerank 模型精排**，显著提升 Top-K 准确率。\n4. **大文件分片 + 断点续传** — 用 **Redis** 维护文件分片状态，结合 **MinIO** 实现 **分片上传与断点续传**，解决了网络抖动导致的上传中断问题，大幅提高大文件传输可靠性。\n5. **分布式锁 + 幂等控制** — 落地 **Redisson + WatchDog** 分布式锁机制，结合 **MD5 内容级去重** 实现接口幂等控制，并发场景下拦截重复提交，避免不必要的 Token 损耗与重复解析。\n6. **令牌桶限流** — 基于 **Redis 令牌桶算法** 的限流策略，按单位时间设置请求上限，限制高频访问与恶意请求，保护后端 LLM\u002F向量服务。\n\n---\n\n## 技术栈\n\n| 分类 | 组件 |\n|------|------|\n| 后端 | Java 17、Spring Boot 3、Spring Data JPA、Spring Security + JWT |\n| 数据 | MySQL 8、Redis 7、Elasticsearch 8.10 |\n| 中间件 | Kafka 3.2、MinIO（对象存储） |\n| 文档\u002F媒体 | Apache Tika、MinerU（可选 PDF 增强）、FFmpeg |\n| AI 能力 | DashScope（对话 \u002F Embedding \u002F **Paraformer-v2 ASR**） |\n| 前端 | Vue 3、TypeScript、Vite、Pinia、Vue Router、Naive UI、UnoCSS、pnpm |\n\n---\n\n## 系统架构\n\n```mermaid\nflowchart LR\n    subgraph upload [Upload]\n        Browser[Browser] -->|chunk + MD5| UploadAPI[UploadController]\n        UploadAPI -->|chunk meta| Redis[(Redis)]\n        UploadAPI -->|chunk bytes| MinIO[(MinIO)]\n    end\n\n    UploadAPI -->|merged event| Kafka[(Kafka)]\n\n    subgraph consume [Async Parse]\n        Kafka --> Consumer[FileProcessingConsumer]\n        Consumer -->|video| FFmpeg[FFmpeg extract audio]\n        FFmpeg --> MinIO\n        MinIO -->|presigned URL via ngrok| DashScope[DashScope ASR]\n        DashScope -->|transcript| Chunker[Chunk + Vectorize]\n        Consumer -->|doc| Tika[Tika \u002F MinerU]\n        Tika --> Chunker\n        Chunker --> ES[(Elasticsearch)]\n        Chunker --> MySQL[(MySQL)]\n    end\n\n    subgraph query [Query]\n        User[User Query] --> Rewrite[Query Rewrite]\n        Rewrite --> Hybrid[Hybrid Search]\n        Hybrid --> ES\n        Hybrid --> Rerank[Rerank]\n        Rerank --> LLM[LLM Answer]\n    end\n```\n\n---\n\n## 环境要求\n\n| 工具 | 说明 |\n|------|------|\n| **Java 17** | 后端运行 |\n| **Maven 3.8.6+** | 构建与 `spring-boot:run` |\n| **Node.js 18.20.0+ \u002F pnpm 8.7.0+** | 前端 |\n| **Docker Desktop** | 一键启动依赖中间件 |\n| **FFmpeg** | **视频解析必需**，用于从视频抽取音轨供 ASR |\n| **DashScope API Key** | **必需**，对话\u002F向量\u002FASR 三件套都依赖（可共用一把 Key） |\n| **ngrok 或公网域名** | **视频解析必需**，DashScope 需从公网拉取 MinIO 上的临时音频 |\n\n---\n\n## 本地部署\n\n> 设计原则：**Docker 只跑中间件**；后端、前端在宿主机运行；**敏感配置放在 `application-local.yml`，已加入 `.gitignore`**。\n\n### 第 1 步：启动依赖中间件\n\n```powershell\ncd docs\ndocker compose up -d\ndocker ps   # 确认 mysql \u002F redis \u002F kafka \u002F es \u002F minio 五个容器均为 Up\n```\n\n**首次启动需创建数据库与存储桶**（仅一次）：\n\n```powershell\n# 创建 MySQL 数据库（密码以 docs\u002Fdocker-compose.yaml 为准）\ndocker exec mysql mysql -uroot -pPaiSmart2025 -e \"CREATE DATABASE IF NOT EXISTS PaiSmart CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\"\n```\n\n浏览器打开 MinIO 控制台 \u003Chttp:\u002F\u002Flocalhost:19001>（账号见 `application.yml` \u002F Compose），创建名为 `uploads` 的 Bucket。\n\n> **本机端口提示**：若你的本机 MySQL 已占用 3306，本仓库 [`docs\u002Fdocker-compose.yaml`](docs\u002Fdocker-compose.yaml) 默认把 Docker MySQL 映射到 **13306**；JDBC URL 需相应改为 `jdbc:mysql:\u002F\u002Flocalhost:13306\u002FPaiSmart`（见下一步）。\n\n**Kafka Topic（重要）**：[`application.yml`](src\u002Fmain\u002Fresources\u002Fapplication.yml) 中 `spring.kafka.topic.file-processing` 默认为 `file-processing-topic1`，DLT 为 `file-processing-dlt`。本仓库 Compose 已配置 `KAFKA_AUTO_CREATE_TOPICS_ENABLE=true`；如未自动创建，可手动在容器内执行：\n\n```powershell\ndocker exec -it kafka kafka-topics.sh --bootstrap-server localhost:9092 --create --partitions 1 --replication-factor 1 --topic file-processing-topic1\ndocker exec -it kafka kafka-topics.sh --bootstrap-server localhost:9092 --create --partitions 1 --replication-factor 1 --topic file-processing-dlt\ndocker exec -it kafka kafka-topics.sh --bootstrap-server localhost:9092 --create --partitions 1 --replication-factor 1 --topic vectorization\n```\n\n### 第 2 步：本地配置\n\n复制 [`application-local.example.yml`](src\u002Fmain\u002Fresources\u002Fapplication-local.example.yml) 为 `src\u002Fmain\u002Fresources\u002Fapplication-local.yml`，按需填写：\n\n```yaml\nspring:\n  datasource:\n    url: jdbc:mysql:\u002F\u002Flocalhost:13306\u002FPaiSmart?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true\n\njwt:\n  secret-key: \"\u003Copenssl rand -base64 32 生成的强随机字符串>\"\n\ndeepseek:\n  api:\n    key: \"\u003CDashScope API Key>\"\n\nembedding:\n  api:\n    key: \"\u003CDashScope API Key>\"\n\nasr:\n  enabled: true\n  api:\n    key: \"\u003CDashScope API Key>\"\n\nffmpeg:\n  path: \"C:\u002Fpath\u002Fto\u002Fffmpeg.exe\"   # 不在 PATH 时需配置绝对路径\n\nminio:\n  publicUrl: \"https:\u002F\u002F\u003C你的 ngrok 域名>\"   # DashScope 需从公网拉取临时音频\n```\n\n> 视频 ASR 三件必备：**有效的 `asr.api.key`**、**FFmpeg 可用**、**`minio.publicUrl` 公网可达**。任一缺失都会被启动时的 [`MinioPublicUrlChecker`](src\u002Fmain\u002Fjava\u002Fcom\u002Fqmtn\u002Fvideomind\u002Fconfig\u002FMinioPublicUrlChecker.java) 提前告警，并在视频上传时直接拒绝。\n\n### 第 3 步：启动 ngrok（视频功能必需）\n\nDashScope 服务在公网，需要能拉取到 MinIO 上的临时音频。本地开发用 ngrok 暴露 MinIO 的 **API 端口 19000**：\n\n```powershell\nngrok http --url=\u003Cyour-ngrok-domain>.ngrok-free.dev 19000\n```\n\n把输出的公网域名同步写回 `application-local.yml` 的 `minio.publicUrl`。\n\n> **重要**：本仓库已实现 **双 `MinioClient` 预签名修复**（[`MinioConfig`](src\u002Fmain\u002Fjava\u002Fcom\u002Fqmtn\u002Fvideomind\u002Fconfig\u002FMinioConfig.java)、[`MinioAudioService`](src\u002Fmain\u002Fjava\u002Fcom\u002Fqmtn\u002Fvideomind\u002Fservice\u002FMinioAudioService.java)），预签名直接用公网 endpoint 计算签名，**不会再因 ngrok Host 改写导致 S3 V4 签名 403**。\n\n### 第 4 步：启动后端\n\n```powershell\nmvn spring-boot:run \"-Dspring-boot.run.profiles=local\"\n```\n\n成功标志：日志出现 **`Started VideoMindApplication`**，端口 **8081**。\n随后会看到启动检查输出：`[视频功能] minio.publicUrl 可达，响应码: 200 — 视频 ASR 链路就绪`。\n\n### 第 5 步：启动前端\n\n```powershell\ncd frontend\npnpm install   # 仅首次\npnpm dev\n```\n\n打开终端输出的本地地址（默认 \u003Chttp:\u002F\u002Flocalhost:9527>）。默认管理员账号：**`admin` \u002F `admin123`**（由 `application.yml` 中 `admin.*` 初始化）。\n\n### 异步处理流程说明\n\n合并分片成功后，后端将解析任务发往 Kafka，由消费者异步执行解析（含视频 ASR）与向量化。因此：\n\n- **HTTP 合并接口会很快返回**（通常 \u003C100ms）\n- **可检索时间** 取决于文件大小与 ASR\u002F向量耗时；大视频会更慢\n- **排查路径**：后端日志目录 `.\u002Flogs\u002F` 下 `videomind.*.log`、`error.*.log`、`business.*.log`\n\n---\n\n## 一键启动脚本（Windows）\n\n仓库根目录提供 [`start.ps1`](start.ps1)，在 **Windows PowerShell** 下自动完成以下全流程：\n\n```\n读取 minio.publicUrl → 检查 Docker 容器 → 启动 ngrok → 启动后端 → 启动前端\n```\n\n### 前提条件\n\n在运行脚本前，请确认：\n\n1. **Docker Desktop 已启动**，且 5 个容器（`mysql` \u002F `redis` \u002F `kafka` \u002F `es` \u002F `minio`）均为 `Up` 状态（`docker ps` 验证）。\n2. **`src\u002Fmain\u002Fresources\u002Fapplication-local.yml` 已存在**，并正确填写了 API Key、数据库端口等敏感配置（参考 `application-local.example.yml`）。\n3. **ngrok 已安装**（`ngrok version` 能正常输出），且 `minio.publicUrl` 配置了你的 ngrok 固定域名（视频功能必需；普通文档功能不受影响）。\n\n### 用法\n\n```powershell\n# 首次运行：放开当前用户的脚本执行策略\nSet-ExecutionPolicy -Scope CurrentUser RemoteSigned\n\n# 一键启动（默认 profile=local）\n.\\start.ps1\n\n# 指定 Spring profile\n.\\start.ps1 -SpringProfile dev\n```\n\n脚本执行顺序：\n\n| 步骤 | 动作 | 说明 |\n|------|------|------|\n| 0 | 读取 `minio.publicUrl` | 从 `application-local.yml` 解析 ngrok 域名；未配置时跳过 ngrok，打印 WARN |\n| 1 | 检查 Docker 容器 | 检测 mysql \u002F redis \u002F kafka \u002F es \u002F minio 是否均在运行；缺失则询问是否继续 |\n| 2 | 启动 ngrok | 若域名已配置且 ngrok 未运行，自动执行 `ngrok http --url=\u003C域名> 19000`；已运行则跳过 |\n| 3 | 启动后端 | 若 8081 端口空闲，在新 PowerShell 窗口运行 `mvn spring-boot:run`；已占用则跳过 |\n| 4 | 启动前端 | 若 9527 端口空闲，在新 PowerShell 窗口运行 `pnpm dev`；已占用则跳过 |\n\n启动完成后控制台会输出访问地址：\n\n```\nFrontend: http:\u002F\u002Flocalhost:9527\nBackend:  http:\u002F\u002Flocalhost:8081\nngrok:    http:\u002F\u002F127.0.0.1:4040\nLogin:    admin \u002F admin123\n```\n\n> 后端和前端在**各自独立的新窗口**中运行，关闭对应窗口即可停止对应服务。\n> Spring Boot 首次启动编译约需 30-60 秒，前端首次运行会自动安装依赖（`pnpm install`），请稍等。\n\n### 常见问题\n\n| 现象 | 原因 | 解决方式 |\n|------|------|----------|\n| 脚本报\"无法加载，因为在此系统上禁止运行脚本\" | 执行策略限制 | 运行 `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned` |\n| `[WARN] Docker 未就绪` | Docker Desktop 未启动 | 启动 Docker Desktop，等待引擎就绪后重试 |\n| `[WARN] 以下容器未运行: kafka, es` | 容器未启动 | 执行 `cd docs; docker compose up -d` 后重试 |\n| `[WARN] 端口 8081 已占用，跳过后端启动` | 上次后端窗口未关闭 | 关闭旧后端窗口或 `Stop-Process -Id \u003Cpid> -Force` 后重试 |\n| `[WARN] ngrok 15 秒未就绪` | ngrok 启动慢或账号未授权 | 手动运行 `ngrok http --url=\u003C域名> 19000` 并检查 \u003Chttp:\u002F\u002F127.0.0.1:4040> |\n\n---\n\n## 配置说明（摘要）\n\n| 配置块 | 用途 |\n|--------|------|\n| `deepseek.api.*` | 对话 LLM（与 DashScope 兼容） |\n| `embedding.api.*` | 文本向量（DashScope text-embedding-v4） |\n| `asr.*` | **核心必选** — 视频 ASR（依赖 FFmpeg + 有效 Key + 公网 `minio.publicUrl`） |\n| `mineru.api.*` | 可选 — PDF 高质量解析（失败自动降级 Tika） |\n| `minio.*` | 对象存储；`bucketName` 默认 `uploads`；`publicUrl` 用于 DashScope 公网拉取 |\n| `spring.kafka.*` | 异步解析队列；Topic 名须与实际 Kafka 一致 |\n\n---\n\n## 常见问题\n\n| 问题 | 排查方向 |\n|------|----------|\n| MySQL `Access denied` | 确认 JDBC 端口（13306 还是 3306）与 root 密码与 Compose 一致 |\n| MinIO 上传报 `bucket does not exist` | 在 \u003Chttp:\u002F\u002Flocalhost:19001> 创建 `uploads` 桶 |\n| 上传成功但分块为空 \u002F 检索不到 | 看 `.\u002Flogs\u002Ferror.*.log` 是否有解析异常；检查 Kafka Topic 是否一致 |\n| 视频 ASR `FILE_DOWNLOAD_FAILED` | ngrok 未启动 \u002F `minio.publicUrl` 与实际域名不一致 \u002F 域名失效 |\n| 视频 ASR `FILE_403_FORBIDDEN` | 旧的预签名 Host 改写已修复；若仍出现，确认后端启动后是否输出「视频 ASR 链路就绪」 |\n| 视频上传被拒「ASR 未就绪」 | `asr.api.key` \u002F `ffmpeg.path` \u002F `minio.publicUrl` 三者必须齐备 |\n| FFmpeg 找不到 | 终端 `ffmpeg -version` 自检；或在 `application-local.yml` 配 `ffmpeg.path` 绝对路径 |\n| 端口 8081 已被占用 | 结束占用进程或修改 `server.port`（前后端代理同步） |\n| 前端「生成摘要」超时 | 摘要为同步长耗时接口，前端已对其单独放宽超时；如仍超时可调更大或异步化 |\n| 启动时 WARN「minio.publicUrl 不可达」 | [`MinioPublicUrlChecker`](src\u002Fmain\u002Fjava\u002Fcom\u002Fqmtn\u002Fvideomind\u002Fconfig\u002FMinioPublicUrlChecker.java) 的告警，按框内提示逐项排查（ngrok \u002F Docker \u002F 域名一致性） |\n\n---\n\n## 开发与安全\n\n- **不要** 将 `application-local.yml`、真实 Key、JWT secret 提交到公开仓库\n- 发布前用 `git diff` \u002F `git log` 自检敏感信息\n- 生产部署需补充：HTTPS、密钥管理、Kafka 分区\u002F消费组扩容、对象存储 CDN 等\n- 本仓库不附带应用镜像 Dockerfile，生产需自行打包 JAR + 静态资源\n\n---\n\n## License\n\n以仓库根目录 [LICENSE](LICENSE) 文件为准。\n\n如果这个项目对您有帮助，还请给个星星\n\n","VideoMind 是一个用于视频解析与知识库构建的AI平台，支持用户鉴权、视频上传、视频解析与AI总结等功能，并能够处理其他类型文件以构建专属知识库。该项目采用Java开发，通过FFmpeg抽取视频音轨并利用阿里云DashScope Paraformer-v2进行转写，结合Elasticsearch实现高效的内容检索和RAG技术减少大模型回答中的幻觉问题。其核心功能包括全异步化的文件上传与解析流程、多模态知识库构建、混合检索策略以及大文件分片上传等，适用于需要对大量多媒体资料进行结构化处理和智能检索的场景，如在线教育、企业内部培训资料管理等领域。",2,"2026-06-11 04:05:59","CREATED_QUERY"]