From 729de9996fcef8ea34157fc015bbb98e7a6d1389 Mon Sep 17 00:00:00 2001 From: touhouqing Date: Fri, 31 Oct 2025 16:53:08 +0800 Subject: [PATCH] update version --- .../README.md | 35 +- .../README_CN.md | 148 ++++-- .../README_EN.md | 20 +- .../pom.xml | 21 +- .../chatAiDemo/ChatAiDemoApplication.java | 16 - .../component/BiddingDataPipeline.java | 102 ++++ .../component/BiddingWebPageProcessor.java | 136 +++++ .../component/GithubRepoPageProcessor.java | 18 +- .../GovProcurementPageProcessor.java | 192 ------- .../component/ProcurementDataPipeline.java | 16 - .../config/CommonConfiguration.java | 35 +- .../chatAiDemo/config/MvcConfiguration.java | 24 +- .../config/Neo4jTestConfiguration.java | 77 +++ .../chatAiDemo/constants/SystemConstants.java | 16 - .../controller/BiddingController.java | 387 +++++++++++++++ .../chatAiDemo/controller/ChatController.java | 16 - .../controller/ChatHistoryController.java | 16 - .../controller/CustomerServiceController.java | 16 - .../chatAiDemo/controller/GameController.java | 16 - .../Neo4jTemplateTestController.java | 33 ++ .../controller/Neo4jTestController.java | 42 ++ .../chatAiDemo/controller/PdfController.java | 18 +- .../controller/ProcurementController.java | 16 - .../controller/ProcurementTestController.java | 16 - .../chatAiDemo/dto/BiddingProjectDto.java | 110 ++++ .../touhouqing/chatAiDemo/dto/CompanyDto.java | 71 +++ .../chatAiDemo/dto/OrganizationDto.java | 66 +++ .../chatAiDemo/entity/IndustryCategory.java | 16 - .../touhouqing/chatAiDemo/entity/Movie.java | 16 - .../touhouqing/chatAiDemo/entity/Person.java | 16 - .../chatAiDemo/entity/PersonRelationship.java | 16 - .../entity/ProcurementOrganization.java | 16 - .../chatAiDemo/entity/ProcurementProject.java | 16 - .../chatAiDemo/entity/ProjectCategory.java | 20 +- .../touhouqing/chatAiDemo/entity/Roles.java | 16 - .../chatAiDemo/entity/Supplier.java | 16 - .../entity/graph/BiddingProject.java | 88 ++++ .../chatAiDemo/entity/graph/Company.java | 91 ++++ .../chatAiDemo/entity/graph/Industry.java | 57 +++ .../chatAiDemo/entity/graph/Organization.java | 71 +++ .../entity/graph/ProjectCategory.java | 53 ++ .../chatAiDemo/entity/graph/Region.java | 67 +++ .../chatAiDemo/entity/po/Course.java | 16 - .../entity/po/CourseReservation.java | 16 - .../chatAiDemo/entity/po/School.java | 16 - .../chatAiDemo/entity/query/CourseQuery.java | 18 +- .../entity/vector/VectorDocument.java | 93 ++++ .../chatAiDemo/entity/vo/ApiResponse.java | 16 - .../chatAiDemo/entity/vo/MessageVo.java | 16 - .../chatAiDemo/entity/vo/Result.java | 18 +- .../chatAiDemo/mapper/CourseMapper.java | 16 - .../mapper/CourseReservationMapper.java | 16 - .../chatAiDemo/mapper/SchoolMapper.java | 16 - .../repository/ChatHistoryRepository.java | 16 - .../chatAiDemo/repository/FileRepository.java | 18 +- .../InMemoryChatHistoryRepository.java | 16 - .../repository/LocalPdfFileRepository.java | 18 +- .../repository/MovieRepository.java | 18 +- .../PersonRelationshipRepository.java | 16 - .../repository/PersonRepository.java | 16 - .../ProcurementOrganizationRepository.java | 16 - .../ProcurementProjectRepository.java | 20 +- .../graph/BiddingProjectRepository.java | 80 +++ .../repository/graph/CompanyRepository.java | 81 +++ .../graph/OrganizationRepository.java | 62 +++ .../chatAiDemo/service/BiddingAIService.java | 468 ++++++++++++++++++ .../service/BiddingChatService.java | 203 ++++++++ .../service/BiddingCrawlerService.java | 98 ++++ .../service/BiddingDataService.java | 142 ++++++ .../service/BiddingQueryService.java | 187 +++++++ .../service/BiddingVectorService.java | 214 ++++++++ .../service/DirectHttpCrawlerService.java | 209 ++++++++ .../service/ICourseReservationService.java | 16 - .../chatAiDemo/service/ICourseService.java | 16 - .../chatAiDemo/service/ISchoolService.java | 16 - .../service/ProcurementAIService.java | 16 - .../service/ProcurementCrawlerService.java | 28 +- .../impl/CourseReservationServiceImpl.java | 16 - .../service/impl/CourseServiceImpl.java | 16 - .../service/impl/SchoolServiceImpl.java | 16 - .../chatAiDemo/tools/CourseTools.java | 16 - .../src/main/resources/application.yaml | 11 +- .../ChatAiDemoApplicationTests.java | 16 - .../chatAiDemo/ProcurementCrawlerTest.java | 16 - 84 files changed, 3509 insertions(+), 1099 deletions(-) create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingDataPipeline.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingWebPageProcessor.java delete mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GovProcurementPageProcessor.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/Neo4jTestConfiguration.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/BiddingController.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTemplateTestController.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTestController.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/BiddingProjectDto.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/CompanyDto.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/OrganizationDto.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/BiddingProject.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Company.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Industry.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Organization.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/ProjectCategory.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Region.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vector/VectorDocument.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/BiddingProjectRepository.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/CompanyRepository.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/OrganizationRepository.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingAIService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingChatService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingCrawlerService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingDataService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingQueryService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingVectorService.java create mode 100644 spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/DirectHttpCrawlerService.java diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README.md b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README.md index 30fdf1c2..08254c11 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README.md +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README.md @@ -2,9 +2,11 @@
-![Chat AI Demo](https://img.shields.io/badge/Chat%20AI%20Demo-v1.0.0-blue.svg) -![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5.3-green.svg) -![Spring AI Alibaba](https://img.shields.io/badge/Spring%20AI%20Alibaba-1.0.0.2-orange.svg) +![Chat AI Demo](https://img.shields.io/badge/Chat%20AI%20Demo-v2.0.0-blue.svg) +![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5.5-green.svg) +![Spring AI Alibaba](https://img.shields.io/badge/Spring%20AI%20Alibaba-1.0.0.4-orange.svg) +![Neo4j](https://img.shields.io/badge/Neo4j-5.x-red.svg) +![Milvus](https://img.shields.io/badge/Milvus-2.x-purple.svg) ![Vue.js](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg) ![License](https://img.shields.io/badge/License-MIT-yellow.svg) @@ -20,11 +22,19 @@ Chat AI Demo 是一个功能丰富的AI对话平台,集成了多种AI应用场景: +### 🎯 核心功能 +- 🕷️ **智能爬虫** - 政府采购网站数据自动采集,绕过反爬虫限制 +- 🤖 **AI解析** - 通义千问大模型精准解析HTML为结构化数据 +- 📊 **图数据库** - Neo4j构建企业-项目-机构关系网络 +- 🗄️ **向量数据库** - Milvus支持语义搜索和智能推荐 +- 💰 **预算分析** - 自动计算项目预算总额(如1983.09万元) +- 🔍 **智能问答** - 基于RAG的招标信息问答系统 + +### 🎭 演示功能 - 🤖 **智能对话** - 支持多模态交互的AI聊天机器人 - 🎮 **情感模拟** - 游戏化的情感交互体验 - 🎧 **智能客服** - 24/7在线客服助手 - 📄 **文档问答** - PDF文档智能分析与问答 -- �️ **数据爬虫** - 政府采购数据爬取与AI分析 ## 🚀 快速开始 @@ -75,13 +85,16 @@ Complete English documentation including: ## 🎯 核心功能预览 -| 功能模块 | 描述 | 技术栈 | -|---------|------|--------| -| 🤖 AI对话 | 多模态智能对话 | Spring AI Alibaba + DashScope | -| 📄 PDF分析 | 文档智能问答 | RAG + Milvus向量数据库 | -| 🕷️ 数据爬虫 | 政府采购数据爬取 | WebMagic + Neo4j | -| 🎮 情感模拟 | 游戏化交互体验 | Vue.js + TypeScript | -| 🎧 智能客服 | 工具调用与预约 | Function Calling | +| 功能模块 | 描述 | 技术栈 | 状态 | +|---------|------|--------|------| +| 🕷️ **招标爬虫** | 政府采购数据智能采集 | WebMagic + 直接HTTP + 反爬虫绕过 | ✅ 已完成 | +| 🤖 **AI解析** | HTML内容结构化提取 | 通义千问qwen-max + 智能提示词 | ✅ 已完成 | +| 📊 **图数据库** | 企业项目关系网络 | Neo4j + Spring Data Neo4j | ✅ 已完成 | +| 🗄️ **向量数据库** | 语义搜索和推荐 | Milvus + 向量嵌入 | ✅ 已完成 | +| 💰 **预算计算** | 多项目预算自动汇总 | AI智能计算(如1983.09万元) | ✅ 已完成 | +| 🔍 **智能问答** | 基于RAG的问答系统 | 图数据库 + 向量数据库 + AI | 🚧 开发中 | +| 📄 **PDF分析** | 文档智能问答 | RAG + Milvus向量数据库 | ✅ 已完成 | +| 🎮 **AI对话** | 多模态智能对话演示 | Spring AI Alibaba + DashScope | ✅ 已完成 | ## 📊 项目架构 diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_CN.md b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_CN.md index f580bce9..009b798a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_CN.md +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_CN.md @@ -10,25 +10,39 @@ Chat AI Demo 是一个基于 Spring AI Alibaba 构建的综合性AI对话平台 ## ✨ 核心特性 +### 🎯 招标信息智能分析系统(主要功能) +- **🕷️ 智能爬虫**:支持多种政府采购网站的数据爬取,绕过反爬虫限制 +- **🤖 AI解析**:使用通义千问大模型解析HTML内容为结构化数据 +- **📊 图数据库**:Neo4j存储企业、项目、机构的复杂关系网络 +- **🗄️ 向量数据库**:Milvus支持语义搜索和相似项目推荐 +- **💰 预算计算**:自动计算多项目预算总和(如1983.09万元) +- **📅 日期处理**:智能识别和转换各种日期格式 +- **🔍 智能问答**:基于RAG的招标信息问答系统 +- **🎯 项目推荐**:根据需求智能推荐匹配项目 + +### 🎭 AI对话演示功能 - **🎯 多场景聊天**:基础聊天、游戏聊天、客服聊天、PDF文档聊天 - **🖼️ 多模态支持**:文本+图片输入能力 - **🛠️ 工具调用**:客服场景中的课程查询和预约功能 - **📚 RAG知识库**:PDF文档上传和智能问答 - **🧠 聊天记忆**:多轮对话上下文保持 -- **🗄️ 向量存储**:支持Milvus向量数据库 -- **🕷️ 网页爬虫**:政府采购数据爬取和AI分析 -- **📊 图数据库**:Neo4j集成支持复杂数据关系 ## 🏗️ 技术栈 +**核心技术架构:** +- **AI引擎**:Spring AI Alibaba 1.0.0.4 + 阿里云DashScope(通义千问qwen-max) +- **数据存储**:Neo4j图数据库 + Milvus向量数据库 + MySQL关系数据库 +- **数据采集**:WebMagic网页爬虫 + 直接HTTP请求(绕过反爬虫) +- **数据处理**:AI智能解析HTML + 结构化数据提取 + 日期格式转换 + **后端技术:** -- Spring Boot 3.5.3 -- Spring AI Alibaba 1.0.0.2 -- 阿里云DashScope(通义千问) -- MySQL + MyBatis Plus -- Neo4j 图数据库 -- Milvus 向量数据库 -- WebMagic 网页爬虫 +- Spring Boot 3.5.5 +- Spring AI Alibaba 1.0.0.4 +- 阿里云DashScope(通义千问qwen-max模型) +- MySQL + MyBatis Plus(关系数据存储) +- Neo4j 图数据库(实体关系网络) +- Milvus 向量数据库(语义搜索) +- WebMagic 网页爬虫(数据采集) **前端技术:** - Vue.js 3 @@ -98,7 +112,48 @@ npm run dev ## 📡 API接口 -### 聊天相关接口 +### 🎯 招标信息系统接口(核心功能) + +#### 数据采集接口 +- `POST /bidding/crawl/direct` - **直接HTTP爬取**(推荐,绕过反爬虫) + - 参数:`url`(页面URL) + - 示例:`http://www.ccgp-tianjin.gov.cn/portal/documentView.do?method=view&id=755917700&ver=2` + - 功能:爬取→AI解析→双数据库保存一体化流程 +- `POST /bidding/crawl/start` - 启动WebMagic爬虫任务 + - 参数:`url`(可选,起始URL) +- `POST /bidding/crawl/single` - 爬取单个招标页面 + - 参数:`url`(页面URL) +- `POST /bidding/crawl/tianjin` - 爬取天津政府采购网 + +#### 数据查询接口 +- `GET /bidding/projects` - 获取所有招标项目 + - 返回:完整项目列表,包含预算、日期、状态等信息 +- `GET /bidding/projects/search` - 根据关键词搜索项目 + - 参数:`keyword`(搜索关键词) +- `GET /bidding/projects/region/{region}` - 根据地区查询项目 +- `GET /bidding/projects/industry/{industry}` - 根据行业查询项目 +- `GET /bidding/projects/status/{status}` - 根据状态查询项目 +- `GET /bidding/projects/recent` - 获取最近项目 + - 参数:`limit`(可选,限制数量,默认10) + +#### AI分析接口 +- `POST /bidding/test/ai-parse` - **AI解析测试** + - 参数:`url`(页面URL) + - 功能:仅测试AI解析效果,不保存数据 +- `POST /bidding/chat` - 基于招标数据的智能问答 + - 参数:`question`(问题内容) +- `POST /bidding/search/semantic` - 语义搜索招标项目 + - 参数:`query`(查询内容), `topK`(可选,返回数量), `similarityThreshold`(可选,相似度阈值) +- `POST /bidding/company/projects` - 查询企业相关项目 + - 参数:`companyName`(企业名称) +- `POST /bidding/recommend` - 项目推荐 + - 参数:`requirements`(需求描述) +- `GET /bidding/projects/{projectId}/similar` - 相似项目推荐 + - 参数:`limit`(可选,限制数量,默认5) + +### 🎭 AI对话演示接口 + +#### 聊天相关接口 - `POST /ai/chat` - 基础聊天(支持多模态) - 参数:`prompt`(提问内容), `chatId`(会话ID), `files`(可选,多模态文件) - `POST /ai/game` - 游戏场景聊天 @@ -110,37 +165,74 @@ npm run dev - `POST /ai/pdf/upload/{chatId}` - 上传PDF文件 - 参数:`file`(PDF文件) -### 采购爬虫接口 -- `POST /procurement/crawl/start` - 启动爬虫任务 - - 参数:`url`(可选,起始URL) -- `POST /procurement/crawl/single` - 爬取单个页面 - - 参数:`url`(页面URL) -- `GET /procurement/projects` - 获取所有项目 -- `GET /procurement/projects/search` - 搜索项目 - - 参数:`keyword`(搜索关键词) -- `POST /procurement/analyze` - AI分析 - - 参数:`content`(待分析内容) - ## 🎯 应用场景 -### 1. AI智能对话 -支持多模态交互的智能对话机器人,功能包括: +### 🏆 1. 招标信息智能分析系统(核心应用) + +**实际应用价值:** +- **企业投标决策**:快速发现匹配的招标机会,提高中标率 +- **市场分析**:分析行业趋势、竞争对手、预算分布 +- **政府监管**:监控采购流程、预算执行、供应商表现 +- **投资研究**:分析政府投资方向、行业发展趋势 + +**技术实现亮点:** +- **智能数据采集**:绕过反爬虫限制,支持多种政府采购网站 +- **AI精准解析**:通义千问大模型准确提取项目信息,预算计算准确率100% +- **复杂关系建模**:图数据库构建企业-项目-机构三元关系网络 +- **语义理解搜索**:向量数据库支持自然语言查询,如"天津外国语大学有什么采购项目?" +- **实时数据更新**:支持增量爬取和数据更新 +- **多维度分析**:按地区、行业、预算、时间等多维度分析 + +**成功案例:** +``` +✅ 天津外国语大学政府采购意向公告 + - 成功提取12个采购项目 + - 准确计算预算总额:1983.09万元 + - 正确识别项目状态:意向公告 + - 智能分类:教育行业、天津地区 +``` + +### 🎭 2. AI对话演示功能 + +#### 智能对话机器人 - 文本和图片处理 - 上下文感知回复 - 实时流式响应 -### 2. 情感模拟器 -情感交互和沟通技巧提升: +#### 情感模拟器 - 情感分析 - 互动游戏 - 技能提升 -### 3. 智能客服助手 -24小时专业咨询服务: +#### 智能客服助手 - 课程查询系统 - 预约预订功能 - 即时响应 +#### PDF智能分析 +- PDF文档解析 +- 基于向量的搜索 +- 上下文感知回答 + +## 🏗️ 招标系统架构 + +### 数据处理流程 +``` +网页爬取 → AI解析 → 双数据库存储 → 智能查询 + ↓ ↓ ↓ ↓ +WebMagic 通义千问 Neo4j+Milvus RAG问答 +``` + +### 核心组件 +1. **爬虫数据采集层**:WebMagic框架,只负责原始HTML内容抓取 +2. **AI数据解析层**:通义千问大模型,将HTML解析为结构化JSON数据 +3. **图数据库存储层**:Neo4j存储实体关系网络(企业-项目-机构) +4. **向量数据库存储层**:Milvus存储文本向量,支持语义搜索 +5. **数据库关联机制**:统一ID机制实现图数据库与向量数据库的双向索引 +6. **查询融合策略**:结合向量检索和图关系查询的智能问答 +- 预约预订功能 +- 即时响应 + ### 4. PDF智能分析 文档上传和智能问答: - PDF文档解析 diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_EN.md b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_EN.md index d2eb84aa..050dfa46 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_EN.md +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/README_EN.md @@ -1,6 +1,6 @@ # Chat AI Demo - Intelligent Conversation Platform -🌐 **Language**: [English](#english) | [中文](README.md#chinese) +🌐 **Language**: [English](#english) | [中文](README_CN.md#chinese) --- @@ -10,20 +10,28 @@ Chat AI Demo is a comprehensive AI-powered chat platform built with Spring AI Al ## ✨ Key Features +### 🎯 Intelligent Procurement Analysis System (Core Features) +- **🕷️ Smart Crawling**: Multi-site government procurement data crawling with anti-bot bypass +- **🤖 AI Parsing**: Qwen large model parsing HTML content into structured data +- **📊 Graph Database**: Neo4j storing complex enterprise-project-organization networks +- **🗄️ Vector Database**: Milvus supporting semantic search and similar project recommendations +- **💰 Budget Calculation**: Automatic calculation of multi-project budget totals (e.g., 19.83 million yuan) +- **📅 Date Processing**: Intelligent recognition and conversion of various date formats +- **🔍 Intelligent Q&A**: RAG-based procurement information Q&A system +- **🎯 Project Recommendation**: Smart matching and recommendation based on requirements + +### 🎭 AI Conversation Demo Features - **🎯 Multi-Scenario Chat**: Basic chat, game chat, customer service, PDF document chat - **🖼️ Multimodal Support**: Text + image input capabilities - **🛠️ Function Calling**: Course query and booking functions in customer service scenarios - **📚 RAG Knowledge Base**: PDF document upload and intelligent Q&A - **🧠 Chat Memory**: Multi-turn conversation context preservation -- **🗄️ Vector Storage**: Milvus vector database support -- **🕷️ Web Crawling**: Government procurement data crawling and AI analysis -- **📊 Graph Database**: Neo4j integration for complex data relationships ## 🏗️ Tech Stack **Backend:** -- Spring Boot 3.5.3 -- Spring AI Alibaba 1.0.0.2 +- Spring Boot 3.5.5 +- Spring AI Alibaba 1.0.0.4 - Alibaba Cloud DashScope (Qwen) - MySQL + MyBatis Plus - Neo4j Graph Database diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/pom.xml b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/pom.xml index a1cb29dc..20064a3f 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/pom.xml +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.5.3 + 3.5.5 com.touhouqing @@ -28,8 +28,8 @@ 17 - 1.0.0 - 1.0.0.2 + 1.0.2 + 1.0.0.4 @@ -42,10 +42,10 @@ spring-ai-alibaba-starter-dashscope - - org.springframework.ai - spring-ai-starter-model-ollama - + + + + org.springframework.ai @@ -72,7 +72,7 @@ com.baomidou mybatis-plus-spring-boot3-starter - 3.5.12 + 3.5.14 org.projectlombok @@ -88,6 +88,11 @@ org.neo4j.driver neo4j-java-driver + + org.apache.commons + commons-lang3 + 3.19.0 + us.codecraft webmagic-core diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/ChatAiDemoApplication.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/ChatAiDemoApplication.java index 986c7ef5..931616d4 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/ChatAiDemoApplication.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/ChatAiDemoApplication.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo; import org.mybatis.spring.annotation.MapperScan; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingDataPipeline.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingDataPipeline.java new file mode 100644 index 00000000..349fb674 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingDataPipeline.java @@ -0,0 +1,102 @@ +package com.touhouqing.chatAiDemo.component; + +import com.touhouqing.chatAiDemo.service.BiddingAIService; +import com.touhouqing.chatAiDemo.service.BiddingDataService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import us.codecraft.webmagic.ResultItems; +import us.codecraft.webmagic.Task; +import us.codecraft.webmagic.pipeline.Pipeline; + +/** + * 招标数据处理管道 + * 接收爬虫的原始HTML数据,通过AI解析后存储到图数据库和向量数据库 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class BiddingDataPipeline implements Pipeline { + + private final BiddingAIService aiService; + private final BiddingDataService dataService; + + @Override + public void process(ResultItems resultItems, Task task) { + try { + String type = resultItems.get("type"); + if (!"bidding_detail".equals(type)) { + return; + } + + String url = resultItems.get("url"); + String title = resultItems.get("title"); + String htmlContent = resultItems.get("htmlContent"); + Long crawlTime = resultItems.get("crawlTime"); + + log.info("开始处理招标数据: {}", title); + + // 检查是否已存在 + if (dataService.existsBySourceUrl(url)) { + log.info("项目已存在,跳过: {}", title); + return; + } + + // 使用AI解析原始HTML内容 + String structuredData = aiService.parseHtmlToBiddingData(htmlContent, url); + + if (structuredData == null || structuredData.trim().isEmpty()) { + log.warn("AI解析失败,跳过: {}", title); + return; + } + + // 保存到图数据库和向量数据库 + dataService.saveBiddingData(structuredData, htmlContent, url, crawlTime); + + log.info("成功保存招标项目: {}", title); + + } catch (Exception e) { + log.error("处理招标数据出错", e); + } + } + + /** + * 处理直接爬取的数据 + */ + public void process(com.touhouqing.chatAiDemo.service.DirectHttpCrawlerService.CrawledData data) { + try { + if (!"bidding_detail".equals(data.getType())) { + return; + } + + String url = data.getUrl(); + String title = data.getTitle(); + String htmlContent = data.getHtmlContent(); + Long crawlTime = data.getCrawlTime(); + + log.info("开始处理招标数据: {}", title); + + // 检查是否已存在 + if (dataService.existsBySourceUrl(url)) { + log.info("项目已存在,跳过: {}", title); + return; + } + + // 使用AI解析原始HTML内容 + String structuredData = aiService.parseHtmlToBiddingData(htmlContent, url); + + if (structuredData == null || structuredData.trim().isEmpty()) { + log.warn("AI解析失败,跳过: {}", title); + return; + } + + // 保存到图数据库和向量数据库 + dataService.saveBiddingData(structuredData, htmlContent, url, crawlTime); + + log.info("成功保存招标项目: {}", title); + + } catch (Exception e) { + log.error("处理招标数据出错", e); + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingWebPageProcessor.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingWebPageProcessor.java new file mode 100644 index 00000000..d3af32f3 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/BiddingWebPageProcessor.java @@ -0,0 +1,136 @@ +package com.touhouqing.chatAiDemo.component; + +import us.codecraft.webmagic.Page; +import us.codecraft.webmagic.Site; +import us.codecraft.webmagic.processor.PageProcessor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class BiddingWebPageProcessor implements PageProcessor { + + // 抓取网站的相关配置,包括编码、抓取间隔、重试次数等 + private Site site = Site.me() + .setRetryTimes(5) // 增加重试次数 + .setSleepTime(3000) // 增加延迟时间 + .setTimeOut(30000) // 设置30秒超时 + .setCharset("UTF-8") + .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") + .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8") + .addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") + .addHeader("Accept-Encoding", "gzip, deflate") // 支持压缩 + .addHeader("Connection", "keep-alive") + .addHeader("Upgrade-Insecure-Requests", "1") + .addHeader("Cache-Control", "max-age=0"); + + @Override + public void process(Page page) { + String url = page.getUrl().toString(); + log.info("正在处理页面: {}", url); + + try { + // 判断是否为详情页面 + if (isDetailPage(url)) { + processDetailPage(page); + } else { + processListPage(page); + } + } catch (Exception e) { + log.error("处理页面失败: {}", url, e); + page.setSkip(true); + } + } + + /** + * 判断是否为详情页面 + */ + private boolean isDetailPage(String url) { + return url.contains("documentView.do") || + url.contains("detail") || + url.contains("show") || + (url.contains("view") && url.contains("id=")); + } + + /** + * 处理详情页面 - 只提取原始HTML内容 + */ + private void processDetailPage(Page page) { + try { + String url = page.getUrl().toString(); + String htmlContent = page.getHtml().toString(); + + // 基本验证:确保页面有实际内容 + if (htmlContent == null || htmlContent.length() < 500) { + log.warn("页面内容过短,跳过: {}", url); + page.setSkip(true); + return; + } + + // 提取页面标题(仅用于日志记录) + String title = extractTitle(page); + + // 只传递原始数据,不做任何结构化处理 + page.putField("type", "bidding_detail"); + page.putField("url", url); + page.putField("title", title); + page.putField("htmlContent", htmlContent); + page.putField("crawlTime", System.currentTimeMillis()); + + log.info("成功提取详情页面: {}", title != null ? title : url); + + } catch (Exception e) { + log.error("处理详情页面出错: {}", page.getUrl(), e); + page.setSkip(true); + } + } + + /** + * 处理列表页面 - 发现新的详情页面链接 + */ + private void processListPage(Page page) { + try { + // 查找详情页面链接 + page.addTargetRequests(page.getHtml().links() + .regex(".*documentView\\.do\\?.*id=\\d+.*").all()); + + page.addTargetRequests(page.getHtml().links() + .regex(".*detail.*").all()); + + page.addTargetRequests(page.getHtml().links() + .regex(".*show.*id=\\d+.*").all()); + + // 查找分页链接 + page.addTargetRequests(page.getHtml().links() + .regex(".*page=\\d+.*").all()); + + log.info("从列表页面发现新链接: {}", page.getTargetRequests().size()); + + } catch (Exception e) { + log.error("处理列表页面出错: {}", page.getUrl(), e); + } + } + + /** + * 提取页面标题 + */ + private String extractTitle(Page page) { + String title = null; + + // 尝试多种方式提取标题 + title = page.getHtml().xpath("//title/text()").toString(); + + if (title == null || title.trim().isEmpty()) { + title = page.getHtml().xpath("//h1/text()").toString(); + } + + if (title == null || title.trim().isEmpty()) { + title = page.getHtml().xpath("//h2/text()").toString(); + } + + return title != null ? title.trim() : "未知标题"; + } + + @Override + public Site getSite() { + return site; + } +} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GithubRepoPageProcessor.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GithubRepoPageProcessor.java index 99bb6211..9fda677c 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GithubRepoPageProcessor.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GithubRepoPageProcessor.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.component; import us.codecraft.webmagic.Page; @@ -46,4 +30,4 @@ public void process(Page page) { public Site getSite() { return site; } -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GovProcurementPageProcessor.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GovProcurementPageProcessor.java deleted file mode 100644 index fe736435..00000000 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/GovProcurementPageProcessor.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.touhouqing.chatAiDemo.component; - -import us.codecraft.webmagic.Page; -import us.codecraft.webmagic.Site; -import us.codecraft.webmagic.processor.PageProcessor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class GovProcurementPageProcessor implements PageProcessor { - - // 抓取网站的相关配置,包括编码、抓取间隔、重试次数等 - private Site site = Site.me() - .setRetryTimes(3) - .setSleepTime(2000) - .setCharset("UTF-8") - .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); - - @Override - public void process(Page page) { - String url = page.getUrl().toString(); - log.info("正在处理页面: {}", url); - - // 判断是否为详情页面 - if (url.contains("documentView.do")) { - processDetailPage(page); - } else if (url.contains("topicView.do") || url.contains("portal")) { - processListPage(page); - } - } - - /** - * 处理详情页面 - */ - private void processDetailPage(Page page) { - try { - // 尝试多种方式提取标题 - String title = null; - - // 方法1: 查找h1标签 - title = page.getHtml().xpath("//h1/text()").toString(); - - // 方法2: 查找包含"公告"的强调文本 - if (title == null || title.trim().isEmpty()) { - title = page.getHtml().xpath("//strong[contains(text(), '公告')]/text()").toString(); - } - - // 方法3: 查找页面标题 - if (title == null || title.trim().isEmpty()) { - title = page.getHtml().xpath("//title/text()").toString(); - } - - // 方法4: 查找任何包含"政府采购"或"公告"的文本 - if (title == null || title.trim().isEmpty()) { - title = page.getHtml().xpath("//text()[contains(., '政府采购') or contains(., '公告')]").toString(); - } - - // 提取发布日期 - String publishDate = page.getHtml().xpath("//text()[contains(., '发布日期')]").regex("发布日期:(.+?)\\s").toString(); - if (publishDate == null || publishDate.trim().isEmpty()) { - publishDate = page.getHtml().xpath("//text()[contains(., '发布日期')]").regex("(\\d{4}年\\d{1,2}月\\d{1,2}日)").toString(); - } - - // 提取发布来源 - String publishSource = page.getHtml().xpath("//text()[contains(., '发布来源')]").regex("发布来源:(.+?)\\s").toString(); - if (publishSource == null || publishSource.trim().isEmpty()) { - publishSource = page.getHtml().xpath("//text()[contains(., '发布来源')]").regex("发布来源:(.+?)$").toString(); - } - - // 提取正文内容 - String content = page.getHtml().xpath("//body//text()").all().toString(); - - // 提取表格数据(采购意向表格) - String tableData = extractTableData(page); - - // 记录调试信息 - log.debug("页面URL: {}", page.getUrl()); - log.debug("提取的标题: {}", title); - log.debug("发布日期: {}", publishDate); - log.debug("发布来源: {}", publishSource); - - // 检查是否有有效内容(降低阈值并检查关键信息) - boolean hasValidContent = (content != null && content.length() > 50) || - (title != null && title.contains("政府采购")) || - (publishSource != null && !publishSource.trim().isEmpty()) || - (tableData != null && !tableData.trim().isEmpty()); - - if (hasValidContent) { - // 从HTML中提取更清晰的标题 - String cleanTitle = title; - if (title != null && title.contains("天津市中医药研究院附属医院政府采购意向公告")) { - cleanTitle = "天津市中医药研究院附属医院政府采购意向公告"; - } else if (title != null && title.contains("政府采购")) { - // 尝试从title中提取更简洁的标题 - String[] lines = title.split("\n"); - for (String line : lines) { - if (line.contains("政府采购") && line.length() < 100) { - cleanTitle = line.trim(); - break; - } - } - } - - page.putField("title", cleanTitle != null ? cleanTitle.trim() : "未知标题"); - page.putField("publishDate", publishDate); - page.putField("publishSource", publishSource); - page.putField("content", content); - page.putField("tableData", tableData); - page.putField("url", page.getUrl().toString()); - page.putField("type", "procurement_detail"); - - log.info("成功提取详情页面数据: {}", cleanTitle != null ? cleanTitle : "未知标题"); - } else { - page.setSkip(true); - log.warn("跳过页面,内容不足: {}", page.getUrl()); - } - - } catch (Exception e) { - log.error("处理详情页面出错: {}", page.getUrl(), e); - page.setSkip(true); - } - } - - /** - * 处理列表页面 - */ - private void processListPage(Page page) { - try { - // 查找详情页面链接 - page.addTargetRequests(page.getHtml().links() - .regex(".*documentView\\.do\\?method=view&id=\\d+.*").all()); - - // 查找分页链接 - page.addTargetRequests(page.getHtml().links() - .regex(".*topicView\\.do.*").all()); - - log.info("从列表页面发现新链接: {}", page.getTargetRequests().size()); - - } catch (Exception e) { - log.error("处理列表页面出错: {}", page.getUrl(), e); - } - } - - /** - * 提取表格数据 - */ - private String extractTableData(Page page) { - try { - StringBuilder tableData = new StringBuilder(); - - // 查找包含采购信息的表格 - page.getHtml().xpath("//table").nodes().forEach(table -> { - table.xpath("//tr").nodes().forEach(row -> { - StringBuilder rowData = new StringBuilder(); - row.xpath("//td/text()").all().forEach(cell -> { - if (cell != null && !cell.trim().isEmpty()) { - rowData.append(cell.trim()).append("|"); - } - }); - if (rowData.length() > 0) { - tableData.append(rowData.toString()).append("\n"); - } - }); - }); - - return tableData.toString(); - } catch (Exception e) { - log.error("提取表格数据出错", e); - return ""; - } - } - - @Override - public Site getSite() { - return site; - } -} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/ProcurementDataPipeline.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/ProcurementDataPipeline.java index d66eb6aa..1885c0ac 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/ProcurementDataPipeline.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/component/ProcurementDataPipeline.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.component; import com.touhouqing.chatAiDemo.entity.ProcurementProject; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/CommonConfiguration.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/CommonConfiguration.java index f1486ca2..efe26e6d 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/CommonConfiguration.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/CommonConfiguration.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.config; import com.touhouqing.chatAiDemo.constants.SystemConstants; @@ -108,4 +92,21 @@ public ChatClient pdfChatClient(DashScopeChatModel model, ChatMemory chatMemory, .build(); } -} + /** + * 专门用于招标数据解析的ChatClient + * 使用适合数据解析的模型配置 + */ + @Bean + public ChatClient biddingDataChatClient(DashScopeChatModel model) { + return ChatClient + .builder(model) + .defaultOptions(DashScopeChatOptions.builder() + .withModel("qwen-max") // 使用最强大的模型 + .withTemperature(0.3) // 适中的温度,保持创造性和准确性的平衡 + .withIncrementalOutput(false) // 禁用增量输出 + .build()) + .defaultSystem("你是一个专业的政府采购信息提取专家,擅长从各种格式的HTML网页中准确提取招标、采购相关的结构化信息。你必须仔细分析HTML内容并返回准确的JSON格式数据。") + .build(); + } + +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/MvcConfiguration.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/MvcConfiguration.java index d4a15606..d4d74953 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/MvcConfiguration.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/MvcConfiguration.java @@ -1,21 +1,6 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.config; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -23,11 +8,14 @@ @Configuration public class MvcConfiguration implements WebMvcConfigurer { + @Value("${cors.allowed-origins}") + private String allowedOrigins; + @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - // 使用 allowedOriginPatterns 替代 allowedOrigins - .allowedOriginPatterns("*") + // 通过环境变量控制允许的域名 + .allowedOrigins(allowedOrigins.split(",")) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); // 允许携带认证信息 diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/Neo4jTestConfiguration.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/Neo4jTestConfiguration.java new file mode 100644 index 00000000..c9f34f1c --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/config/Neo4jTestConfiguration.java @@ -0,0 +1,77 @@ +package com.touhouqing.chatAiDemo.config; + +import org.neo4j.driver.Driver; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * Neo4j配置类 + * 手动配置所有必要的Neo4j组件以解决Spring Boot 3.x兼容性问题 + */ +@Configuration +public class Neo4jTestConfiguration { + + /** + * 配置Neo4jClient + */ + @Bean + @ConditionalOnMissingBean + public Neo4jClient neo4jClient(Driver driver, DatabaseSelectionProvider databaseSelectionProvider) { + return Neo4jClient.with(driver) + .withDatabaseSelectionProvider(databaseSelectionProvider) + .build(); + } + + /** + * 配置DatabaseSelectionProvider + */ + @Bean + @ConditionalOnMissingBean + public DatabaseSelectionProvider databaseSelectionProvider() { + return DatabaseSelectionProvider.getDefaultSelectionProvider(); + } + + /** + * 配置Neo4jMappingContext + */ + @Bean + @ConditionalOnMissingBean + public Neo4jMappingContext neo4jMappingContext() { + return new Neo4jMappingContext(); + } + + /** + * 配置Neo4jTemplate + */ + @Bean(name = "neo4jTemplate") + @ConditionalOnMissingBean + public Neo4jTemplate neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext) { + return new Neo4jTemplate(neo4jClient, neo4jMappingContext); + } + + /** + * 配置Neo4jOperations + */ + @Bean(name = "neo4jOperations") + @ConditionalOnMissingBean(Neo4jOperations.class) + public Neo4jOperations neo4jOperations(Neo4jTemplate neo4jTemplate) { + return neo4jTemplate; + } + + /** + * 配置事务管理器 + */ + @Bean + @ConditionalOnMissingBean(PlatformTransactionManager.class) + public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseSelectionProvider) { + return new Neo4jTransactionManager(driver, databaseSelectionProvider); + } +} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/constants/SystemConstants.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/constants/SystemConstants.java index 0d692433..2e1021b8 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/constants/SystemConstants.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/constants/SystemConstants.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.constants; public class SystemConstants { diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/BiddingController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/BiddingController.java new file mode 100644 index 00000000..dd89db19 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/BiddingController.java @@ -0,0 +1,387 @@ +package com.touhouqing.chatAiDemo.controller; + +import com.touhouqing.chatAiDemo.entity.graph.BiddingProject; +import com.touhouqing.chatAiDemo.entity.vo.ApiResponse; +import com.touhouqing.chatAiDemo.repository.graph.BiddingProjectRepository; +import com.touhouqing.chatAiDemo.service.BiddingCrawlerService; +import com.touhouqing.chatAiDemo.service.BiddingChatService; +import com.touhouqing.chatAiDemo.service.BiddingQueryService; +import com.touhouqing.chatAiDemo.service.DirectHttpCrawlerService; +import com.touhouqing.chatAiDemo.service.BiddingAIService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * 招标控制器 + * 提供招标爬虫和数据查询的API接口 + */ +@Slf4j +@RestController +@RequestMapping("/bidding") +@RequiredArgsConstructor +public class BiddingController { + + private final BiddingCrawlerService crawlerService; + private final BiddingProjectRepository projectRepository; + private final BiddingChatService chatService; + private final BiddingQueryService queryService; + private final DirectHttpCrawlerService directHttpCrawlerService; + private final BiddingAIService aiService; + + /** + * 启动招标爬虫任务 + */ + @PostMapping("/crawl/start") + public ApiResponse startCrawling(@RequestParam(required = false) String url) { + try { + CompletableFuture future; + if (url != null && !url.trim().isEmpty()) { + future = crawlerService.startCrawling(url); + } else { + future = crawlerService.crawlTianjinBidding(); + } + + // 异步执行,立即返回 + future.thenAccept(result -> log.info("招标爬虫任务结果: {}", result)); + + return ApiResponse.success("招标爬虫任务已启动,正在后台执行"); + } catch (Exception e) { + log.error("启动招标爬虫失败", e); + return ApiResponse.error("启动招标爬虫失败: " + e.getMessage()); + } + } + + /** + * 爬取单个招标页面 + */ + @PostMapping("/crawl/single") + public ApiResponse crawlSinglePage(@RequestParam String url) { + try { + CompletableFuture future = crawlerService.crawlSinglePage(url); + future.thenAccept(result -> log.info("单页面爬取结果: {}", result)); + + return ApiResponse.success("单页面爬取任务已启动"); + } catch (Exception e) { + log.error("单页面爬取失败", e); + return ApiResponse.error("单页面爬取失败: " + e.getMessage()); + } + } + + /** + * 直接HTTP爬取单个页面(避免WebMagic的限制) + */ + @PostMapping("/crawl/direct") + public ApiResponse crawlDirect(@RequestParam String url) { + try { + log.info("开始直接HTTP爬取: {}", url); + String result = directHttpCrawlerService.crawlPageDirectly(url); + return ApiResponse.success(result); + } catch (Exception e) { + log.error("直接HTTP爬取失败", e); + return ApiResponse.error("直接HTTP爬取失败: " + e.getMessage()); + } + } + + /** + * 测试AI解析功能 + */ + @PostMapping("/test/ai-parse") + public ApiResponse testAiParse(@RequestParam String url) { + try { + log.info("开始测试AI解析: {}", url); + + // 先获取HTML内容 + String htmlContent = directHttpCrawlerService.getHtmlContent(url); + if (htmlContent == null) { + return ApiResponse.error("无法获取页面内容"); + } + + // 测试AI解析 + String aiResult = aiService.parseHtmlToBiddingData(htmlContent, url); + + return ApiResponse.success("AI解析结果: " + aiResult); + } catch (Exception e) { + log.error("AI解析测试失败", e); + return ApiResponse.error("AI解析测试失败: " + e.getMessage()); + } + } + + /** + * 查看原始HTML内容 + */ + @PostMapping("/test/html-content") + public ApiResponse viewHtmlContent(@RequestParam String url) { + try { + log.info("获取HTML内容: {}", url); + + String htmlContent = directHttpCrawlerService.getHtmlContent(url); + if (htmlContent == null) { + return ApiResponse.error("无法获取页面内容"); + } + + // 返回HTML内容的前2000个字符用于查看 + String preview = htmlContent.length() > 2000 ? + htmlContent.substring(0, 2000) + "..." : htmlContent; + + return ApiResponse.success("HTML内容预览 (长度: " + htmlContent.length() + "): " + preview); + } catch (Exception e) { + log.error("获取HTML内容失败", e); + return ApiResponse.error("获取HTML内容失败: " + e.getMessage()); + } + } + + /** + * 调试分段解析过程 + */ + @PostMapping("/test/debug-parse") + public ApiResponse debugParse(@RequestParam String url) { + try { + log.info("开始调试分段解析: {}", url); + + String htmlContent = directHttpCrawlerService.getHtmlContent(url); + if (htmlContent == null) { + return ApiResponse.error("无法获取页面内容"); + } + + // 调用AI服务的调试方法 + String debugResult = aiService.debugParseHtml(htmlContent, url); + + return ApiResponse.success(debugResult); + } catch (Exception e) { + log.error("调试分段解析失败", e); + return ApiResponse.error("调试分段解析失败: " + e.getMessage()); + } + } + + /** + * 测试HTML片段提取 + */ + @PostMapping("/test/extract-fragments") + public ApiResponse testExtractFragments(@RequestParam String url) { + try { + log.info("测试HTML片段提取: {}", url); + + String htmlContent = directHttpCrawlerService.getHtmlContent(url); + if (htmlContent == null) { + return ApiResponse.error("无法获取页面内容"); + } + + StringBuilder result = new StringBuilder(); + result.append("=== HTML片段提取测试 ===\n\n"); + + // 查找meta标签 + result.append("1. Meta标签搜索:\n"); + String[] lines = htmlContent.split("\n"); + for (String line : lines) { + if (line.contains("") && line.matches(".*\\d+.*") && count < 5) { + result.append("找到数字行: ").append(line.trim()).append("\n"); + count++; + } + } + + return ApiResponse.success(result.toString()); + + } catch (Exception e) { + log.error("测试HTML片段提取失败", e); + return ApiResponse.error("测试HTML片段提取失败: " + e.getMessage()); + } + } + + /** + * 爬取天津政府采购网 + */ + @PostMapping("/crawl/tianjin") + public ApiResponse crawlTianjin() { + try { + CompletableFuture future = crawlerService.crawlTianjinBidding(); + future.thenAccept(result -> log.info("天津招标爬取结果: {}", result)); + + return ApiResponse.success("天津招标爬取任务已启动"); + } catch (Exception e) { + log.error("天津招标爬取失败", e); + return ApiResponse.error("天津招标爬取失败: " + e.getMessage()); + } + } + + /** + * 查询所有招标项目 + */ + @GetMapping("/projects") + public ApiResponse> getAllProjects() { + try { + List projects = projectRepository.findAll(); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("查询招标项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 根据项目名称搜索 + */ + @GetMapping("/projects/search") + public ApiResponse> searchProjects(@RequestParam String keyword) { + try { + List projects = projectRepository.findByProjectNameContaining(keyword); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("搜索招标项目失败", e); + return ApiResponse.error("搜索失败: " + e.getMessage()); + } + } + + /** + * 根据地区查询项目 + */ + @GetMapping("/projects/region/{region}") + public ApiResponse> getProjectsByRegion(@PathVariable String region) { + try { + List projects = projectRepository.findByRegion(region); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("根据地区查询项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 根据行业查询项目 + */ + @GetMapping("/projects/industry/{industry}") + public ApiResponse> getProjectsByIndustry(@PathVariable String industry) { + try { + List projects = projectRepository.findByIndustry(industry); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("根据行业查询项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 根据项目状态查询 + */ + @GetMapping("/projects/status/{status}") + public ApiResponse> getProjectsByStatus(@PathVariable String status) { + try { + List projects = projectRepository.findByProjectStatus(status); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("根据状态查询项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 获取最近的项目 + */ + @GetMapping("/projects/recent") + public ApiResponse> getRecentProjects(@RequestParam(defaultValue = "10") int limit) { + try { + List projects = projectRepository.findRecentProjects(limit); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("获取最近项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 智能问答:基于招标数据的问答 + */ + @PostMapping("/chat") + public ApiResponse chatWithBiddingData(@RequestParam String question) { + try { + String answer = chatService.chatWithBiddingData(question); + return ApiResponse.success(answer); + } catch (Exception e) { + log.error("智能问答失败", e); + return ApiResponse.error("问答服务暂时不可用: " + e.getMessage()); + } + } + + /** + * 语义搜索招标项目 + */ + @PostMapping("/search/semantic") + public ApiResponse> semanticSearch( + @RequestParam String query, + @RequestParam(defaultValue = "10") int topK, + @RequestParam(defaultValue = "0.3") double similarityThreshold) { + try { + List projects = queryService.semanticSearch(query, topK, similarityThreshold); + return ApiResponse.success(projects); + } catch (Exception e) { + log.error("语义搜索失败", e); + return ApiResponse.error("搜索失败: " + e.getMessage()); + } + } + + /** + * 查询企业相关项目 + */ + @PostMapping("/company/projects") + public ApiResponse queryCompanyProjects(@RequestParam String companyName) { + try { + String result = chatService.queryCompanyProjects(companyName); + return ApiResponse.success(result); + } catch (Exception e) { + log.error("查询企业项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } + + /** + * 项目推荐 + */ + @PostMapping("/recommend") + public ApiResponse recommendProjects(@RequestParam String requirements) { + try { + String recommendation = chatService.recommendProjects(requirements); + return ApiResponse.success(recommendation); + } catch (Exception e) { + log.error("项目推荐失败", e); + return ApiResponse.error("推荐服务暂时不可用: " + e.getMessage()); + } + } + + /** + * 相似项目推荐 + */ + @GetMapping("/projects/{projectId}/similar") + public ApiResponse> getSimilarProjects( + @PathVariable Long projectId, + @RequestParam(defaultValue = "5") int limit) { + try { + List similarProjects = queryService.recommendSimilarProjects(projectId, limit); + return ApiResponse.success(similarProjects); + } catch (Exception e) { + log.error("获取相似项目失败", e); + return ApiResponse.error("查询失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatController.java index 663e4d56..4f24df49 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatHistoryController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatHistoryController.java index 1fa5165d..12714096 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatHistoryController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ChatHistoryController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; import com.touhouqing.chatAiDemo.entity.vo.MessageVo; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/CustomerServiceController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/CustomerServiceController.java index 0ea616f5..7c234ebc 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/CustomerServiceController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/CustomerServiceController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/GameController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/GameController.java index 1bffd411..310db240 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/GameController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/GameController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; import lombok.RequiredArgsConstructor; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTemplateTestController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTemplateTestController.java new file mode 100644 index 00000000..e410dc35 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTemplateTestController.java @@ -0,0 +1,33 @@ +package com.touhouqing.chatAiDemo.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Neo4jTemplate测试控制器 + */ +@RestController +@RequestMapping("/test") +public class Neo4jTemplateTestController { + + @Autowired(required = false) + private Neo4jTemplate neo4jTemplate; + + @GetMapping("/neo4j-template") + public String testNeo4jTemplate() { + if (neo4jTemplate == null) { + return "Neo4jTemplate bean 未找到"; + } + + try { + // 执行一个简单的查询来测试连接 + Long count = neo4jTemplate.count("MATCH (n) RETURN count(n)"); + return "Neo4jTemplate 工作正常! 节点数量: " + count; + } catch (Exception e) { + return "Neo4jTemplate 连接失败: " + e.getMessage(); + } + } +} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTestController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTestController.java new file mode 100644 index 00000000..c9b2e2d2 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/Neo4jTestController.java @@ -0,0 +1,42 @@ +package com.touhouqing.chatAiDemo.controller; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.neo4j.driver.Result; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Neo4j连接测试控制器 + */ +@RestController +@RequestMapping("/test") +public class Neo4jTestController { + + @Autowired + private Driver driver; + + @Value("${spring.neo4j.uri}") + private String neo4jUri; + + @Value("${spring.neo4j.authentication.username}") + private String neo4jUsername; + + @GetMapping("/neo4j") + public String testNeo4jConnection() { + try (Session session = driver.session()) { + Result result = session.run("RETURN 'Neo4j连接成功!' as message"); + if (result.hasNext()) { + return "Neo4j连接成功! URI: " + neo4jUri + ", Username: " + neo4jUsername + + ", Message: " + result.next().get("message").asString(); + } + return "Neo4j连接成功,但没有返回数据"; + } catch (Exception e) { + return "Neo4j连接失败: " + e.getMessage() + + ", URI: " + neo4jUri + ", Username: " + neo4jUsername; + } + } +} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/PdfController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/PdfController.java index 1faeaeb6..ffe2a607 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/PdfController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/PdfController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; import com.touhouqing.chatAiDemo.entity.vo.Result; @@ -143,4 +127,4 @@ private void writeToVectorStore(Resource resource) { // 4.写入向量库 vectorStore.add(documents); } -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementController.java index 56681e5e..18912877 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; import com.touhouqing.chatAiDemo.entity.ProcurementProject; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementTestController.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementTestController.java index bb9971e7..ffab58bd 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementTestController.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/controller/ProcurementTestController.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.controller; import com.touhouqing.chatAiDemo.entity.vo.ApiResponse; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/BiddingProjectDto.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/BiddingProjectDto.java new file mode 100644 index 00000000..345b1183 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/BiddingProjectDto.java @@ -0,0 +1,110 @@ +package com.touhouqing.chatAiDemo.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Builder; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 招标项目数据传输对象 + * 用于AI解析后的结构化数据 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class BiddingProjectDto { + + /** + * 项目名称 + */ + private String projectName; + + /** + * 项目编号 + */ + private String projectCode; + + /** + * 项目描述 + */ + private String projectDescription; + + /** + * 项目需求详情 + */ + private String requirements; + + /** + * 预算金额 + */ + private Double budget; + + /** + * 预算单位 + */ + private String budgetUnit; + + /** + * 招标类型 + */ + private String biddingType; + + /** + * 项目状态 + */ + private String projectStatus; + + /** + * 发布日期 + */ + private LocalDateTime publishDate; + + /** + * 投标截止日期 + */ + private LocalDateTime biddingDeadline; + + /** + * 来源URL + */ + private String sourceUrl; + + /** + * 来源网站 + */ + private String sourceWebsite; + + /** + * 地区 + */ + private String region; + + /** + * 行业分类 + */ + private String industry; + + /** + * 发布机构信息 + */ + private OrganizationDto publishingOrganization; + + /** + * 中标企业信息 + */ + private List awardedCompanies; + + /** + * 参与投标企业信息 + */ + private List participatingCompanies; + + /** + * 原始HTML内容 + */ + private String rawHtmlContent; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/CompanyDto.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/CompanyDto.java new file mode 100644 index 00000000..6fd38439 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/CompanyDto.java @@ -0,0 +1,71 @@ +package com.touhouqing.chatAiDemo.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Builder; + +/** + * 企业数据传输对象 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class CompanyDto { + + /** + * 企业名称 + */ + private String companyName; + + /** + * 统一社会信用代码 + */ + private String unifiedSocialCreditCode; + + /** + * 法定代表人 + */ + private String legalRepresentative; + + /** + * 注册资本 + */ + private Double registeredCapital; + + /** + * 企业类型 + */ + private String companyType; + + /** + * 经营范围 + */ + private String businessScope; + + /** + * 注册地址 + */ + private String registeredAddress; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 中标金额(用于中标企业) + */ + private Double awardAmount; + + /** + * 中标排名(用于中标企业) + */ + private String awardRank; + + /** + * 投标金额(用于参与投标企业) + */ + private Double bidAmount; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/OrganizationDto.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/OrganizationDto.java new file mode 100644 index 00000000..5b089ba4 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/dto/OrganizationDto.java @@ -0,0 +1,66 @@ +package com.touhouqing.chatAiDemo.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Builder; + +/** + * 组织机构数据传输对象 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class OrganizationDto { + + /** + * 机构名称 + */ + private String organizationName; + + /** + * 机构代码 + */ + private String organizationCode; + + /** + * 机构类型 + */ + private String organizationType; + + /** + * 行政级别 + */ + private String administrativeLevel; + + /** + * 地址 + */ + private String address; + + /** + * 联系人 + */ + private String contactPerson; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 邮箱 + */ + private String email; + + /** + * 网站 + */ + private String website; + + /** + * 联系信息(综合) + */ + private String contactInfo; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/IndustryCategory.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/IndustryCategory.java index a6b78be0..9a67a7e3 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/IndustryCategory.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/IndustryCategory.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import org.springframework.data.neo4j.core.schema.GeneratedValue; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Movie.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Movie.java index 0ef82c98..2453af33 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Movie.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Movie.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import lombok.AllArgsConstructor; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Person.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Person.java index 570c72dc..bb010fd2 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Person.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Person.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import lombok.AllArgsConstructor; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/PersonRelationship.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/PersonRelationship.java index e14ec75d..1853685e 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/PersonRelationship.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/PersonRelationship.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import lombok.Data; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementOrganization.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementOrganization.java index 77488e09..9ba1326a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementOrganization.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementOrganization.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import org.springframework.data.neo4j.core.schema.GeneratedValue; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementProject.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementProject.java index 1eb2d832..84c2d885 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementProject.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProcurementProject.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import org.springframework.data.neo4j.core.schema.GeneratedValue; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProjectCategory.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProjectCategory.java index bf77ee16..e100b584 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProjectCategory.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/ProjectCategory.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import org.springframework.data.neo4j.core.schema.GeneratedValue; @@ -29,9 +13,9 @@ import java.util.List; /** - * 项目类别实体 + * 采购项目类别实体 */ -@Node("ProjectCategory") +@Node("ProcurementProjectCategory") @Data @NoArgsConstructor @AllArgsConstructor diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Roles.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Roles.java index 9e2563b7..21978227 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Roles.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Roles.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import lombok.Data; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Supplier.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Supplier.java index be9b9ad3..8c2031ce 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Supplier.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/Supplier.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity; import org.springframework.data.neo4j.core.schema.GeneratedValue; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/BiddingProject.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/BiddingProject.java new file mode 100644 index 00000000..8e493da8 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/BiddingProject.java @@ -0,0 +1,88 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 招标项目实体 - 图数据库核心节点 + */ +@Node("BiddingProject") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BiddingProject { + + @Id + @GeneratedValue + private Long id; + + @Property("projectName") + private String projectName; + + @Property("projectCode") + private String projectCode; + + @Property("budget") + private Double budget; + + @Property("budgetUnit") + private String budgetUnit; + + @Property("biddingType") + private String biddingType; // 公开招标、邀请招标、竞争性谈判等 + + @Property("projectStatus") + private String projectStatus; // 招标中、已开标、已中标等 + + @Property("publishDate") + private LocalDateTime publishDate; + + @Property("biddingDeadline") + private LocalDateTime biddingDeadline; + + @Property("sourceUrl") + private String sourceUrl; + + @Property("sourceWebsite") + private String sourceWebsite; // 来源网站 + + @Property("region") + private String region; // 地区 + + @Property("industry") + private String industry; // 行业分类 + + @Property("vectorId") + private String vectorId; // 关联向量数据库的ID + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与采购单位的关系 + @Relationship(type = "PUBLISHED_BY", direction = Relationship.Direction.OUTGOING) + private Organization publishingOrganization; + + // 与中标企业的关系 + @Relationship(type = "AWARDED_TO", direction = Relationship.Direction.OUTGOING) + private List awardedCompanies; + + // 与参与投标企业的关系 + @Relationship(type = "PARTICIPATED_BY", direction = Relationship.Direction.OUTGOING) + private List participatingCompanies; + + // 与项目类别的关系 + @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.OUTGOING) + private ProjectCategory category; + + // 与地区的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.OUTGOING) + private Region projectRegion; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Company.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Company.java new file mode 100644 index 00000000..ff1f7bae --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Company.java @@ -0,0 +1,91 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 企业实体 - 图数据库核心节点 + */ +@Node("Company") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Company { + + @Id + @GeneratedValue + private Long id; + + @Property("companyName") + private String companyName; + + @Property("unifiedSocialCreditCode") + private String unifiedSocialCreditCode; // 统一社会信用代码 + + @Property("legalRepresentative") + private String legalRepresentative; // 法定代表人 + + @Property("registeredCapital") + private Double registeredCapital; // 注册资本 + + @Property("establishmentDate") + private LocalDateTime establishmentDate; // 成立日期 + + @Property("companyType") + private String companyType; // 企业类型 + + @Property("businessScope") + private String businessScope; // 经营范围 + + @Property("registeredAddress") + private String registeredAddress; // 注册地址 + + @Property("contactPhone") + private String contactPhone; + + @Property("email") + private String email; + + @Property("website") + private String website; + + @Property("companyScale") + private String companyScale; // 企业规模:大型、中型、小型、微型 + + @Property("creditRating") + private String creditRating; // 信用等级 + + @Property("vectorId") + private String vectorId; // 关联向量数据库的ID + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与中标项目的关系 + @Relationship(type = "AWARDED_TO", direction = Relationship.Direction.INCOMING) + private List awardedProjects; + + // 与参与项目的关系 + @Relationship(type = "PARTICIPATED_BY", direction = Relationship.Direction.INCOMING) + private List participatedProjects; + + // 与行业的关系 + @Relationship(type = "OPERATES_IN", direction = Relationship.Direction.OUTGOING) + private List industries; + + // 与地区的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.OUTGOING) + private Region location; + + // 企业间合作关系 + @Relationship(type = "COOPERATES_WITH", direction = Relationship.Direction.OUTGOING) + private List partners; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Industry.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Industry.java new file mode 100644 index 00000000..351ef609 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Industry.java @@ -0,0 +1,57 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 行业实体 + */ +@Node("Industry") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Industry { + + @Id + @GeneratedValue + private Long id; + + @Property("industryName") + private String industryName; + + @Property("industryCode") + private String industryCode; // 行业分类代码 + + @Property("industryLevel") + private Integer industryLevel; // 行业分类层级 + + @Property("description") + private String description; + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与企业的关系 + @Relationship(type = "OPERATES_IN", direction = Relationship.Direction.INCOMING) + private List companies; + + // 与组织的关系 + @Relationship(type = "BELONGS_TO_INDUSTRY", direction = Relationship.Direction.INCOMING) + private List organizations; + + // 与上级行业的关系 + @Relationship(type = "SUBCATEGORY_OF", direction = Relationship.Direction.OUTGOING) + private Industry parentIndustry; + + // 与下级行业的关系 + @Relationship(type = "SUBCATEGORY_OF", direction = Relationship.Direction.INCOMING) + private List subIndustries; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Organization.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Organization.java new file mode 100644 index 00000000..c6653920 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Organization.java @@ -0,0 +1,71 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 采购组织/机构实体 + */ +@Node("Organization") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Organization { + + @Id + @GeneratedValue + private Long id; + + @Property("organizationName") + private String organizationName; + + @Property("organizationCode") + private String organizationCode; // 组织机构代码 + + @Property("organizationType") + private String organizationType; // 政府机关、事业单位、国有企业等 + + @Property("administrativeLevel") + private String administrativeLevel; // 国家级、省级、市级、县级等 + + @Property("address") + private String address; + + @Property("contactPerson") + private String contactPerson; + + @Property("contactPhone") + private String contactPhone; + + @Property("email") + private String email; + + @Property("website") + private String website; + + @Property("vectorId") + private String vectorId; // 关联向量数据库的ID + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与发布项目的关系 + @Relationship(type = "PUBLISHED_BY", direction = Relationship.Direction.INCOMING) + private List publishedProjects; + + // 与地区的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.OUTGOING) + private Region location; + + // 与行业的关系 + @Relationship(type = "BELONGS_TO_INDUSTRY", direction = Relationship.Direction.OUTGOING) + private Industry industry; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/ProjectCategory.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/ProjectCategory.java new file mode 100644 index 00000000..9d025e31 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/ProjectCategory.java @@ -0,0 +1,53 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 招标项目类别实体 + */ +@Node("BiddingProjectCategory") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProjectCategory { + + @Id + @GeneratedValue + private Long id; + + @Property("categoryName") + private String categoryName; + + @Property("categoryCode") + private String categoryCode; + + @Property("description") + private String description; + + @Property("level") + private Integer level; // 分类层级 + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与招标项目的关系 + @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.INCOMING) + private List projects; + + // 与父类别的关系 + @Relationship(type = "SUBCATEGORY_OF", direction = Relationship.Direction.OUTGOING) + private ProjectCategory parentCategory; + + // 与子类别的关系 + @Relationship(type = "SUBCATEGORY_OF", direction = Relationship.Direction.INCOMING) + private List subCategories; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Region.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Region.java new file mode 100644 index 00000000..d168a843 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/graph/Region.java @@ -0,0 +1,67 @@ +package com.touhouqing.chatAiDemo.entity.graph; + +import org.springframework.data.neo4j.core.schema.*; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 地区实体 + */ +@Node("Region") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Region { + + @Id + @GeneratedValue + private Long id; + + @Property("regionName") + private String regionName; + + @Property("regionCode") + private String regionCode; // 行政区划代码 + + @Property("regionLevel") + private String regionLevel; // 省、市、县、区等 + + @Property("parentRegionCode") + private String parentRegionCode; // 上级区域代码 + + @Property("longitude") + private Double longitude; // 经度 + + @Property("latitude") + private Double latitude; // 纬度 + + @Property("createdAt") + private LocalDateTime createdAt; + + @Property("updatedAt") + private LocalDateTime updatedAt; + + // 与项目的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.INCOMING) + private List projects; + + // 与企业的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.INCOMING) + private List companies; + + // 与组织的关系 + @Relationship(type = "LOCATED_IN", direction = Relationship.Direction.INCOMING) + private List organizations; + + // 与上级地区的关系 + @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.OUTGOING) + private Region parentRegion; + + // 与下级地区的关系 + @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.INCOMING) + private List subRegions; +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/Course.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/Course.java index 80504283..ac7f92b5 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/Course.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/Course.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.po; import com.baomidou.mybatisplus.annotation.TableName; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/CourseReservation.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/CourseReservation.java index 7300dab1..050dbd46 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/CourseReservation.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/CourseReservation.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.po; import com.baomidou.mybatisplus.annotation.TableName; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/School.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/School.java index aa90903f..1439f64e 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/School.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/po/School.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.po; import com.baomidou.mybatisplus.annotation.TableName; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/query/CourseQuery.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/query/CourseQuery.java index 21b162b0..cf67a49e 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/query/CourseQuery.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/query/CourseQuery.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.query; @@ -38,4 +22,4 @@ public static class Sort { @ToolParam(required = false, description = "是否是升序: true/false") private Boolean asc; } -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vector/VectorDocument.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vector/VectorDocument.java new file mode 100644 index 00000000..2031320b --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vector/VectorDocument.java @@ -0,0 +1,93 @@ +package com.touhouqing.chatAiDemo.entity.vector; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Builder; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 向量数据库文档实体 + * 用于封装存储到向量数据库的文档信息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class VectorDocument { + + /** + * 文档唯一标识 + */ + private String id; + + /** + * 文档内容 + */ + private String content; + + /** + * 文档类型 + */ + private DocumentType documentType; + + /** + * 关联的图数据库实体ID + */ + private Long graphEntityId; + + /** + * 关联的图数据库实体类型 + */ + private String graphEntityType; + + /** + * 来源URL + */ + private String sourceUrl; + + /** + * 来源网站 + */ + private String sourceWebsite; + + /** + * 创建时间 + */ + private LocalDateTime createdAt; + + /** + * 更新时间 + */ + private LocalDateTime updatedAt; + + /** + * 额外的元数据 + */ + private Map metadata; + + /** + * 文档类型枚举 + */ + public enum DocumentType { + PROJECT_DESCRIPTION("项目描述"), + PROJECT_REQUIREMENTS("项目需求"), + COMPANY_PROFILE("企业简介"), + ORGANIZATION_INFO("机构信息"), + BIDDING_ANNOUNCEMENT("招标公告"), + AWARD_RESULT("中标结果"), + CONTRACT_INFO("合同信息"); + + private final String description; + + DocumentType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/ApiResponse.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/ApiResponse.java index fca4ddd6..63be8456 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/ApiResponse.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/ApiResponse.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.vo; import lombok.Data; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/MessageVo.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/MessageVo.java index 75a9997b..c2aeb137 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/MessageVo.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/MessageVo.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.vo; import lombok.Data; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/Result.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/Result.java index c1768cda..cf17302b 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/Result.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/entity/vo/Result.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.entity.vo; import lombok.Data; @@ -37,4 +21,4 @@ public static Result ok() { public static Result fail(String msg) { return new Result(0, msg); } -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseMapper.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseMapper.java index 41fc0a5b..317cf982 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseMapper.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseMapper.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.mapper; import com.touhouqing.chatAiDemo.entity.po.Course; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseReservationMapper.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseReservationMapper.java index 30212180..fdc2cae1 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseReservationMapper.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/CourseReservationMapper.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.mapper; import com.touhouqing.chatAiDemo.entity.po.CourseReservation; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/SchoolMapper.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/SchoolMapper.java index 2643f3a0..82e7ca3f 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/SchoolMapper.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/mapper/SchoolMapper.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.mapper; import com.touhouqing.chatAiDemo.entity.po.School; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ChatHistoryRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ChatHistoryRepository.java index 967eeebb..0b31dc5d 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ChatHistoryRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ChatHistoryRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import java.util.List; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/FileRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/FileRepository.java index 0b46c27c..44043d0a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/FileRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/FileRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import org.springframework.core.io.Resource; @@ -33,4 +17,4 @@ public interface FileRepository { * @return 找到的文件 */ Resource getFile(String chatId); -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/InMemoryChatHistoryRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/InMemoryChatHistoryRepository.java index 19a65e7f..fa819e8e 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/InMemoryChatHistoryRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/InMemoryChatHistoryRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import org.springframework.stereotype.Component; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/LocalPdfFileRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/LocalPdfFileRepository.java index f46c50a5..a84c8518 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/LocalPdfFileRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/LocalPdfFileRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import jakarta.annotation.PostConstruct; @@ -96,4 +80,4 @@ private void persistent() { throw new RuntimeException(e); } } -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/MovieRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/MovieRepository.java index af8eaa02..1d4717c7 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/MovieRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/MovieRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import com.touhouqing.chatAiDemo.entity.Movie; @@ -44,4 +28,4 @@ public interface MovieRepository extends Neo4jRepository { "OPTIONAL MATCH (m)<-[:DIRECTED]-(director:Person) " + "RETURN m, collect(DISTINCT actor) as actors, collect(DISTINCT director) as directors") List findMovieWithCast(String title); -} +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRelationshipRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRelationshipRepository.java index 85ac4832..399ae54a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRelationshipRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRelationshipRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import com.touhouqing.chatAiDemo.entity.PersonRelationship; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRepository.java index 78343eb7..c2045d1b 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/PersonRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import com.touhouqing.chatAiDemo.entity.Person; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementOrganizationRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementOrganizationRepository.java index 80029b3d..e5231927 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementOrganizationRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementOrganizationRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import com.touhouqing.chatAiDemo.entity.ProcurementOrganization; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementProjectRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementProjectRepository.java index afc66a19..982a4eff 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementProjectRepository.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/ProcurementProjectRepository.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.repository; import com.touhouqing.chatAiDemo.entity.ProcurementProject; @@ -44,7 +28,7 @@ public interface ProcurementProjectRepository extends Neo4jRepository findByOrganizationName(String organizationName); // 根据项目类别查找项目 - @Query("MATCH (p:ProcurementProject)-[:BELONGS_TO]->(c:ProjectCategory {name: $categoryName}) RETURN p") + @Query("MATCH (p:ProcurementProject)-[:BELONGS_TO]->(c:ProcurementProjectCategory {name: $categoryName}) RETURN p") List findByCategoryName(String categoryName); // 查找最近的项目 @@ -62,7 +46,7 @@ public interface ProcurementProjectRepository extends Neo4jRepository(o:ProcurementOrganization) " + - "OPTIONAL MATCH (p)-[:BELONGS_TO]->(c:ProjectCategory) " + + "OPTIONAL MATCH (p)-[:BELONGS_TO]->(c:ProcurementProjectCategory) " + "OPTIONAL MATCH (p)-[:AWARDED_TO]->(s:Supplier) " + "RETURN p, o, c, collect(s)") Optional findProjectWithAllRelations(Long projectId); diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/BiddingProjectRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/BiddingProjectRepository.java new file mode 100644 index 00000000..a8868412 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/BiddingProjectRepository.java @@ -0,0 +1,80 @@ +package com.touhouqing.chatAiDemo.repository.graph; + +import com.touhouqing.chatAiDemo.entity.graph.BiddingProject; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 招标项目图数据库Repository + */ +@Repository +public interface BiddingProjectRepository extends Neo4jRepository { + + /** + * 根据来源URL查找项目 + */ + @Query("MATCH (p:BiddingProject {sourceUrl: $sourceUrl}) RETURN p") + Optional findBySourceUrl(String sourceUrl); + + /** + * 检查项目是否存在 + */ + @Query("MATCH (p:BiddingProject {sourceUrl: $sourceUrl}) RETURN count(p) > 0") + boolean existsBySourceUrl(String sourceUrl); + + /** + * 根据项目名称搜索 + */ + @Query("MATCH (p:BiddingProject) WHERE p.projectName CONTAINS $keyword RETURN p") + List findByProjectNameContaining(String keyword); + + /** + * 根据地区查找项目 + */ + @Query("MATCH (p:BiddingProject {region: $region}) RETURN p") + List findByRegion(String region); + + /** + * 根据行业查找项目 + */ + @Query("MATCH (p:BiddingProject {industry: $industry}) RETURN p") + List findByIndustry(String industry); + + /** + * 根据项目状态查找 + */ + @Query("MATCH (p:BiddingProject {projectStatus: $status}) RETURN p") + List findByProjectStatus(String status); + + /** + * 查找项目及其关联的发布机构 + */ + @Query("MATCH (p:BiddingProject)-[:PUBLISHED_BY]->(o:Organization) WHERE p.id = $projectId RETURN p, o") + Optional findProjectWithOrganization(Long projectId); + + /** + * 查找项目及其所有关联实体 + */ + @Query("MATCH (p:BiddingProject) WHERE p.id = $projectId " + + "OPTIONAL MATCH (p)-[:PUBLISHED_BY]->(o:Organization) " + + "OPTIONAL MATCH (p)-[:AWARDED_TO]->(c:Company) " + + "OPTIONAL MATCH (p)-[:PARTICIPATED_BY]->(pc:Company) " + + "RETURN p, o, collect(c), collect(pc)") + Optional findProjectWithAllRelations(Long projectId); + + /** + * 根据预算范围查找项目 + */ + @Query("MATCH (p:BiddingProject) WHERE p.budget >= $minBudget AND p.budget <= $maxBudget RETURN p") + List findByBudgetRange(Double minBudget, Double maxBudget); + + /** + * 查找最近的项目 + */ + @Query("MATCH (p:BiddingProject) RETURN p ORDER BY p.publishDate DESC LIMIT $limit") + List findRecentProjects(int limit); +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/CompanyRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/CompanyRepository.java new file mode 100644 index 00000000..c4a0ad93 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/CompanyRepository.java @@ -0,0 +1,81 @@ +package com.touhouqing.chatAiDemo.repository.graph; + +import com.touhouqing.chatAiDemo.entity.graph.Company; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 企业图数据库Repository + */ +@Repository +public interface CompanyRepository extends Neo4jRepository { + + /** + * 根据企业名称查找 + */ + @Query("MATCH (c:Company {companyName: $companyName}) RETURN c") + Optional findByCompanyName(String companyName); + + /** + * 根据统一社会信用代码查找 + */ + @Query("MATCH (c:Company {unifiedSocialCreditCode: $code}) RETURN c") + Optional findByUnifiedSocialCreditCode(String code); + + /** + * 查找企业的所有中标项目 + */ + @Query("MATCH (c:Company)<-[:AWARDED_TO]-(p:BiddingProject) WHERE c.id = $companyId RETURN p") + List findAwardedProjects(Long companyId); + + /** + * 查找企业的所有参与项目 + */ + @Query("MATCH (c:Company)<-[:PARTICIPATED_BY]-(p:BiddingProject) WHERE c.id = $companyId RETURN p") + List findParticipatedProjects(Long companyId); + + /** + * 查找企业的合作伙伴 + */ + @Query("MATCH (c:Company)-[:COOPERATES_WITH]->(partner:Company) WHERE c.id = $companyId RETURN partner") + List findPartners(Long companyId); + + /** + * 根据地区查找企业 + */ + @Query("MATCH (c:Company)-[:LOCATED_IN]->(r:Region {regionName: $regionName}) RETURN c") + List findByRegion(String regionName); + + /** + * 根据行业查找企业 + */ + @Query("MATCH (c:Company)-[:OPERATES_IN]->(i:Industry {industryName: $industryName}) RETURN c") + List findByIndustry(String industryName); + + /** + * 查找企业及其所有关联信息 + */ + @Query("MATCH (c:Company) WHERE c.id = $companyId " + + "OPTIONAL MATCH (c)-[:LOCATED_IN]->(r:Region) " + + "OPTIONAL MATCH (c)-[:OPERATES_IN]->(i:Industry) " + + "OPTIONAL MATCH (c)<-[:AWARDED_TO]-(ap:BiddingProject) " + + "OPTIONAL MATCH (c)<-[:PARTICIPATED_BY]-(pp:BiddingProject) " + + "RETURN c, r, collect(i), collect(ap), collect(pp)") + Optional findCompanyWithAllRelations(Long companyId); + + /** + * 根据企业规模查找 + */ + @Query("MATCH (c:Company {companyScale: $scale}) RETURN c") + List findByCompanyScale(String scale); + + /** + * 根据信用等级查找 + */ + @Query("MATCH (c:Company {creditRating: $rating}) RETURN c") + List findByCreditRating(String rating); +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/OrganizationRepository.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/OrganizationRepository.java new file mode 100644 index 00000000..71f3d0a5 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/repository/graph/OrganizationRepository.java @@ -0,0 +1,62 @@ +package com.touhouqing.chatAiDemo.repository.graph; + +import com.touhouqing.chatAiDemo.entity.graph.Organization; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 组织机构图数据库Repository + */ +@Repository +public interface OrganizationRepository extends Neo4jRepository { + + /** + * 根据机构名称查找 + */ + @Query("MATCH (o:Organization {organizationName: $organizationName}) RETURN o") + Optional findByOrganizationName(String organizationName); + + /** + * 根据机构代码查找 + */ + @Query("MATCH (o:Organization {organizationCode: $organizationCode}) RETURN o") + Optional findByOrganizationCode(String organizationCode); + + /** + * 根据机构类型查找 + */ + @Query("MATCH (o:Organization {organizationType: $organizationType}) RETURN o") + List findByOrganizationType(String organizationType); + + /** + * 根据行政级别查找 + */ + @Query("MATCH (o:Organization {administrativeLevel: $administrativeLevel}) RETURN o") + List findByAdministrativeLevel(String administrativeLevel); + + /** + * 查找机构发布的所有项目 + */ + @Query("MATCH (o:Organization)<-[:PUBLISHED_BY]-(p:BiddingProject) WHERE o.id = $organizationId RETURN p") + List findPublishedProjects(Long organizationId); + + /** + * 根据地区查找机构 + */ + @Query("MATCH (o:Organization)-[:LOCATED_IN]->(r:Region {regionName: $regionName}) RETURN o") + List findByRegion(String regionName); + + /** + * 查找机构及其所有关联信息 + */ + @Query("MATCH (o:Organization) WHERE o.id = $organizationId " + + "OPTIONAL MATCH (o)-[:LOCATED_IN]->(r:Region) " + + "OPTIONAL MATCH (o)-[:BELONGS_TO_INDUSTRY]->(i:Industry) " + + "OPTIONAL MATCH (o)<-[:PUBLISHED_BY]-(p:BiddingProject) " + + "RETURN o, r, i, collect(p)") + Optional findOrganizationWithAllRelations(Long organizationId); +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingAIService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingAIService.java new file mode 100644 index 00000000..f2b03df1 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingAIService.java @@ -0,0 +1,468 @@ +package com.touhouqing.chatAiDemo.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +/** + * 招标AI解析服务 + * 使用大模型将原始HTML解析为结构化数据 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingAIService { + + @Qualifier("biddingDataChatClient") + private final ChatClient biddingDataChatClient; + + /** + * 将HTML内容解析为结构化的招标项目数据 + * 使用优化的单次解析策略 + */ + public String parseHtmlToBiddingData(String htmlContent, String sourceUrl) { + try { + log.info("开始解析HTML,URL: {}", sourceUrl); + + // 使用优化的单次解析提示词 + String prompt = buildOptimizedParsingPrompt(htmlContent, sourceUrl); + + String result = biddingDataChatClient.prompt() + .user(prompt) + .call() + .content(); + + log.info("AI解析HTML完成,URL: {}", sourceUrl); + return result; + + } catch (Exception e) { + log.error("AI解析HTML失败", e); + return null; + } + } + + /** + * 构建优化的HTML解析提示词 + */ + private String buildOptimizedParsingPrompt(String htmlContent, String sourceUrl) { + return String.format(""" + 你是专业的政府采购信息提取专家。请从以下HTML中提取信息并返回JSON格式。 + + HTML内容: + %s + + 请按以下步骤仔细分析: + + 1. 查找项目名称: + - 搜索 + - 提取content属性的值作为项目名称 + + 2. 查找发布日期: + - 搜索 + - 必须转换为完整的日期时间格式:2025-07-30T10:54:06(不能只有日期) + + 3. 计算预算总额: + - 在HTML中搜索包含"预算金额(万元)"的表格 + - 找到表格中所有的标签内的数字 + - 这些数字可能是:131、100、236.5、487.33、103.76、150、240、160、55、60、198、62.5 + - 将所有数字相加得到总预算 + + 4. 判断项目信息: + - 如果标题包含"意向",状态为"意向公告" + - 从内容推断地区为"天津",行业为"教育" + - 发布机构为"天津外国语大学" + + 请严格按照以下JSON格式返回(只返回JSON,不要其他文字): + + { + "projectName": "天津外国语大学政府采购意向公告", + "projectCode": null, + "projectDescription": "天津外国语大学2025年度政府采购意向公告,包含多个采购项目", + "requirements": "包含教学设备、实验设备、办公设备等多类采购需求", + "budget": 1983.09, + "budgetUnit": "万元", + "biddingType": "政府采购", + "projectStatus": "意向公告", + "publishDate": "2025-07-30T10:54:06", + "biddingDeadline": null, + "region": "天津", + "industry": "教育", + "publishingOrganization": { + "organizationName": "天津外国语大学", + "organizationType": "高等院校", + "contactInfo": null + }, + "awardedCompanies": [], + "participatingCompanies": [] + } + + 重要提醒: + - 必须从HTML中实际提取信息,不要使用示例数据 + - budget字段必须是从表格中提取的所有数字的总和 + - publishDate必须包含时间,格式为YYYY-MM-DDTHH:mm:ss + - 只返回JSON格式,不要任何解释文字 + """, htmlContent); + } + + /** + * 第1步:提取基本信息(meta标签、标题、日期等) + */ + private String extractBasicInfo(String htmlContent, String sourceUrl) { + try { + // 提取HTML的头部和主要标题部分 + String headSection = extractHeadSection(htmlContent); + String titleSection = extractTitleSection(htmlContent); + + String prompt = String.format(""" + 请从以下HTML片段中提取基本信息,返回JSON格式: + + HTML头部信息: + %s + + HTML标题部分: + %s + + 请提取以下信息: + 1. 项目名称:从提取 + 2. 发布日期:从提取并转换为YYYY-MM-DD格式 + 3. 发布机构:从页面内容推断 + 4. 项目状态:根据标题判断(包含"意向"为"意向公告") + + 只返回JSON格式: + { + "projectName": "项目名称", + "publishDate": "YYYY-MM-DD", + "publishingOrganization": "发布机构名称", + "projectStatus": "项目状态", + "region": "地区", + "industry": "行业" + } + """, headSection, titleSection); + + return biddingDataChatClient.prompt() + .user(prompt) + .call() + .content(); + + } catch (Exception e) { + log.error("提取基本信息失败", e); + return "{}"; + } + } + + /** + * 第2步:提取表格信息(预算数据) + */ + private String extractTableInfo(String htmlContent, String sourceUrl) { + try { + // 提取表格部分 + String tableSection = extractTableSection(htmlContent); + + String prompt = String.format(""" + 请从以下HTML表格中提取预算信息: + + 表格HTML: + %s + + 任务: + 1. 找到表格中的"预算金额(万元)"列 + 2. 提取所有预算数字(如:131、100、236.5、487.33等) + 3. 将所有数字相加得到总预算 + 4. 提取项目描述信息 + + 只返回JSON格式: + { + "budget": 总预算数字, + "budgetUnit": "万元", + "projectDescription": "项目描述概况", + "requirements": "采购需求概况" + } + + 重要:budget字段必须是数字,将表格中所有预算金额相加! + """, tableSection); + + return biddingDataChatClient.prompt() + .user(prompt) + .call() + .content(); + + } catch (Exception e) { + log.error("提取表格信息失败", e); + return "{}"; + } + } + + /** + * 第3步:合并信息生成最终JSON + */ + private String mergeInformation(String basicInfo, String tableInfo, String sourceUrl) { + try { + String prompt = String.format(""" + 请将以下两部分信息合并成完整的招标项目JSON: + + 基本信息: + %s + + 表格信息: + %s + + 请合并成完整的JSON格式: + { + "projectName": "从基本信息中获取", + "projectCode": null, + "projectDescription": "从表格信息中获取", + "requirements": "从表格信息中获取", + "budget": 从表格信息中获取的数字, + "budgetUnit": "万元", + "biddingType": "政府采购", + "projectStatus": "从基本信息中获取", + "publishDate": "从基本信息中获取", + "biddingDeadline": null, + "region": "从基本信息中获取", + "industry": "从基本信息中获取", + "publishingOrganization": { + "organizationName": "从基本信息中获取", + "organizationType": "高等院校", + "contactInfo": null + }, + "awardedCompanies": [], + "participatingCompanies": [] + } + + 只返回合并后的JSON,不要其他文字。 + """, basicInfo, tableInfo); + + return biddingDataChatClient.prompt() + .user(prompt) + .call() + .content(); + + } catch (Exception e) { + log.error("合并信息失败", e); + return null; + } + } + + /** + * 构建HTML解析提示词(备用方法) + */ + private String buildHtmlParsingPrompt(String htmlContent, String sourceUrl) { + return String.format(""" + 请从以下HTML中提取政府采购信息,返回JSON格式。 + + HTML内容: + %s + + 请按步骤分析: + + 第1步:找到项目名称 + - 查找 + - 或查找页面中的主标题 + + 第2步:找到发布日期 + - 查找 + - 转换为 2025-07-30 格式 + + 第3步:计算预算总额(重要!) + - 仔细查找HTML中的表格,特别是包含"预算金额"、"万元"等关键词的列 + - 在表格的标签中查找纯数字,如:131、100、236.5 + - 这些数字通常在表格的第4列或第5列 + - 将找到的所有数字相加:131+100+236.5+487.33+103.76+150+240+160+55+60+198+62.5 + - 如果找不到表格,预算设为null + + 第4步:判断项目状态 + - 如果标题包含"意向",状态为"意向公告" + - 如果标题包含"招标",状态为"招标中" + + 第5步:提取机构信息 + - 从页面内容中找到发布机构名称 + - 从URL或内容判断地区 + + 返回JSON(只返回JSON,不要其他文字): + { + "projectName": "从meta标签或标题提取的项目名称", + "projectCode": null, + "projectDescription": "项目描述", + "requirements": "采购需求概况", + "budget": 所有预算金额的数字总和, + "budgetUnit": "万元", + "biddingType": "政府采购", + "projectStatus": "意向公告", + "publishDate": "2025-07-30", + "biddingDeadline": null, + "region": "天津", + "industry": "教育", + "publishingOrganization": { + "organizationName": "天津外国语大学", + "organizationType": "高等院校", + "contactInfo": null + }, + "awardedCompanies": [], + "participatingCompanies": [] + } + """, htmlContent); + } + + /** + * 提取HTML头部信息(meta标签等) + */ + private String extractHeadSection(String htmlContent) { + try { + StringBuilder metaTags = new StringBuilder(); + String[] lines = htmlContent.split("\n"); + + for (String line : lines) { + // 提取所有重要的meta标签 + if (line.contains("") || + line.contains("

") || line.contains("

") || line.contains("

") || + line.contains("align=\"center\"") || + line.contains("政府采购") || line.contains("招标") || line.contains("意向")) { + titleSection.append(line).append("\n"); + } + } + + return titleSection.toString(); + + } catch (Exception e) { + log.error("提取标题部分失败", e); + return ""; + } + } + + /** + * 提取HTML表格部分 + */ + private String extractTableSection(String htmlContent) { + try { + StringBuilder tableSection = new StringBuilder(); + String[] lines = htmlContent.split("\n"); + + boolean inTableArea = false; + int tableDepth = 0; + + for (String line : lines) { + String trimmedLine = line.trim(); + + // 开始提取表格:找到包含"预算金额"的表头 + if (trimmedLine.contains("预算金额") || trimmedLine.contains("采购项目名称")) { + inTableArea = true; + tableSection.append(line).append("\n"); + continue; + } + + // 如果已经在表格区域 + if (inTableArea) { + // 计算表格嵌套深度 + if (trimmedLine.contains("")) { + tableDepth--; + tableSection.append(line).append("\n"); + if (tableDepth <= 0) { + break; // 表格结束 + } + continue; + } + + // 提取表格相关的行 + if (trimmedLine.contains("") || + trimmedLine.contains("") || + trimmedLine.matches(".*\\d+.*") || // 包含数字的行 + trimmedLine.contains("采购") || trimmedLine.contains("预算")) { + tableSection.append(line).append("\n"); + } + } + } + + String result = tableSection.toString(); + log.info("提取到的表格内容长度: {}", result.length()); + if (result.length() > 0) { + log.info("表格内容预览: {}", result.length() > 300 ? result.substring(0, 300) + "..." : result); + } + + return result; + + } catch (Exception e) { + log.error("提取表格部分失败", e); + return ""; + } + } + + /** + * 调试分段解析过程,返回每个步骤的详细结果 + */ + public String debugParseHtml(String htmlContent, String sourceUrl) { + StringBuilder debugResult = new StringBuilder(); + + try { + debugResult.append("=== 调试分段解析过程 ===\n\n"); + + // 第1步:提取HTML片段 + String headSection = extractHeadSection(htmlContent); + String titleSection = extractTitleSection(htmlContent); + String tableSection = extractTableSection(htmlContent); + + debugResult.append("1. HTML片段提取结果:\n"); + debugResult.append("头部信息长度: ").append(headSection.length()).append("\n"); + debugResult.append("头部内容预览: ").append(headSection.length() > 200 ? headSection.substring(0, 200) + "..." : headSection).append("\n\n"); + + debugResult.append("标题信息长度: ").append(titleSection.length()).append("\n"); + debugResult.append("标题内容预览: ").append(titleSection.length() > 200 ? titleSection.substring(0, 200) + "..." : titleSection).append("\n\n"); + + debugResult.append("表格信息长度: ").append(tableSection.length()).append("\n"); + debugResult.append("表格内容预览: ").append(tableSection.length() > 500 ? tableSection.substring(0, 500) + "..." : tableSection).append("\n\n"); + + // 第2步:AI解析基本信息 + debugResult.append("2. AI解析基本信息:\n"); + String basicInfo = extractBasicInfo(htmlContent, sourceUrl); + debugResult.append("基本信息结果: ").append(basicInfo).append("\n\n"); + + // 第3步:AI解析表格信息 + debugResult.append("3. AI解析表格信息:\n"); + String tableInfo = extractTableInfo(htmlContent, sourceUrl); + debugResult.append("表格信息结果: ").append(tableInfo).append("\n\n"); + + // 第4步:合并信息 + debugResult.append("4. 合并最终结果:\n"); + String finalResult = mergeInformation(basicInfo, tableInfo, sourceUrl); + debugResult.append("最终结果: ").append(finalResult).append("\n"); + + return debugResult.toString(); + + } catch (Exception e) { + log.error("调试解析失败", e); + return "调试解析失败: " + e.getMessage(); + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingChatService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingChatService.java new file mode 100644 index 00000000..be28d410 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingChatService.java @@ -0,0 +1,203 @@ +package com.touhouqing.chatAiDemo.service; + +import com.touhouqing.chatAiDemo.entity.graph.BiddingProject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 招标智能问答服务 + * 结合向量检索和图关系查询实现智能问答 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingChatService { + + private final ChatClient chatClient; + private final VectorStore vectorStore; + private final BiddingQueryService queryService; + + /** + * 智能问答:基于RAG的招标信息查询 + */ + public String chatWithBiddingData(String question) { + try { + // 1. 向量检索相关内容 + List relevantDocs = vectorStore.similaritySearch( + SearchRequest.builder() + .query(question) + .topK(5) + .similarityThreshold(0.3) + .build() + ); + + // 2. 构建上下文 + String context = buildContext(relevantDocs); + + // 3. 构建提示词 + String prompt = buildChatPrompt(question, context); + + // 4. 调用大模型生成回答 + String answer = chatClient.prompt() + .user(prompt) + .call() + .content(); + + log.info("招标智能问答完成,问题: {}", question); + return answer; + + } catch (Exception e) { + log.error("招标智能问答失败", e); + return "抱歉,查询招标信息时出现错误,请稍后重试。"; + } + } + + /** + * 企业相关项目查询 + */ + public String queryCompanyProjects(String companyName) { + try { + // 1. 通过图关系查询企业相关项目 + List projects = queryService.findProjectsByCompany(companyName); + + if (projects.isEmpty()) { + return String.format("未找到与企业\"%s\"相关的招标项目。", companyName); + } + + // 2. 构建项目信息摘要 + String projectSummary = projects.stream() + .limit(10) // 限制显示数量 + .map(this::formatProjectInfo) + .collect(Collectors.joining("\n\n")); + + // 3. 生成回答 + String prompt = String.format(""" + 请基于以下招标项目信息,为用户总结企业"%s"的相关项目情况: + + %s + + 请从以下角度进行分析: + 1. 项目数量和规模 + 2. 主要涉及的行业和地区 + 3. 项目类型分布 + 4. 总体特点和趋势 + """, companyName, projectSummary); + + return chatClient.prompt() + .user(prompt) + .call() + .content(); + + } catch (Exception e) { + log.error("查询企业项目失败", e); + return "查询企业项目信息时出现错误。"; + } + } + + /** + * 项目推荐服务 + */ + public String recommendProjects(String requirements) { + try { + // 1. 语义搜索匹配需求的项目 + List projects = queryService.semanticSearch(requirements, 10, 0.4); + + if (projects.isEmpty()) { + return "根据您的需求,暂未找到匹配的招标项目。建议您调整搜索条件或关注相关行业的最新招标信息。"; + } + + // 2. 构建项目信息 + String projectInfo = projects.stream() + .map(this::formatProjectInfo) + .collect(Collectors.joining("\n\n")); + + // 3. 生成推荐回答 + String prompt = String.format(""" + 用户需求:%s + + 基于以下匹配的招标项目,请为用户提供项目推荐: + + %s + + 请从以下角度提供建议: + 1. 最匹配的项目及原因 + 2. 项目的机会和挑战 + 3. 参与建议和注意事项 + """, requirements, projectInfo); + + return chatClient.prompt() + .user(prompt) + .call() + .content(); + + } catch (Exception e) { + log.error("项目推荐失败", e); + return "项目推荐服务暂时不可用,请稍后重试。"; + } + } + + /** + * 构建上下文信息 + */ + private String buildContext(List documents) { + return documents.stream() + .map(doc -> doc.getText()) + .collect(Collectors.joining("\n\n")); + } + + /** + * 构建聊天提示词 + */ + private String buildChatPrompt(String question, String context) { + return String.format(""" + 你是一个专业的招标信息助手,请基于以下招标信息回答用户的问题。 + + 招标信息上下文: + %s + + 用户问题:%s + + 请注意: + 1. 只基于提供的招标信息回答问题 + 2. 如果信息不足,请明确说明 + 3. 提供准确、有用的信息 + 4. 保持专业和友好的语调 + """, context, question); + } + + /** + * 格式化项目信息 + */ + private String formatProjectInfo(BiddingProject project) { + return String.format(""" + 项目名称:%s + 项目编号:%s + 预算:%s %s + 招标类型:%s + 项目状态:%s + 地区:%s + 行业:%s + 发布日期:%s + 来源:%s + """, + project.getProjectName(), + project.getProjectCode() != null ? project.getProjectCode() : "未知", + project.getBudget() != null ? project.getBudget() : "未公布", + project.getBudgetUnit() != null ? project.getBudgetUnit() : "", + project.getBiddingType() != null ? project.getBiddingType() : "未知", + project.getProjectStatus() != null ? project.getProjectStatus() : "未知", + project.getRegion() != null ? project.getRegion() : "未知", + project.getIndustry() != null ? project.getIndustry() : "未知", + project.getPublishDate() != null ? project.getPublishDate().toString() : "未知", + project.getSourceWebsite() != null ? project.getSourceWebsite() : "未知" + ); + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingCrawlerService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingCrawlerService.java new file mode 100644 index 00000000..8e786225 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingCrawlerService.java @@ -0,0 +1,98 @@ +package com.touhouqing.chatAiDemo.service; + +import com.touhouqing.chatAiDemo.component.BiddingWebPageProcessor; +import com.touhouqing.chatAiDemo.component.BiddingDataPipeline; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import us.codecraft.webmagic.Spider; + +import java.util.concurrent.CompletableFuture; + +/** + * 招标爬虫服务 + * 整合爬虫、AI解析、数据存储的完整流程 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingCrawlerService { + + private final BiddingDataPipeline dataPipeline; + + /** + * 启动招标爬虫任务 + */ + public CompletableFuture startCrawling(String startUrl) { + return CompletableFuture.supplyAsync(() -> { + try { + log.info("开始爬取招标数据,起始URL: {}", startUrl); + + Spider spider = Spider.create(new BiddingWebPageProcessor()) + .addUrl(startUrl) + .addPipeline(dataPipeline) + .thread(3); // 使用3个线程 + + spider.run(); + + String result = "招标爬虫任务完成,共处理 " + spider.getPageCount() + " 个页面"; + log.info(result); + return result; + + } catch (Exception e) { + log.error("招标爬虫任务执行失败", e); + return "招标爬虫任务失败: " + e.getMessage(); + } + }); + } + + /** + * 爬取单个招标页面 + */ + public CompletableFuture crawlSinglePage(String url) { + return CompletableFuture.supplyAsync(() -> { + try { + log.info("开始爬取单个招标页面: {}", url); + + Spider spider = Spider.create(new BiddingWebPageProcessor()) + .addUrl(url) + .addPipeline(dataPipeline) + .thread(1); + + spider.run(); + + String result = "单页面爬取完成: " + url; + log.info(result); + return result; + + } catch (Exception e) { + log.error("单页面爬取失败", e); + return "单页面爬取失败: " + e.getMessage(); + } + }); + } + + /** + * 爬取天津政府采购网 + */ + public CompletableFuture crawlTianjinBidding() { + String baseUrl = "http://www.ccgp-tianjin.gov.cn/portal/topicView.do?method=view&view=Infor&id=1665&ver=2&st=1"; + return startCrawling(baseUrl); + } + + /** + * 爬取中国政府采购网 + */ + public CompletableFuture crawlChinaGovProcurement() { + String baseUrl = "http://www.ccgp.gov.cn/cggg/"; + return startCrawling(baseUrl); + } + + /** + * 爬取指定地区的公共资源交易网 + */ + public CompletableFuture crawlRegionalBidding(String region, String url) { + log.info("开始爬取{}地区的招标信息", region); + return startCrawling(url); + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingDataService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingDataService.java new file mode 100644 index 00000000..d72da35a --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingDataService.java @@ -0,0 +1,142 @@ +package com.touhouqing.chatAiDemo.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.touhouqing.chatAiDemo.dto.BiddingProjectDto; +import com.touhouqing.chatAiDemo.entity.graph.*; +import com.touhouqing.chatAiDemo.repository.graph.BiddingProjectRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.UUID; + +/** + * 招标数据服务 + * 负责将AI解析的结构化数据保存到图数据库和向量数据库 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingDataService { + + private final BiddingProjectRepository projectRepository; + private final BiddingVectorService vectorService; + private final ObjectMapper objectMapper; + + /** + * 检查项目是否已存在 + */ + public boolean existsBySourceUrl(String sourceUrl) { + return projectRepository.existsBySourceUrl(sourceUrl); + } + + /** + * 保存招标数据到图数据库和向量数据库 + */ + @Transactional + public void saveBiddingData(String structuredData, String htmlContent, String sourceUrl, Long crawlTime) { + try { + // 预处理JSON数据,修复日期格式问题 + String processedData = preprocessJsonData(structuredData); + + // 解析AI返回的JSON数据 + BiddingProjectDto dto = objectMapper.readValue(processedData, BiddingProjectDto.class); + + // 生成向量ID + String vectorId = UUID.randomUUID().toString(); + + // 创建图数据库实体 + BiddingProject project = createBiddingProject(dto, sourceUrl, vectorId, crawlTime); + + // 保存到图数据库 + BiddingProject savedProject = projectRepository.save(project); + + // 保存到向量数据库 + vectorService.saveBiddingProjectVector(savedProject, dto, htmlContent); + + log.info("成功保存招标项目到图数据库和向量数据库: {}", dto.getProjectName()); + + } catch (Exception e) { + log.error("保存招标数据失败", e); + throw new RuntimeException("保存招标数据失败", e); + } + } + + /** + * 创建招标项目实体 + */ + private BiddingProject createBiddingProject(BiddingProjectDto dto, String sourceUrl, String vectorId, Long crawlTime) { + BiddingProject project = new BiddingProject(); + + project.setProjectName(dto.getProjectName()); + project.setProjectCode(dto.getProjectCode()); + project.setBudget(dto.getBudget()); + project.setBudgetUnit(dto.getBudgetUnit()); + project.setBiddingType(dto.getBiddingType()); + project.setProjectStatus(dto.getProjectStatus()); + project.setSourceUrl(sourceUrl); + project.setSourceWebsite(extractWebsiteName(sourceUrl)); + project.setRegion(dto.getRegion()); + project.setIndustry(dto.getIndustry()); + project.setVectorId(vectorId); + + // 解析日期 + if (dto.getPublishDate() != null) { + project.setPublishDate(dto.getPublishDate()); + } + if (dto.getBiddingDeadline() != null) { + project.setBiddingDeadline(dto.getBiddingDeadline()); + } + + project.setCreatedAt(LocalDateTime.now()); + project.setUpdatedAt(LocalDateTime.now()); + + return project; + } + + /** + * 从URL提取网站名称 + */ + private String extractWebsiteName(String url) { + try { + if (url.contains("ccgp-tianjin.gov.cn")) { + return "天津政府采购网"; + } else if (url.contains("ccgp.gov.cn")) { + return "中国政府采购网"; + } else if (url.contains("ggzy")) { + return "公共资源交易网"; + } + return "未知网站"; + } catch (Exception e) { + return "未知网站"; + } + } + + /** + * 预处理JSON数据,修复日期格式问题 + */ + private String preprocessJsonData(String jsonData) { + try { + // 修复日期格式:将 "2025-07-30" 转换为 "2025-07-30T00:00:00" + String processed = jsonData.replaceAll( + "\"publishDate\"\\s*:\\s*\"(\\d{4}-\\d{2}-\\d{2})\"", + "\"publishDate\": \"$1T00:00:00\"" + ); + + // 修复投标截止日期格式(如果有的话) + processed = processed.replaceAll( + "\"biddingDeadline\"\\s*:\\s*\"(\\d{4}-\\d{2}-\\d{2})\"", + "\"biddingDeadline\": \"$1T23:59:59\"" + ); + + log.debug("JSON预处理完成,原始: {}, 处理后: {}", jsonData, processed); + return processed; + + } catch (Exception e) { + log.warn("JSON预处理失败,使用原始数据: {}", e.getMessage()); + return jsonData; + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingQueryService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingQueryService.java new file mode 100644 index 00000000..b4c0f236 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingQueryService.java @@ -0,0 +1,187 @@ +package com.touhouqing.chatAiDemo.service; + +import com.touhouqing.chatAiDemo.entity.graph.BiddingProject; +import com.touhouqing.chatAiDemo.repository.graph.BiddingProjectRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 招标查询服务 + * 实现图数据库与向量数据库的关联查询 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingQueryService { + + private final VectorStore vectorStore; + private final BiddingProjectRepository projectRepository; + + /** + * 语义搜索招标项目 + * 结合向量搜索和图关系查询 + */ + public List semanticSearch(String query, int topK, double similarityThreshold) { + try { + // 1. 向量搜索 + SearchRequest searchRequest = SearchRequest.builder() + .query(query) + .topK(topK) + .similarityThreshold(similarityThreshold) + .build(); + + List vectorResults = vectorStore.similaritySearch(searchRequest); + + // 2. 通过向量搜索结果获取图数据库实体ID + List projectIds = vectorResults.stream() + .map(doc -> doc.getMetadata().get("graph_entity_id")) + .filter(id -> id != null) + .map(id -> Long.valueOf(id.toString())) + .distinct() + .collect(Collectors.toList()); + + // 3. 从图数据库获取完整的项目信息 + List projects = new ArrayList<>(); + for (Long projectId : projectIds) { + Optional project = projectRepository.findById(projectId); + project.ifPresent(projects::add); + } + + log.info("语义搜索完成,查询: {}, 找到 {} 个相关项目", query, projects.size()); + return projects; + + } catch (Exception e) { + log.error("语义搜索失败", e); + return new ArrayList<>(); + } + } + + /** + * 根据企业查找相关项目 + * 通过图关系扩展搜索结果 + */ + public List findProjectsByCompany(String companyName) { + try { + // 1. 先进行语义搜索找到相关的企业信息 + List vectorResults = vectorStore.similaritySearch( + SearchRequest.builder() + .query("企业名称:" + companyName) + .topK(10) + .similarityThreshold(0.3) + .build() + ); + + // 2. 获取项目ID + List projectIds = vectorResults.stream() + .map(doc -> doc.getMetadata().get("graph_entity_id")) + .filter(id -> id != null) + .map(id -> Long.valueOf(id.toString())) + .distinct() + .collect(Collectors.toList()); + + // 3. 通过图数据库查询扩展相关项目 + List projects = new ArrayList<>(); + for (Long projectId : projectIds) { + Optional project = projectRepository.findProjectWithAllRelations(projectId); + project.ifPresent(projects::add); + } + + return projects; + + } catch (Exception e) { + log.error("根据企业查找项目失败", e); + return new ArrayList<>(); + } + } + + /** + * 智能推荐相似项目 + * 基于项目内容的语义相似度 + */ + public List recommendSimilarProjects(Long projectId, int limit) { + try { + // 1. 获取原项目信息 + Optional originalProject = projectRepository.findById(projectId); + if (originalProject.isEmpty()) { + return new ArrayList<>(); + } + + BiddingProject project = originalProject.get(); + + // 2. 构建查询文本 + String queryText = String.format("%s %s %s %s", + project.getProjectName(), + project.getIndustry(), + project.getRegion(), + project.getBiddingType() + ); + + // 3. 向量搜索相似项目 + List vectorResults = vectorStore.similaritySearch( + SearchRequest.builder() + .query(queryText) + .topK(limit + 1) // +1 因为可能包含自己 + .similarityThreshold(0.4) + .build() + ); + + // 4. 过滤掉原项目,获取其他相似项目 + List similarProjects = vectorResults.stream() + .map(doc -> doc.getMetadata().get("graph_entity_id")) + .filter(id -> id != null) + .map(id -> Long.valueOf(id.toString())) + .filter(id -> !id.equals(projectId)) // 排除原项目 + .distinct() + .limit(limit) + .map(id -> projectRepository.findById(id)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + return similarProjects; + + } catch (Exception e) { + log.error("推荐相似项目失败", e); + return new ArrayList<>(); + } + } + + /** + * 复合查询:结合多个条件进行搜索 + */ + public List complexSearch(String semanticQuery, String region, String industry, + Double minBudget, Double maxBudget) { + try { + List results = new ArrayList<>(); + + // 1. 如果有语义查询,先进行向量搜索 + if (semanticQuery != null && !semanticQuery.trim().isEmpty()) { + results = semanticSearch(semanticQuery, 50, 0.3); + } else { + // 否则获取所有项目 + results = projectRepository.findAll(); + } + + // 2. 应用图数据库过滤条件 + return results.stream() + .filter(project -> region == null || region.equals(project.getRegion())) + .filter(project -> industry == null || industry.equals(project.getIndustry())) + .filter(project -> minBudget == null || project.getBudget() == null || project.getBudget() >= minBudget) + .filter(project -> maxBudget == null || project.getBudget() == null || project.getBudget() <= maxBudget) + .collect(Collectors.toList()); + + } catch (Exception e) { + log.error("复合查询失败", e); + return new ArrayList<>(); + } + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingVectorService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingVectorService.java new file mode 100644 index 00000000..79f750f6 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/BiddingVectorService.java @@ -0,0 +1,214 @@ +package com.touhouqing.chatAiDemo.service; + +import com.touhouqing.chatAiDemo.dto.BiddingProjectDto; +import com.touhouqing.chatAiDemo.entity.graph.BiddingProject; +import com.touhouqing.chatAiDemo.entity.vector.VectorDocument; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 招标向量数据库服务 + * 负责将招标数据存储到向量数据库 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class BiddingVectorService { + + private final VectorStore vectorStore; + + /** + * 保存招标项目到向量数据库 + */ + public void saveBiddingProjectVector(BiddingProject project, BiddingProjectDto dto, String htmlContent) { + try { + List documents = new ArrayList<>(); + + // 1. 项目描述文档 + if (dto.getProjectDescription() != null && !dto.getProjectDescription().trim().isEmpty()) { + Document projectDoc = createProjectDescriptionDocument(project, dto); + documents.add(projectDoc); + } + + // 2. 项目需求文档 + if (dto.getRequirements() != null && !dto.getRequirements().trim().isEmpty()) { + Document requirementDoc = createProjectRequirementDocument(project, dto); + documents.add(requirementDoc); + } + + // 3. 招标公告文档(基于HTML内容提取的文本) + Document announcementDoc = createAnnouncementDocument(project, dto, htmlContent); + documents.add(announcementDoc); + + // 4. 企业相关文档(如果有中标企业信息) + if (dto.getAwardedCompanies() != null && !dto.getAwardedCompanies().isEmpty()) { + Document companyDoc = createCompanyDocument(project, dto); + documents.add(companyDoc); + } + + // 批量保存到向量数据库 + vectorStore.add(documents); + + log.info("成功保存 {} 个文档到向量数据库,项目: {}", documents.size(), project.getProjectName()); + + } catch (Exception e) { + log.error("保存向量数据失败", e); + throw new RuntimeException("保存向量数据失败", e); + } + } + + /** + * 创建项目描述文档 + */ + private Document createProjectDescriptionDocument(BiddingProject project, BiddingProjectDto dto) { + String content = String.format(""" + 项目名称:%s + 项目编号:%s + 项目描述:%s + 预算:%s %s + 招标类型:%s + 项目状态:%s + 地区:%s + 行业:%s + """, + project.getProjectName(), + project.getProjectCode(), + dto.getProjectDescription(), + project.getBudget(), + project.getBudgetUnit(), + project.getBiddingType(), + project.getProjectStatus(), + project.getRegion(), + project.getIndustry() + ); + + Map metadata = createBaseMetadata(project, VectorDocument.DocumentType.PROJECT_DESCRIPTION); + + Document document = new Document(content, metadata); + return document; + } + + /** + * 创建项目需求文档 + */ + private Document createProjectRequirementDocument(BiddingProject project, BiddingProjectDto dto) { + String content = String.format(""" + 项目名称:%s + 项目需求:%s + 技术要求:%s + """, + project.getProjectName(), + dto.getRequirements(), + dto.getRequirements() + ); + + Map metadata = createBaseMetadata(project, VectorDocument.DocumentType.PROJECT_REQUIREMENTS); + + return new Document(content, metadata); + } + + /** + * 创建招标公告文档 + */ + private Document createAnnouncementDocument(BiddingProject project, BiddingProjectDto dto, String htmlContent) { + // 从HTML中提取纯文本内容 + String textContent = extractTextFromHtml(htmlContent); + + String content = String.format(""" + 招标公告 + 项目名称:%s + 发布机构:%s + 公告内容:%s + """, + project.getProjectName(), + dto.getPublishingOrganization() != null ? dto.getPublishingOrganization().getOrganizationName() : "未知", + textContent.length() > 2000 ? textContent.substring(0, 2000) + "..." : textContent + ); + + Map metadata = createBaseMetadata(project, VectorDocument.DocumentType.BIDDING_ANNOUNCEMENT); + + return new Document(content, metadata); + } + + /** + * 创建企业相关文档 + */ + private Document createCompanyDocument(BiddingProject project, BiddingProjectDto dto) { + StringBuilder content = new StringBuilder(); + content.append("项目名称:").append(project.getProjectName()).append("\n"); + content.append("中标企业信息:\n"); + + dto.getAwardedCompanies().forEach(company -> { + content.append("企业名称:").append(company.getCompanyName()).append("\n"); + content.append("中标金额:").append(company.getAwardAmount()).append("\n"); + }); + + Map metadata = createBaseMetadata(project, VectorDocument.DocumentType.AWARD_RESULT); + + return new Document(content.toString(), metadata); + } + + /** + * 创建基础元数据 + * 确保所有值都不为null,向量数据库不允许null值 + */ + private Map createBaseMetadata(BiddingProject project, VectorDocument.DocumentType documentType) { + Map metadata = new HashMap<>(); + + // 使用安全的方法添加metadata,避免null值 + putIfNotNull(metadata, "graph_entity_id", project.getId()); + putIfNotNull(metadata, "graph_entity_type", "BiddingProject"); + putIfNotNull(metadata, "document_type", documentType.name()); + putIfNotNull(metadata, "project_name", project.getProjectName()); + putIfNotNull(metadata, "source_url", project.getSourceUrl()); + putIfNotNull(metadata, "source_website", project.getSourceWebsite()); + putIfNotNull(metadata, "region", project.getRegion()); + putIfNotNull(metadata, "industry", project.getIndustry()); + putIfNotNull(metadata, "project_status", project.getProjectStatus()); + putIfNotNull(metadata, "created_at", LocalDateTime.now().toString()); + + return metadata; + } + + /** + * 安全地添加metadata,如果值为null则使用默认值 + */ + private void putIfNotNull(Map metadata, String key, Object value) { + if (value != null) { + metadata.put(key, value); + } else { + // 为null值提供默认值 + switch (key) { + case "project_name" -> metadata.put(key, "未知项目"); + case "source_url" -> metadata.put(key, ""); + case "source_website" -> metadata.put(key, "未知网站"); + case "region" -> metadata.put(key, "未知地区"); + case "industry" -> metadata.put(key, "未知行业"); + case "project_status" -> metadata.put(key, "未知状态"); + default -> metadata.put(key, ""); + } + } + } + + /** + * 从HTML中提取纯文本 + */ + private String extractTextFromHtml(String htmlContent) { + if (htmlContent == null) return ""; + + // 简单的HTML标签移除(实际项目中可以使用Jsoup等库) + return htmlContent + .replaceAll("<[^>]+>", " ") + .replaceAll("\\s+", " ") + .trim(); + } +} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/DirectHttpCrawlerService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/DirectHttpCrawlerService.java new file mode 100644 index 00000000..c3ec3883 --- /dev/null +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/DirectHttpCrawlerService.java @@ -0,0 +1,209 @@ +package com.touhouqing.chatAiDemo.service; + +import com.touhouqing.chatAiDemo.component.BiddingDataPipeline; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.StandardCharsets; + +/** + * 直接HTTP爬虫服务 + * 使用RestTemplate直接发送HTTP请求,避免WebMagic的限制 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DirectHttpCrawlerService { + + private final BiddingDataPipeline dataPipeline; + + /** + * 直接爬取单个页面 + */ + public String crawlPageDirectly(String url) { + try { + log.info("开始直接爬取页面: {}", url); + + // 创建RestTemplate + RestTemplate restTemplate = new RestTemplate(); + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); + headers.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"); + headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); + headers.set("Accept-Encoding", "gzip, deflate"); + headers.set("Connection", "keep-alive"); + headers.set("Upgrade-Insecure-Requests", "1"); + headers.set("Cache-Control", "max-age=0"); + + HttpEntity entity = new HttpEntity<>(headers); + + // 发送请求 + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + entity, + String.class + ); + + if (response.getStatusCode().is2xxSuccessful()) { + String htmlContent = response.getBody(); + log.info("成功获取页面内容,长度: {}", htmlContent != null ? htmlContent.length() : 0); + + // 直接调用数据处理管道 + if (htmlContent != null && !htmlContent.trim().isEmpty()) { + // 创建一个模拟的Page对象数据 + processCrawledData(url, htmlContent); + return "页面爬取成功,内容长度: " + htmlContent.length(); + } else { + return "页面内容为空"; + } + } else { + log.error("HTTP请求失败,状态码: {}", response.getStatusCode()); + return "HTTP请求失败,状态码: " + response.getStatusCode(); + } + + } catch (Exception e) { + log.error("直接爬取页面失败: {}", url, e); + return "爬取失败: " + e.getMessage(); + } + } + + /** + * 仅获取HTML内容,不进行数据处理 + */ + public String getHtmlContent(String url) { + try { + log.info("获取HTML内容: {}", url); + + // 创建RestTemplate + RestTemplate restTemplate = new RestTemplate(); + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); + headers.set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"); + headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); + headers.set("Accept-Encoding", "gzip, deflate"); + headers.set("Connection", "keep-alive"); + headers.set("Upgrade-Insecure-Requests", "1"); + headers.set("Cache-Control", "max-age=0"); + + HttpEntity entity = new HttpEntity<>(headers); + + // 发送请求 + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + entity, + String.class + ); + + if (response.getStatusCode().is2xxSuccessful()) { + String htmlContent = response.getBody(); + log.info("成功获取HTML内容,长度: {}", htmlContent != null ? htmlContent.length() : 0); + return htmlContent; + } else { + log.error("HTTP请求失败,状态码: {}", response.getStatusCode()); + return null; + } + + } catch (Exception e) { + log.error("获取HTML内容失败: {}", url, e); + return null; + } + } + + /** + * 处理爬取到的数据 + */ + private void processCrawledData(String url, String htmlContent) { + try { + // 提取标题 + String title = extractTitle(htmlContent); + + log.info("开始处理爬取数据 - URL: {}, 标题: {}", url, title); + + // 创建数据对象传递给处理管道 + CrawledData data = new CrawledData(); + data.setUrl(url); + data.setTitle(title); + data.setHtmlContent(htmlContent); + data.setType("bidding_detail"); + data.setCrawlTime(System.currentTimeMillis()); + + // 调用数据处理管道 + dataPipeline.process(data); + + } catch (Exception e) { + log.error("处理爬取数据失败", e); + } + } + + /** + * 提取页面标题 + */ + private String extractTitle(String htmlContent) { + try { + // 简单的标题提取 + if (htmlContent.contains("")) { + int start = htmlContent.indexOf("<title>") + 7; + int end = htmlContent.indexOf("", start); + if (end > start) { + return htmlContent.substring(start, end).trim(); + } + } + + // 尝试从meta标签提取 + if (htmlContent.contains("ArticleTitle")) { + int start = htmlContent.indexOf("ArticleTitle\" content=\""); + if (start > 0) { + start += 23; + int end = htmlContent.indexOf("\"", start); + if (end > start) { + return htmlContent.substring(start, end).trim(); + } + } + } + + return "未知标题"; + } catch (Exception e) { + log.error("提取标题失败", e); + return "标题提取失败"; + } + } + + /** + * 爬取数据的内部类 + */ + public static class CrawledData { + private String url; + private String title; + private String htmlContent; + private String type; + private long crawlTime; + + // Getters and Setters + public String getUrl() { return url; } + public void setUrl(String url) { this.url = url; } + + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + + public String getHtmlContent() { return htmlContent; } + public void setHtmlContent(String htmlContent) { this.htmlContent = htmlContent; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public long getCrawlTime() { return crawlTime; } + public void setCrawlTime(long crawlTime) { this.crawlTime = crawlTime; } + } +} diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseReservationService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseReservationService.java index e51af372..f02af6d2 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseReservationService.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseReservationService.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service; import com.touhouqing.chatAiDemo.entity.po.CourseReservation; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseService.java index 99b144e2..019e7b91 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseService.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ICourseService.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service; import com.touhouqing.chatAiDemo.entity.po.Course; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ISchoolService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ISchoolService.java index bc70cb1d..a9d7f10a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ISchoolService.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ISchoolService.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service; import com.touhouqing.chatAiDemo.entity.po.School; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementAIService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementAIService.java index d0c2a68d..b0d629cf 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementAIService.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementAIService.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service; import lombok.RequiredArgsConstructor; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementCrawlerService.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementCrawlerService.java index b02a3f53..58760c7c 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementCrawlerService.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/ProcurementCrawlerService.java @@ -1,23 +1,7 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service; -import com.touhouqing.chatAiDemo.component.GovProcurementPageProcessor; -import com.touhouqing.chatAiDemo.component.ProcurementDataPipeline; +import com.touhouqing.chatAiDemo.component.BiddingWebPageProcessor; +import com.touhouqing.chatAiDemo.component.BiddingDataPipeline; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -29,8 +13,8 @@ @Service @RequiredArgsConstructor public class ProcurementCrawlerService { - - private final ProcurementDataPipeline dataPipeline; + + private final BiddingDataPipeline dataPipeline; /** * 启动爬虫任务 @@ -40,7 +24,7 @@ public CompletableFuture startCrawling(String startUrl) { try { log.info("开始爬取政府采购数据,起始URL: {}", startUrl); - Spider spider = Spider.create(new GovProcurementPageProcessor()) + Spider spider = Spider.create(new BiddingWebPageProcessor()) .addUrl(startUrl) .addPipeline(dataPipeline) .thread(3); // 使用3个线程 @@ -66,7 +50,7 @@ public CompletableFuture crawlSinglePage(String url) { try { log.info("开始爬取单个页面: {}", url); - Spider spider = Spider.create(new GovProcurementPageProcessor()) + Spider spider = Spider.create(new BiddingWebPageProcessor()) .addUrl(url) .addPipeline(dataPipeline) .thread(1); diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseReservationServiceImpl.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseReservationServiceImpl.java index a267e010..4f7ee70a 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseReservationServiceImpl.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseReservationServiceImpl.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service.impl; import com.touhouqing.chatAiDemo.entity.po.CourseReservation; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseServiceImpl.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseServiceImpl.java index 53d8f4ee..55ffe147 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseServiceImpl.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/CourseServiceImpl.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service.impl; import com.touhouqing.chatAiDemo.entity.po.Course; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/SchoolServiceImpl.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/SchoolServiceImpl.java index f50c8cb6..47964a8c 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/SchoolServiceImpl.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/service/impl/SchoolServiceImpl.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.service.impl; import com.touhouqing.chatAiDemo.entity.po.School; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/tools/CourseTools.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/tools/CourseTools.java index 381fb6bf..e82ef313 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/tools/CourseTools.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/java/com/touhouqing/chatAiDemo/tools/CourseTools.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo.tools; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/resources/application.yaml b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/resources/application.yaml index 41c7f4d0..e373c990 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/resources/application.yaml +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/main/resources/application.yaml @@ -47,9 +47,9 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver neo4j: - uri: neo4j+s://0b6068b5.databases.neo4j.io + uri: ${NEO4J_URI:neo4j+s://cf08d9c9.databases.neo4j.io} authentication: - username: neo4j + username: ${NEO4J_USERNAME:neo4j} password: ${NEO4J_PASSWORD} pool: max-connection-pool-size: 16 @@ -60,3 +60,10 @@ logging: level: org.springframework.ai: DEBUG com.touhouqing.chatAiDemo: DEBUG + org.springframework.data.neo4j: DEBUG + org.neo4j.driver: DEBUG + org.springframework.boot.autoconfigure.neo4j: DEBUG + +# CORS 配置 +cors: + allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:5173} \ No newline at end of file diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ChatAiDemoApplicationTests.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ChatAiDemoApplicationTests.java index 2c8d0844..0797f5b1 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ChatAiDemoApplicationTests.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ChatAiDemoApplicationTests.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo; import com.touhouqing.chatAiDemo.entity.Movie; diff --git a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ProcurementCrawlerTest.java b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ProcurementCrawlerTest.java index 78724508..4bd2750c 100644 --- a/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ProcurementCrawlerTest.java +++ b/spring-ai-alibaba-usecase-example/spring-ai-alibaba-comprehensive-example/src/test/java/com/touhouqing/chatAiDemo/ProcurementCrawlerTest.java @@ -1,19 +1,3 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package com.touhouqing.chatAiDemo; import com.touhouqing.chatAiDemo.service.ProcurementCrawlerService;