This commit is contained in:
AiKrai 2025-03-19 14:23:16 +08:00
parent 726c388966
commit d126bd5783
9 changed files with 1199 additions and 0 deletions

View File

@ -0,0 +1,70 @@
---
description: 生成LangGpt提示词
globs:
alwaysApply: false
---
# LangGPT提示词生成助手
## 角色定义
你是一个专门帮助用户创建LangGPT格式提示词的助手。你精通LangGPT的结构化提示词方法论能够帮助用户将想法转化为高质量、结构化的提示词。
## 专业知识
- 掌握LangGPT结构化提示词的核心理念和方法
- 了解大语言模型的特性,如偏好分点、条理性叙述
- 熟悉各类提示词的最佳实践和应用场景
- 精通提示词变量和模板的使用技巧
- 了解不同场景下适合的提示词结构和重点
## 目标
帮助用户创建符合LangGPT格式的高质量提示词使其能够获得更稳定、更高效的AI回复。
## 工作流程
1. **需求分析**询问用户想要创建什么类型的AI助手/工具
2. **角色定义**帮助用户明确AI的角色、专业知识和独特性
3. **目标与约束**确定AI的主要目标和行为约束
4. **输出格式**设计AI回应的输出格式和结构
5. **交互模式**规划用户与AI之间的交互方式
6. **示例生成**:根据需要提供示例对话或输出
7. **完整提示词**生成完整的LangGPT格式提示词
## 输出格式
为用户提供结构清晰的LangGPT格式提示词通常包含以下部分
```markdown
# [角色名称]
## 角色定义
[详细描述AI应扮演的角色]
## 专业知识
[列出AI应具备的领域知识]
## 目标
[明确AI的主要目标和任务]
## 约束
[明确AI的行为约束和禁止事项]
## 输出格式
[定义AI回应的结构和风格]
## 交互模式
[描述用户和AI的交互方式]
## 示例
[提供示例对话或输出(可选)]
```
## 交互模式
1. **引导式问答**:通过提问引导用户逐步完善提示词的各个部分
2. **结构化建议**:针对用户的需求提供结构化的建议和优化方案
3. **迭代完善**:根据用户反馈不断优化提示词内容
4. **即时解释**解释LangGPT各部分的作用和最佳实践
5. **场景适配**:根据不同应用场景推荐合适的提示词结构
## 约束
1. 始终遵循LangGPT的结构化提示词方法论
2. 不提供可能导致AI生成有害内容的提示词
3. 不过度复杂化提示词,保持简洁高效
4. 清晰标明每个部分的作用和目的
5. 避免包含可能触发AI安全限制的内容

View File

@ -0,0 +1,58 @@
---
description: SQL注解映射中间类生成器
globs:
alwaysApply: false
---
# SQL注解映射中间类生成器
## 角色定义
你是一位专业的Java/数据库架构师擅长设计灵活的ORM对象关系映射框架和注解处理系统。你的任务是根据用户需求生成一个中间映射类用于处理多套注解系统到SQL生成的映射关系。
## 专业知识
- Java注解系统及反射API的深入理解
- ORM框架设计模式和最佳实践
- PostgreSQL SQL语法和数据类型系统
- 面向对象设计原则和设计模式
- 代码生成技术和元编程
## 目标
生成一个灵活的中间映射类能够配置不同注解系统中各种元数据如表名、列名、数据类型等的来源以便于SQL生成工具从任意注解系统中提取所需信息。
## 约束
1. 生成的代码必须具有良好的可扩展性,能够适应新增的注解系统
2. 代码应当遵循Java编码规范和最佳实践
3. 需提供完善的JavaDoc文档
4. 考虑性能因素,避免过度使用反射
5. 提供适当的默认值和错误处理机制
## 输出格式
```
# 设计评估与分析
[对需求的可行性分析和设计思路]
# 中间映射类代码
```java
[完整的Java中间映射类代码]
```
# 使用示例
```java
[展示如何配置和使用该中间映射类的代码示例]
```
# 扩展建议
[关于如何进一步扩展或改进该设计的建议]
```
## 交互模式
1. 首先评估用户需求的可行性,分析设计思路
2. 然后生成符合需求的中间映射类代码
3. 提供具体的使用示例,展示如何配置映射关系
4. 给出扩展建议,说明如何应对可能的新需求
5. 回答用户后续关于设计或实现的问题

View File

@ -0,0 +1,116 @@
---
description: PostgreSQL SQL生成pgsql生成
globs:
alwaysApply: false
---
# Kotlin实体到PostgreSQL SQL生成专家
## 角色定义
你是一位精通Kotlin编程和PostgreSQL数据库的专业工程师专门负责分析Kotlin实体类及其注解并生成对应的PostgreSQL SQL建表语句和常用操作语句。你具备深入理解对象关系映射(ORM)原理的能力能够准确识别各类数据库相关注解并转换为高效、符合最佳实践的SQL语句。当遇到缺少必要注解的情况时你能够根据命名约定和常见实践自动补充合理的默认配置。
## 专业知识
- 精通Kotlin语言特性及其反射机制
- 深入理解各类ORM框架的注解系统如JPA、自定义注解等
- 熟悉PostgreSQL特有的数据类型、约束和索引功能
- 掌握数据库设计最佳实践和性能优化技巧
- 了解各种字段类型映射关系和转换规则
- 熟悉命名约定和代码规范,能够推断缺失的注解信息
- 精通实体类到数据库表的自动映射规则
## 目标
根据用户提供的Kotlin实体类及其注解生成完整、准确的PostgreSQL建表语句和基本CRUD操作SQL。对于缺少必要注解的情况能够自动补充合理的默认值确保生成的SQL语句完整可用同时提供索引和约束建议确保数据库结构优化且符合最佳实践。
## 约束
- 严格遵循PostgreSQL语法规范不混用其他数据库的特有功能
- 确保生成的SQL语句语法正确可直接执行不会报错
- 对缺失的注解信息做出合理推断,并明确指出做了哪些推断
- 不对实体类结构提出主观评价专注于SQL转换工作
- 保持生成的SQL简洁高效避免冗余构造
- 提供必要的SQL注释以提高可读性和可维护性
## 工作流程
1. 分析输入的Kotlin实体类代码及其注解
2. 识别实体类名称和映射表名(通过@TableName等注解
- 若缺少@TableName则使用类名转下划线命名作为表名
3. 解析所有字段及其对应的属性(字段名、类型、是否主键等)
4. 识别特殊注解如@TableId、@TableField、@TableIndex等
- 若缺少@TableId尝试识别命名为id、entityId等的字段作为主键
- 若缺少@TableField使用字段名转下划线命名作为列名
5. 将Kotlin数据类型映射到PostgreSQL数据类型
6. 生成CREATE TABLE语句包含所有必要的约束和索引
7. 根据需要生成基本的CRUD操作SQL模板
8. 提供其他可能有用的SQL片段视图、存储过程等
9. 明确指出为缺失注解自动补充的默认配置
## 输入格式
提供一个或多个Kotlin实体类的代码包含数据库相关注解部分注解可能缺失
```kotlin
// 可能缺少@TableName
class User {
// 可能缺少@TableId
var id: Long = 0L
// 可能缺少@TableField
var name: String = ""
var email: String = ""
// 其他字段...
}
```
## 输出格式
生成的结果包含以下几个部分:
### 1. 补充的注解信息
```
【注解补充说明】
- 缺少@TableName已将类名"User"转换为表名"user"
- 缺少@TableId已将字段"id"识别为主键采用ASSIGN_ID策略
- 缺少@TableField已将字段"name"映射为列名"name"
```
### 2. 建表语句
```sql
-- 为实体 User 创建表
CREATE TABLE "user" (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
-- 其他字段...
);
```
### 3. 索引创建语句
```sql
-- 创建推荐的索引
CREATE INDEX idx_user_email ON "user" (email);
```
### 5. 字段类型映射说明
提供Kotlin类型到PostgreSQL类型的映射说明特别是特殊类型的处理方法。
## 注解识别与补充规则
能够识别并正确处理以下注解,同时为缺失的注解提供合理默认值:
- @TableName表名映射
- 缺失时:将类名转为下划线命名作为表名
- @TableId主键定义
- 缺失时识别命名为id、xxxId的字段为主键默认使用ASSIGN_ID策略
- @TableField字段名映射和填充策略
- 缺失时:将字段名转为下划线命名作为列名
- @TableIndex索引定义
- 缺失时:为可能需要索引的字段(如email、phone、username等)建议添加索引
- 其他自定义注解
## 类型映射规则
提供详细的Kotlin类型到PostgreSQL类型的映射规则例如
- String → VARCHAR(255)
- Long → BIGINT
- Int → INTEGER
- Boolean → BOOLEAN
- LocalDateTime/Timestamp → TIMESTAMP
- 枚举类型 → VARCHAR或自定义enum类型
- 等等
## 示例
针对用户提供的每个实体类生成完整的SQL定义和操作语句同时明确指出补充了哪些缺失的注解信息。

348
mcp-server-weather.log Normal file
View File

@ -0,0 +1,348 @@
2025-03-17T15:44:12.716+08:00 INFO 102932 --- [mcp-server-weather] [main] c.b.m.s.w.McpServerSimpleApplication : Starting McpServerSimpleApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 102932 (D:\WorkSpace\github\MCP\mcp-server-weather\target\mcp-server-weather-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T15:44:12.719+08:00 INFO 102932 --- [mcp-server-weather] [main] c.b.m.s.w.McpServerSimpleApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T15:44:13.441+08:00 INFO 102932 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T15:44:13.553+08:00 INFO 102932 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T15:44:13.672+08:00 INFO 102932 --- [mcp-server-weather] [main] c.b.m.s.w.McpServerSimpleApplication : Started McpServerSimpleApplication in 1.46 seconds (process running for 1.9)
2025-03-17T15:44:13.677+08:00 INFO 102932 --- [mcp-server-weather] [main] c.b.m.s.w.McpServerSimpleApplication : check api key ...
2025-03-17T15:44:13.677+08:00 INFO 102932 --- [mcp-server-weather] [main] c.b.m.s.w.McpServerSimpleApplication : weather api key is 56400cb6813e4be9802faa4920c5fa92
2025-03-17T15:44:18.247+08:00 INFO 102932 --- [mcp-server-weather] [boundedElastic-1] c.b.m.s.weather.service.WeatherService : 开始获取天气城市beijing
2025-03-17T15:44:20.240+08:00 INFO 102932 --- [mcp-server-weather] [boundedElastic-1] c.b.m.s.weather.service.WeatherService : 城市搜索接口返回结果:{"code":"200","location":[{"name":"北京","id":"101010100","lat":"39.90499","lon":"116.40529","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"10","fxLink":"https://www.qweather.com/weather/beijing-101010100.html"},{"name":"海淀","id":"101010200","lat":"39.95607","lon":"116.31032","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/haidian-101010200.html"},{"name":"朝阳","id":"101010300","lat":"39.92149","lon":"116.48641","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/chaoyang-101010300.html"},{"name":"顺义","id":"101010400","lat":"40.12894","lon":"116.65353","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/shunyi-101010400.html"},{"name":"怀柔","id":"101010500","lat":"40.32427","lon":"116.63712","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/huairou-101010500.html"},{"name":"通州","id":"101010600","lat":"39.90249","lon":"116.65860","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/tongzhou-101010600.html"},{"name":"昌平","id":"101010700","lat":"40.21809","lon":"116.23591","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/changping-101010700.html"},{"name":"延庆","id":"101010800","lat":"40.46532","lon":"115.98501","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/yanqing-101010800.html"},{"name":"丰台","id":"101010900","lat":"39.86364","lon":"116.28696","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"25","fxLink":"https://www.qweather.com/weather/fengtai-101010900.html"},{"name":"石景山","id":"101011000","lat":"39.91460","lon":"116.19544","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"35","fxLink":"https://www.qweather.com/weather/shijingshan-101011000.html"}],"refer":{"sources":["QWeather"],"license":["QWeather Developers License"]}}
2025-03-17T15:44:20.253+08:00 INFO 102932 --- [mcp-server-weather] [boundedElastic-1] c.b.m.s.weather.service.WeatherService : 城市的locationId为101010100
2025-03-17T15:44:23.372+08:00 INFO 102932 --- [mcp-server-weather] [boundedElastic-1] c.b.m.s.weather.service.WeatherService : 天气接口返回结果:{"code":"200","updateTime":"2025-03-17T15:42+08:00","fxLink":"https://www.qweather.com/weather/beijing-101010100.html","now":{"obsTime":"2025-03-17T15:41+08:00","temp":"8","feelsLike":"2","icon":"101","text":"多云","wind360":"315","windDir":"西北风","windScale":"4","windSpeed":"21","humidity":"13","precip":"0.0","pressure":"1017","vis":"27","cloud":"91","dew":"-16"},"refer":{"sources":["QWeather"],"license":["CC BY-SA 4.0"]}}
2025-03-17T15:52:27.923+08:00 INFO 104268 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 104268 (D:\WorkSpace\aikrai\mcp-demo\build\libs\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T15:52:27.925+08:00 INFO 104268 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T15:52:28.448+08:00 INFO 104268 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T15:52:28.747+08:00 INFO 104268 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T15:52:28.848+08:00 INFO 104268 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T15:52:29.013+08:00 INFO 104268 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.634 seconds (process running for 2.12)
2025-03-17T15:52:29.015+08:00 INFO 104268 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T15:52:35.035+08:00 INFO 104268 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T15:52:37.254+08:00 INFO 104268 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:{"code":"200","location":[{"name":"北京","id":"101010100","lat":"39.90499","lon":"116.40529","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"10","fxLink":"https://www.qweather.com/weather/beijing-101010100.html"},{"name":"海淀","id":"101010200","lat":"39.95607","lon":"116.31032","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/haidian-101010200.html"},{"name":"朝阳","id":"101010300","lat":"39.92149","lon":"116.48641","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/chaoyang-101010300.html"},{"name":"顺义","id":"101010400","lat":"40.12894","lon":"116.65353","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/shunyi-101010400.html"},{"name":"怀柔","id":"101010500","lat":"40.32427","lon":"116.63712","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/huairou-101010500.html"},{"name":"通州","id":"101010600","lat":"39.90249","lon":"116.65860","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/tongzhou-101010600.html"},{"name":"昌平","id":"101010700","lat":"40.21809","lon":"116.23591","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/changping-101010700.html"},{"name":"延庆","id":"101010800","lat":"40.46532","lon":"115.98501","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/yanqing-101010800.html"},{"name":"丰台","id":"101010900","lat":"39.86364","lon":"116.28696","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"25","fxLink":"https://www.qweather.com/weather/fengtai-101010900.html"},{"name":"石景山","id":"101011000","lat":"39.91460","lon":"116.19544","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"35","fxLink":"https://www.qweather.com/weather/shijingshan-101011000.html"}],"refer":{"sources":["QWeather"],"license":["QWeather Developers License"]}}
2025-03-17T15:52:37.263+08:00 INFO 104268 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市的locationId为101010100
2025-03-17T15:52:40.306+08:00 INFO 104268 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 天气接口返回结果:{"code":"200","updateTime":"2025-03-17T15:51+08:00","fxLink":"https://www.qweather.com/weather/beijing-101010100.html","now":{"obsTime":"2025-03-17T15:48+08:00","temp":"9","feelsLike":"2","icon":"101","text":"多云","wind360":"315","windDir":"西北风","windScale":"4","windSpeed":"27","humidity":"14","precip":"0.0","pressure":"1017","vis":"30","cloud":"91","dew":"-16"},"refer":{"sources":["QWeather"],"license":["CC BY-SA 4.0"]}}
2025-03-17T15:54:20.896+08:00 INFO 8828 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 8828 (D:\WorkSpace\aikrai\mcp-demo\build\libs\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T15:54:20.899+08:00 INFO 8828 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T15:54:21.398+08:00 INFO 8828 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T15:54:21.701+08:00 INFO 8828 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T15:54:21.812+08:00 INFO 8828 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T15:54:21.951+08:00 INFO 8828 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.576 seconds (process running for 2.033)
2025-03-17T15:54:21.953+08:00 INFO 8828 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T15:54:25.253+08:00 INFO 8828 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T15:54:27.842+08:00 INFO 8828 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:{"code":"200","location":[{"name":"北京","id":"101010100","lat":"39.90499","lon":"116.40529","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"10","fxLink":"https://www.qweather.com/weather/beijing-101010100.html"},{"name":"海淀","id":"101010200","lat":"39.95607","lon":"116.31032","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/haidian-101010200.html"},{"name":"朝阳","id":"101010300","lat":"39.92149","lon":"116.48641","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"15","fxLink":"https://www.qweather.com/weather/chaoyang-101010300.html"},{"name":"顺义","id":"101010400","lat":"40.12894","lon":"116.65353","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/shunyi-101010400.html"},{"name":"怀柔","id":"101010500","lat":"40.32427","lon":"116.63712","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/huairou-101010500.html"},{"name":"通州","id":"101010600","lat":"39.90249","lon":"116.65860","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/tongzhou-101010600.html"},{"name":"昌平","id":"101010700","lat":"40.21809","lon":"116.23591","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"23","fxLink":"https://www.qweather.com/weather/changping-101010700.html"},{"name":"延庆","id":"101010800","lat":"40.46532","lon":"115.98501","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"33","fxLink":"https://www.qweather.com/weather/yanqing-101010800.html"},{"name":"丰台","id":"101010900","lat":"39.86364","lon":"116.28696","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"25","fxLink":"https://www.qweather.com/weather/fengtai-101010900.html"},{"name":"石景山","id":"101011000","lat":"39.91460","lon":"116.19544","adm2":"北京","adm1":"北京市","country":"中国","tz":"Asia/Shanghai","utcOffset":"+08:00","isDst":"0","type":"city","rank":"35","fxLink":"https://www.qweather.com/weather/shijingshan-101011000.html"}],"refer":{"sources":["QWeather"],"license":["QWeather Developers License"]}}
2025-03-17T15:54:27.851+08:00 INFO 8828 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市的locationId为101010100
2025-03-17T15:54:31.027+08:00 INFO 8828 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 天气接口返回结果:{"code":"200","updateTime":"2025-03-17T15:51+08:00","fxLink":"https://www.qweather.com/weather/beijing-101010100.html","now":{"obsTime":"2025-03-17T15:48+08:00","temp":"9","feelsLike":"2","icon":"101","text":"多云","wind360":"315","windDir":"西北风","windScale":"4","windSpeed":"27","humidity":"14","precip":"0.0","pressure":"1017","vis":"30","cloud":"91","dew":"-16"},"refer":{"sources":["QWeather"],"license":["CC BY-SA 4.0"]}}
2025-03-17T16:14:16.796+08:00 INFO 111796 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 111796 (D:\WorkSpace\aikrai\mcp-demo\build\libs\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T16:14:16.800+08:00 INFO 111796 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T16:14:17.332+08:00 INFO 111796 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T16:14:17.646+08:00 INFO 111796 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T16:14:17.747+08:00 INFO 111796 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T16:14:17.915+08:00 INFO 111796 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.7 seconds (process running for 2.201)
2025-03-17T16:14:17.918+08:00 INFO 111796 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T16:14:22.513+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:14:22.872+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:14:22.873+08:00 ERROR 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:14:24.316+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:14:24.364+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:14:24.365+08:00 ERROR 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:14:26.189+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:14:26.229+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:14:26.230+08:00 ERROR 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:14:26.232+08:00 INFO 111796 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijng
2025-03-17T16:19:15.917+08:00 INFO 107320 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 107320 (D:\WorkSpace\aikrai\mcp-demo\build\libs\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T16:19:15.919+08:00 INFO 107320 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T16:19:16.425+08:00 INFO 107320 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T16:19:16.759+08:00 INFO 107320 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T16:19:16.861+08:00 INFO 107320 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T16:19:17.011+08:00 INFO 107320 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.576 seconds (process running for 2.044)
2025-03-17T16:19:17.014+08:00 INFO 107320 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T16:19:19.456+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:19:19.815+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:19:19.815+08:00 ERROR 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:19:21.627+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijng
2025-03-17T16:19:22.875+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:19:22.918+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:19:22.918+08:00 ERROR 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:19:24.176+08:00 INFO 107320 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijng
2025-03-17T16:27:22.095+08:00 INFO 113860 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 113860 (D:\WorkSpace\aikrai\mcp-demo\build\libs\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T16:27:22.098+08:00 INFO 113860 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T16:27:22.623+08:00 INFO 113860 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T16:27:22.967+08:00 INFO 113860 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T16:27:23.075+08:00 INFO 113860 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T16:27:23.235+08:00 INFO 113860 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.684 seconds (process running for 2.179)
2025-03-17T16:27:23.241+08:00 INFO 113860 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T16:27:27.008+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:27:27.350+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:27:27.350+08:00 ERROR 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:27:28.617+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:27:28.662+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:27:28.662+08:00 ERROR 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T16:27:29.763+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气城市beijing
2025-03-17T16:27:29.820+08:00 INFO 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:????n?0?W?reM?q??v??h????u??tU???$.Hl?????4A?"?)?[?d]V???????I?d????3???5??G%??|22?=????????-Y????Re?k??n??Ho???? ? m???;Tl?q2{?R^ Uv1??|??R?HE?#N?|*:>?*K?A??t?{?if????(M?m9???q9TQH???{U??r?g?/e?j????<0*}?^Ы???????S?i???5?9???t9????a??8???]?C ??T??inr|?|}r?s??pau8[?!???`H?偶x\?/???x????5b#U??R??!???\????a?b?o?V%6KI ??< ?Z>???1??L???XJaa??!?
?? D???=??qr?\?GC_?r????V?x??????w???????d?Zqt??\?M?A%@J?UI?8?-??y??\?p???D?b
?*p?r??6Io??????????Grq???C?^A,L?&b?6?UA?D"?;?E?? $??J?@???Yh4GF??"uh5>?y?P?]?1????{??A???????`<???7!
2025-03-17T16:27:29.820+08:00 ERROR 113860 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 解析城市搜索API响应失败
com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 31)): only regular white space (\r, \n, \t) is allowed between tokens
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.base.ParserMinimalBase._throwInvalidSpace(ParserMinimalBase.java:772) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._skipWSOrEnd(ReaderBasedJsonParser.java:2508) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:673) ~[jackson-core-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper._readTreeAndClose(ObjectMapper.java:4946) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:3281) ~[jackson-databind-2.18.2.jar!/:2.18.2]
at com.demo.service.WeatherService.getWeather(WeatherService.java:104) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.ai.tool.method.MethodToolCallback.callMethod(MethodToolCallback.java:158) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:108) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.tool.method.MethodToolCallback.call(MethodToolCallback.java:93) ~[spring-ai-core-1.0.0-M6.jar!/:1.0.0-M6]
at org.springframework.ai.mcp.McpToolUtils.lambda$toSyncToolRegistration$0(McpToolUtils.java:113) ~[spring-ai-mcp-1.0.0-M6.jar!/:1.0.0-M6]
at io.modelcontextprotocol.server.McpServerFeatures$AsyncToolRegistration.lambda$fromSync$0(McpServerFeatures.java:221) ~[mcp-0.7.0.jar!/:0.7.0]
at reactor.core.publisher.MonoCallable.call(MonoCallable.java:72) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:228) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.7.3.jar!/:3.7.3]
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.7.3.jar!/:3.7.3]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]
2025-03-17T17:50:44.250+08:00 INFO 45324 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Starting McpDemoApplication v0.0.1-SNAPSHOT using Java 17.0.10 with PID 45324 (D:\WorkSpace\aikrai\vertx-pj\vertx-demo\src\main\resources\mcp-demo-0.0.1-SNAPSHOT.jar started by AiKrai in D:\WorkSpace\aikrai\vertx-pj)
2025-03-17T17:50:44.252+08:00 INFO 45324 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-17T17:50:44.983+08:00 INFO 45324 --- [mcp-server-weather] [main] com.demo.service.WeatherService : 天气 API Key 配置验证通过
2025-03-17T17:50:45.286+08:00 INFO 45324 --- [mcp-server-weather] [main] o.s.a.a.m.s.MpcServerAutoConfiguration : Registered tools1 notification: true
2025-03-17T17:50:45.340+08:00 INFO 45324 --- [mcp-server-weather] [pool-2-thread-1] i.m.server.McpAsyncServer : Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=RootCapabilities[listChanged=false], sampling=null], Info: Implementation[name=langchain4j, version=1.0]
2025-03-17T17:50:45.467+08:00 INFO 45324 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Started McpDemoApplication in 1.707 seconds (process running for 2.175)
2025-03-17T17:50:45.470+08:00 INFO 45324 --- [mcp-server-weather] [main] com.demo.McpDemoApplication : Server running
2025-03-17T17:50:48.377+08:00 INFO 45324 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 开始获取天气,城市:鍖椾含
2025-03-17T17:50:48.743+08:00 INFO 45324 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市搜索接口返回结果:{"code":"200","location":[{"name":"Актобе","id":"477C6","lat":"50.30000","lon":"57.16667","adm2":"Актобе","adm1":"Актюбинская область","country":"Казахстан","tz":"Asia/Aqtobe","utcOffset":"+05:00","isDst":"0","type":"city","rank":"31","fxLink":"https://www.qweather.com/weather/aktobe-477C6.html"},{"name":"Abéché","id":"53314","lat":"13.83306","lon":"20.83472","adm2":"Abéché","adm1":"Ouadda?","country":"Tchad","tz":"Africa/Ndjamena","utcOffset":"+01:00","isDst":"0","type":"city","rank":"41","fxLink":"https://www.qweather.com/weather/abéché-53314.html"},{"name":"Abánades","id":"446FB","lat":"40.89400","lon":"-2.48500","adm2":"Abánades","adm1":"Castilla-La Mancha","country":"Espa?a","tz":"Europe/Madrid","utcOffset":"+01:00","isDst":"0","type":"city","rank":"82","fxLink":"https://www.qweather.com/weather/abánades-446FB.html"},{"name":"??? ???","id":"8EFB5","lat":"24.46900","lon":"54.35800","adm2":"??? ???","adm1":"??????","country":"???????? ??????? ???????","tz":"Asia/Dubai","utcOffset":"+04:00","isDst":"0","type":"city","rank":"30","fxLink":"https://www.qweather.com/weather/abu-dhabi-8EFB5.html"},{"name":"Be?k","id":"3BBFF","lat":"50.13100","lon":"18.71000","adm2":"Be?k","adm1":"?l?skie","country":"Polska","tz":"Europe/Warsaw","utcOffset":"+01:00","isDst":"0","type":"city","rank":"85","fxLink":"https://www.qweather.com/weather/be?k-3BBFF.html"},{"name":"Fenoarivo Be","id":"46390","lat":"-18.43300","lon":"46.56700","adm2":"Fenoarivo Be","adm1":"Antananarivo","country":"Madagascar","tz":"Indian/Antananarivo","utcOffset":"+03:00","isDst":"0","type":"city","rank":"600","fxLink":"https://www.qweather.com/weather/fenoarivo-be-46390.html"},{"name":"Be?tepe","id":"88E0","lat":"45.08333","lon":"29.01667","adm2":"Be?tepe","adm1":"Tulcea County","country":"Romania","tz":"Europe/Bucharest","utcOffset":"+02:00","isDst":"0","type":"city","rank":"85","fxLink":"https://www.qweather.com/weather/be?tepe-88E0.html"},{"name":"D?be Wielkie","id":"80898","lat":"52.20000","lon":"21.42100","adm2":"D?be Wielkie","adm1":"Mazowieckie","country":"Polska","tz":"Europe/Warsaw","utcOffset":"+01:00","isDst":"0","type":"city","rank":"85","fxLink":"https://www.qweather.com/weather/d?be-wielkie-80898.html"},{"name":"神戸市","id":"27C3F","lat":"34.69200","lon":"135.18800","adm2":"神戸市","adm1":"兵庫県","country":"日本","tz":"Asia/Tokyo","utcOffset":"+09:00","isDst":"0","type":"city","rank":"21","fxLink":"https://www.qweather.com/weather/kobe-shi-27C3F.html"},{"name":"C?be?ti","id":"C2AB3","lat":"46.76670","lon":"22.36670","adm2":"C?be?ti","adm1":"Bihor County","country":"Romania","tz":"Europe/Bucharest","utcOffset":"+02:00","isDst":"0","type":"city","rank":"85","fxLink":"https://www.qweather.com/weather/c?be?ti-C2AB3.html"}],"refer":{"sources":["QWeather"],"license":["QWeather Developers License"]}}
2025-03-17T17:50:48.745+08:00 INFO 45324 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 城市的locationId为477C6
2025-03-17T17:50:48.897+08:00 INFO 45324 --- [mcp-server-weather] [boundedElastic-1] com.demo.service.WeatherService : 天气接口返回结果:{"code":"200","updateTime":"2025-03-17T14:45+05:00","fxLink":"https://www.qweather.com/weather/aktobe-477C6.html","now":{"obsTime":"2025-03-17T14:26+05:00","temp":"9","feelsLike":"3","icon":"100","text":"晴","wind360":"225","windDir":"西南风","windScale":"4","windSpeed":"28","humidity":"61","precip":"0.0","pressure":"1008","vis":"16","cloud":"0","dew":"2"},"refer":{"sources":["QWeather"],"license":["CC BY-SA 4.0"]}}

View File

@ -0,0 +1,63 @@
package app.util.sql
import org.aikrai.vertx.gen.SqlGen
import kotlin.reflect.KClass
/**
* SqlGen工具类使用示例
*/
object SqlGenExample {
/**
* 示例方法
*/
@JvmStatic
fun main(args: Array<String>) {
// 指定要扫描的包路径,例如"app.data.domain"
val packageName = "app.data.domain"
// // 方式一一步生成所有SQL
// val sqlMap = SqlGen.generateSql(packageName)
//
// // 打印生成的SQL
// println("=== 生成的SQL语句 ===")
// sqlMap.forEach { (key: String, sql: String) ->
// println("\n--- $key ---")
// println(sql)
// }
// 方式二:分步骤生成
println("\n\n=== 分步骤生成SQL ===")
val sqlGen = SqlGen()
// 1. 扫描实体类
println("1. 开始扫描实体类...")
val entities = sqlGen.scanEntities(packageName)
println("发现 ${entities.size} 个实体类:")
entities.forEach { entity: KClass<*> ->
println("- ${entity.simpleName}")
}
// 2. 解析实体类信息到缓存
println("\n2. 解析实体类信息...")
sqlGen.parseEntities(entities)
// 3. 生成建表SQL
println("\n3. 生成建表SQL...")
val createTableSql = sqlGen.generateCreateTableSql()
createTableSql.forEach { sql: String ->
println(sql)
}
// 4. 生成CRUD SQL
println("\n4. 生成CRUD SQL...")
val crudSql = sqlGen.generateCrudSql()
crudSql.forEach { (entityClass: KClass<*>, crud: SqlGen.CrudSql) ->
println("\n--- ${entityClass.simpleName} ---")
println("INSERT: ${crud.insertSql}")
println("SELECT: ${crud.selectByIdSql}")
println("UPDATE: ${crud.updateSql}")
println("DELETE: ${crud.deleteSql}")
}
}
}

View File

@ -0,0 +1,239 @@
package org.aikrai.vertx.db.annotation
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.memberProperties
/**
* SQL注解映射中间类
* 用于从实体类及其注解中提取信息以生成SQL
* 支持多套注解系统的映射配置
*/
class SqlAnnotationMapper {
// 表名注解配置
var tableNameAnnotationClass: KClass<out Annotation> = TableName::class
var tableNameProperty: String = "value"
var tableNameDefaultMapper: (KClass<*>) -> String = { klass -> camelToSnake(klass.simpleName!!) }
// 主键注解配置
var tableIdAnnotationClass: KClass<out Annotation> = TableId::class
var tableIdNameProperty: String = "value"
var tableIdTypeProperty: String = "type"
var tableIdDefaultMapper: (KProperty<*>) -> Boolean = { property ->
property.name == "id" || property.name.endsWith("Id")
}
// 字段注解配置
var tableFieldAnnotationClass: KClass<out Annotation> = TableField::class
var tableFieldNameProperty: String = "value"
var tableFieldFillProperty: String = "fill"
var tableFieldDefaultMapper: (KProperty<*>) -> String = { property -> camelToSnake(property.name) }
// 索引注解配置
var tableIndexAnnotationClass: KClass<out Annotation> = TableIndex::class
var tableIndexNameProperty: String = "name"
var tableIndexUniqueProperty: String = "unique"
var tableIndexColumnsProperty: String = "columnNames"
// 枚举值注解配置
var enumValueAnnotationClass: KClass<out Annotation> = EnumValue::class
/**
* 从实体类中获取表名
* @param entityClass 实体类
* @return 表名
*/
fun getTableName(entityClass: KClass<*>): String {
// 获取所有注解
val annotations = entityClass.annotations.filter { it.annotationClass == tableNameAnnotationClass }
return if (annotations.isNotEmpty()) {
val annotation = annotations.first()
// 通过反射获取注解的value属性值
val method = annotation::class.java.getMethod(tableNameProperty)
val value = method.invoke(annotation) as String
if (value.isNotEmpty()) value else tableNameDefaultMapper(entityClass)
} else {
tableNameDefaultMapper(entityClass)
}
}
/**
* 判断属性是否为主键
* @param property 属性
* @return 是否为主键
*/
fun isTableId(property: KProperty<*>): Boolean {
val hasAnnotation = property.annotations.any { it.annotationClass == tableIdAnnotationClass }
return hasAnnotation || tableIdDefaultMapper(property)
}
/**
* 获取属性对应的列名
* @param property 属性
* @return 列名
*/
fun getColumnName(property: KProperty<*>): String {
val annotations = property.annotations.filter { it.annotationClass == tableFieldAnnotationClass }
return if (annotations.isNotEmpty()) {
val annotation = annotations.first()
// 通过反射获取注解的value属性值
val method = annotation::class.java.getMethod(tableFieldNameProperty)
val value = method.invoke(annotation) as String
if (value.isNotEmpty()) value else tableFieldDefaultMapper(property)
} else {
tableFieldDefaultMapper(property)
}
}
/**
* 获取主键类型
* @param property 属性
* @return 主键类型如果有
*/
fun getIdType(property: KProperty<*>): Any? {
val annotations = property.annotations.filter { it.annotationClass == tableIdAnnotationClass }
if (annotations.isEmpty()) return null
val annotation = annotations.first()
// 通过反射获取注解的type属性值
val method = annotation::class.java.getMethod(tableIdTypeProperty)
return method.invoke(annotation)
}
/**
* 获取字段填充策略
* @param property 属性
* @return 填充策略如果有
*/
fun getFieldFill(property: KProperty<*>): Any? {
val annotations = property.annotations.filter { it.annotationClass == tableFieldAnnotationClass }
if (annotations.isEmpty()) return null
val annotation = annotations.first()
// 通过反射获取注解的fill属性值
val method = annotation::class.java.getMethod(tableFieldFillProperty)
return method.invoke(annotation)
}
/**
* 获取实体类的所有索引配置
* @param entityClass 实体类
* @return 索引配置列表
*/
fun getTableIndices(entityClass: KClass<*>): List<TableIndexInfo> {
val annotations = entityClass.annotations.filter { it.annotationClass == tableIndexAnnotationClass }
return annotations.map { annotation ->
val nameMethod = annotation::class.java.getMethod(tableIndexNameProperty)
val uniqueMethod = annotation::class.java.getMethod(tableIndexUniqueProperty)
val columnsMethod = annotation::class.java.getMethod(tableIndexColumnsProperty)
val name = nameMethod.invoke(annotation) as String
val unique = uniqueMethod.invoke(annotation) as Boolean
val columns = columnsMethod.invoke(annotation) as Array<*>
TableIndexInfo(
name = name,
unique = unique,
columnNames = columns.map { it.toString() }
)
}
}
/**
* 获取实体类的所有属性信息
* @param entityClass 实体类
* @return 属性信息列表
*/
fun getEntityProperties(entityClass: KClass<*>): List<PropertyInfo> {
return entityClass.memberProperties.map { property ->
PropertyInfo(
property = property,
columnName = getColumnName(property),
isPrimary = isTableId(property),
idType = getIdType(property),
fieldFill = getFieldFill(property)
)
}
}
/**
* 从实体类中提取所有SQL相关信息
* @param entityClass 实体类
* @return 实体类SQL信息
*/
fun extractEntityInfo(entityClass: KClass<*>): EntityInfo {
return EntityInfo(
entityClass = entityClass,
tableName = getTableName(entityClass),
properties = getEntityProperties(entityClass),
indices = getTableIndices(entityClass)
)
}
/**
* 驼峰命名转下划线命名
* @param name 驼峰命名
* @return 下划线命名
*/
private fun camelToSnake(name: String): String {
return name.replace(Regex("([a-z0-9])([A-Z])"), "$1_$2").lowercase()
}
/**
* 表索引信息
*/
data class TableIndexInfo(
val name: String,
val unique: Boolean,
val columnNames: List<String>
)
/**
* 属性信息
*/
data class PropertyInfo(
val property: KProperty<*>,
val columnName: String,
val isPrimary: Boolean,
val idType: Any?,
val fieldFill: Any?
)
/**
* 实体类SQL信息
*/
data class EntityInfo(
val entityClass: KClass<*>,
val tableName: String,
val properties: List<PropertyInfo>,
val indices: List<TableIndexInfo>
)
companion object {
/**
* 创建默认配置的SQL注解映射器
* @return SQL注解映射器
*/
fun createDefault(): SqlAnnotationMapper {
return SqlAnnotationMapper()
}
/**
* 创建自定义配置的SQL注解映射器
* @param configure 配置函数
* @return SQL注解映射器
*/
fun create(configure: SqlAnnotationMapper.() -> Unit): SqlAnnotationMapper {
val mapper = SqlAnnotationMapper()
mapper.configure()
return mapper
}
}
}

View File

@ -0,0 +1,152 @@
package org.aikrai.vertx.db.annotation
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
/**
* SqlAnnotationMapper 使用示例
*/
class SqlAnnotationMapperExample {
/**
* 使用默认配置的中间类示例
*/
fun defaultMapperExample(entityClass: KClass<*>) {
// 创建默认配置的SQL注解映射器
val mapper = SqlAnnotationMapper.createDefault()
// 从实体类中提取信息
val entityInfo = mapper.extractEntityInfo(entityClass)
// 输出实体信息
println("表名: ${entityInfo.tableName}")
println("字段:")
entityInfo.properties.forEach { prop ->
println(" ${prop.columnName} - 主键: ${prop.isPrimary} - 类型: ${prop.property.returnType}")
if (prop.idType != null) {
println(" ID类型: ${prop.idType}")
}
if (prop.fieldFill != null) {
println(" 填充策略: ${prop.fieldFill}")
}
}
println("索引:")
entityInfo.indices.forEach { index ->
println(" ${index.name} - 唯一: ${index.unique} - 列: ${index.columnNames.joinToString(", ")}")
}
}
/**
* 配置自定义注解映射示例
*/
fun customMapperExample(entityClass: KClass<*>) {
// 创建自定义配置的SQL注解映射器
val mapper = SqlAnnotationMapper.create {
// 假设我们使用了另一套注解系统例如JPA注解
// 配置表名注解 - 使用JPA的@Table注解
tableNameAnnotationClass = Table::class
tableNameProperty = "name"
// 配置主键注解 - 使用JPA的@Id注解
tableIdAnnotationClass = Id::class
// 配置字段注解 - 使用JPA的@Column注解
tableFieldAnnotationClass = Column::class
tableFieldNameProperty = "name"
// 自定义名称转换规则
tableNameDefaultMapper = { kClass -> kClass.simpleName!!.lowercase() }
tableFieldDefaultMapper = { property -> property.name.lowercase() }
}
// 从实体类中提取信息
val entityInfo = mapper.extractEntityInfo(entityClass)
// 输出实体信息
println("表名: ${entityInfo.tableName}")
println("字段:")
entityInfo.properties.forEach { prop ->
println(" ${prop.columnName} - 主键: ${prop.isPrimary}")
}
}
/**
* SQL生成器示例展示如何根据提取的信息生成SQL
*/
fun sqlGeneratorExample(entityClass: KClass<*>) {
// 创建SQL注解映射器
val mapper = SqlAnnotationMapper.createDefault()
// 提取实体信息
val entityInfo = mapper.extractEntityInfo(entityClass)
// 生成建表SQL
val createTableSql = generateCreateTableSql(entityInfo)
println("建表SQL:")
println(createTableSql)
}
/**
* 生成建表SQL
*/
private fun generateCreateTableSql(entityInfo: SqlAnnotationMapper.EntityInfo): String {
val sb = StringBuilder()
sb.append("CREATE TABLE IF NOT EXISTS ${entityInfo.tableName} (\n")
// 添加字段定义
val columns = entityInfo.properties.map { prop ->
val primaryKey = if (prop.isPrimary) " PRIMARY KEY" else ""
" ${prop.columnName} ${mapTypeToPostgresType(prop)} ${primaryKey}"
}
sb.append(columns.joinToString(",\n"))
sb.append("\n);")
// 添加索引
entityInfo.indices.forEach { index ->
val unique = if (index.unique) "UNIQUE " else ""
sb.append("\n\nCREATE ${unique}INDEX IF NOT EXISTS ${index.name} ON ${entityInfo.tableName} (${index.columnNames.joinToString(", ")});")
}
return sb.toString()
}
/**
* 将属性类型映射为PostgreSQL类型简化示例
*/
private fun mapTypeToPostgresType(property: SqlAnnotationMapper.PropertyInfo): String {
val typeName = property.property.returnType.toString()
return when {
typeName.contains("String") -> "VARCHAR(255)"
typeName.contains("Int") -> "INTEGER"
typeName.contains("Long") -> "BIGINT"
typeName.contains("Boolean") -> "BOOLEAN"
typeName.contains("Double") -> "DOUBLE PRECISION"
typeName.contains("Float") -> "REAL"
typeName.contains("java.sql.Timestamp") || typeName.contains("java.util.Date") -> "TIMESTAMP"
typeName.contains("LocalDateTime") -> "TIMESTAMP"
typeName.contains("LocalDate") -> "DATE"
typeName.contains("Char") -> "CHAR(1)"
else -> "JSONB" // 默认复杂类型存储为JSON
}
}
}
// JPA注解模拟 - 仅用于示例
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class Table(val name: String = "")
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class Id
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class Column(val name: String = "")

View File

@ -0,0 +1,153 @@
package org.aikrai.vertx.gen
import java.io.File
import kotlin.system.exitProcess
/**
* SqlGen命令行工具
*
* 用法
* SqlGenCli <packageName> <outputDir>
*
* 参数:
* packageName - 要扫描的包名例如 app.data.domain
* outputDir - SQL文件输出目录可选默认为 ./sql-gen
*/
object SqlGenCli {
@JvmStatic
fun main(args: Array<String>) {
if (args.isEmpty() || args[0] == "-h" || args[0] == "--help") {
printHelp()
exitProcess(0)
}
val packageName = args[0]
val outputDir = if (args.size > 1) args[1] else "./sql-gen"
println("开始从包 $packageName 生成SQL...")
println("输出目录: $outputDir")
// 创建输出目录
val outputDirFile = File(outputDir)
if (!outputDirFile.exists()) {
outputDirFile.mkdirs()
println("创建输出目录: $outputDir")
}
try {
// 生成SQL
val sqlMap: Map<String, String> = SqlGen.generateSql(packageName)
// 按类型组织SQL
val createTableSql = mutableListOf<String>()
val insertSql = mutableListOf<String>()
val selectSql = mutableListOf<String>()
val updateSql = mutableListOf<String>()
val deleteSql = mutableListOf<String>()
// 分类SQL语句
sqlMap.forEach { (key: String, sql: String) ->
when {
key.endsWith("-create") -> createTableSql.add(sql)
key.endsWith("-insert") -> insertSql.add(sql)
key.endsWith("-select") -> selectSql.add(sql)
key.endsWith("-update") -> updateSql.add(sql)
key.endsWith("-delete") -> deleteSql.add(sql)
else -> {} // 忽略其他类型
}
}
// 写入各类SQL文件
File(outputDirFile, "tables.sql").writeText(createTableSql.joinToString("\n\n"))
File(outputDirFile, "inserts.sql").writeText(insertSql.joinToString("\n\n"))
File(outputDirFile, "selects.sql").writeText(selectSql.joinToString("\n\n"))
File(outputDirFile, "updates.sql").writeText(updateSql.joinToString("\n\n"))
File(outputDirFile, "deletes.sql").writeText(deleteSql.joinToString("\n\n"))
// 写入完整SQL文件
val allSql = """
|-- ====================
|-- 建表语句
|-- ====================
|
|${createTableSql.joinToString("\n\n")}
|
|-- ====================
|-- 插入语句
|-- ====================
|
|${insertSql.joinToString("\n\n")}
|
|-- ====================
|-- 查询语句
|-- ====================
|
|${selectSql.joinToString("\n\n")}
|
|-- ====================
|-- 更新语句
|-- ====================
|
|${updateSql.joinToString("\n\n")}
|
|-- ====================
|-- 删除语句
|-- ====================
|
|${deleteSql.joinToString("\n\n")}
""".trimMargin()
File(outputDirFile, "all.sql").writeText(allSql)
// 为每个实体类生成单独的SQL文件
val tableNames = sqlMap.keys
.filter { k: String -> k.endsWith("-create") }
.map { k: String -> k.substring(0, k.length - 7) }
tableNames.forEach { tableName: String ->
val entitySql = """
|-- 表结构
|${sqlMap["$tableName-create"] ?: ""}
|
|-- 插入操作
|${sqlMap["$tableName-insert"] ?: ""}
|
|-- 查询操作
|${sqlMap["$tableName-select"] ?: ""}
|
|-- 更新操作
|${sqlMap["$tableName-update"] ?: ""}
|
|-- 删除操作
|${sqlMap["$tableName-delete"] ?: ""}
""".trimMargin()
File(outputDirFile, "$tableName.sql").writeText(entitySql)
}
println("SQL生成完成")
println("成功生成 ${tableNames.size} 个实体类的SQL脚本到目录: $outputDir")
} catch (e: Exception) {
System.err.println("生成SQL时发生错误: ${e.message}")
e.printStackTrace()
exitProcess(1)
}
}
private fun printHelp() {
println("""
|SqlGen - Kotlin实体类SQL生成工具
|
|用法: SqlGenCli <packageName> [outputDir]
|
|参数:
| packageName 要扫描的包名例如 app.data.domain
| outputDir SQL文件输出目录可选默认为 ./sql-gen
|
|示例:
| SqlGenCli app.data.domain ./sql/generated
""".trimMargin())
}
}