info
This commit is contained in:
95
README.md
Normal file
95
README.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# ydoyun-report-mcp
|
||||||
|
|
||||||
|
一个基于 Spring Boot 和 MCP (Model Context Protocol) 的报表服务项目,用于将报表存储过程调用能力暴露给 AI 模型。
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- Java 17
|
||||||
|
- Spring Boot 3.2.6
|
||||||
|
- Spring AI MCP Server WebMVC 1.0.0
|
||||||
|
- Maven
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ydoyun-report-mcp/
|
||||||
|
├── pom.xml
|
||||||
|
├── src/
|
||||||
|
│ ├── main/
|
||||||
|
│ │ ├── java/
|
||||||
|
│ │ │ └── com/junchi/app/mcp/
|
||||||
|
│ │ │ ├── YdoyunReportMcpApplication.java # 主应用类
|
||||||
|
│ │ │ ├── config/ # 配置类
|
||||||
|
│ │ │ │ ├── McpToolConfig.java # MCP 工具配置
|
||||||
|
│ │ │ │ └── RestTemplateConfig.java # RestTemplate 配置
|
||||||
|
│ │ │ ├── mcptool/ # MCP 工具服务
|
||||||
|
│ │ │ │ └── ReportMcpToolsService.java # 报表 MCP 工具服务
|
||||||
|
│ │ │ └── report/ # 报表服务
|
||||||
|
│ │ │ ├── ReportService.java # 报表服务实现
|
||||||
|
│ │ │ └── vo/ # 值对象
|
||||||
|
│ │ │ ├── ProcedureRequestVO.java # 存储过程请求 VO
|
||||||
|
│ │ │ └── ProcedureResponseVO.java # 存储过程响应 VO
|
||||||
|
│ │ └── resources/
|
||||||
|
│ │ └── application.yml # 应用配置文件
|
||||||
|
│ └── test/ # 测试代码目录
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能说明
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
|
||||||
|
1. **MCP 工具服务**: 提供 `executeReportProcedure` 工具方法,供 AI 模型调用
|
||||||
|
2. **存储过程调用**: 通过 HTTP 调用远程存储过程 API
|
||||||
|
3. **日期处理**: 支持 `today`、`yesterday`、`tomorrow` 关键字或具体日期格式(yyyy-MM-dd)
|
||||||
|
|
||||||
|
### 配置说明
|
||||||
|
|
||||||
|
在 `application.yml` 中配置:
|
||||||
|
|
||||||
|
- **服务端口**: 48090
|
||||||
|
- **MCP 服务器**: 异步类型,仅启用 tools 能力
|
||||||
|
- **远程 API**: 配置存储过程执行接口地址和 API 密钥
|
||||||
|
|
||||||
|
## 构建和运行
|
||||||
|
|
||||||
|
### 构建项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean package
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
或者运行打包后的 jar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -jar target/ydoyun-report-mcp-1.0.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 说明
|
||||||
|
|
||||||
|
### MCP 工具
|
||||||
|
|
||||||
|
**executeReportProcedure**
|
||||||
|
|
||||||
|
根据存储过程名称、日期和仓库代码调用后端存储过程接口。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- `name` (String): 存储过程名称
|
||||||
|
- `rq` (String): 日期,支持 `today`/`yesterday`/`tomorrow` 或 `2025-12-01` 格式
|
||||||
|
- `ckdm` (String): 仓库代码
|
||||||
|
- `p` (String): 秘钥
|
||||||
|
|
||||||
|
返回:执行结果的字符串表示
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 确保远程 API 服务可访问
|
||||||
|
2. 配置正确的 API 密钥
|
||||||
|
3. 根据实际需求调整日志级别
|
||||||
|
|
||||||
105
pom.xml
Normal file
105
pom.xml
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.junchi.app</groupId>
|
||||||
|
<artifactId>ydoyun-report-mcp</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>ydoyun-report-mcp</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<spring.boot.version>3.2.6</spring.boot.version>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.6</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot Web 启动器:提供 Web 服务能力 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot 测试启动器:提供测试框架支持 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Actuator:提供应用监控和管理端点 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok:简化 Java 代码,自动生成 getter/setter 等方法 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring AI MCP Server WebMVC 启动器:提供 MCP 服务器能力 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Logstash Logback 编码器:用于日志格式化,便于日志收集和分析 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.logstash.logback</groupId>
|
||||||
|
<artifactId>logstash-logback-encoder</artifactId>
|
||||||
|
<version>7.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Spring Boot Starter:提供 MyBatis 与 Spring Boot 集成 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<version>3.0.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL 驱动:用于连接 MySQL 数据库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- Spring Boot Maven 插件:用于打包可执行 jar 文件 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Maven 编译插件:配置 Java 编译版本和编码 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>17</source>
|
||||||
|
<target>17</target>
|
||||||
|
<encoding>${encoding}</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.junchi.app.mcp;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 云朵云报表 MCP 服务主应用类
|
||||||
|
*
|
||||||
|
* <p>这是一个基于 Spring Boot 和 MCP (Model Context Protocol) 的报表服务应用,
|
||||||
|
* 用于将报表存储过程调用能力暴露给 AI 模型。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan("com.junchi.app.mcp.dal.mapper")
|
||||||
|
public class YdoyunReportMcpApplication {
|
||||||
|
/**
|
||||||
|
* 应用程序入口方法
|
||||||
|
*
|
||||||
|
* @param args 命令行参数
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(YdoyunReportMcpApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
src/main/java/com/junchi/app/mcp/config/McpToolConfig.java
Normal file
35
src/main/java/com/junchi/app/mcp/config/McpToolConfig.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package com.junchi.app.mcp.config;
|
||||||
|
|
||||||
|
import com.junchi.app.mcp.mcptool.ReportMcpToolsService;
|
||||||
|
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||||
|
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 工具配置类
|
||||||
|
*
|
||||||
|
* <p>用于注册 MCP 工具回调提供者,将报表工具服务暴露给 AI 模型使用。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class McpToolConfig {
|
||||||
|
/**
|
||||||
|
* 创建工具回调提供者
|
||||||
|
*
|
||||||
|
* <p>将 ReportMcpToolsService 中定义的工具方法注册到 MCP 服务器,
|
||||||
|
* 使得 AI 模型可以通过 MCP 协议调用这些工具。</p>
|
||||||
|
*
|
||||||
|
* @param reportMcpToolsService 报表 MCP 工具服务实例
|
||||||
|
* @return 工具回调提供者
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public ToolCallbackProvider userTools(ReportMcpToolsService reportMcpToolsService) {
|
||||||
|
return MethodToolCallbackProvider.builder()
|
||||||
|
.toolObjects(new Object[]{reportMcpToolsService})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.junchi.app.mcp.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RestTemplate 配置类
|
||||||
|
*
|
||||||
|
* <p>用于配置 HTTP 客户端 RestTemplate,用于调用远程存储过程 API。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RestTemplateConfig {
|
||||||
|
/**
|
||||||
|
* 创建 RestTemplate Bean
|
||||||
|
*
|
||||||
|
* <p>用于执行 HTTP 请求,调用远程存储过程接口。</p>
|
||||||
|
*
|
||||||
|
* @return RestTemplate 实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RestTemplate restTemplate() {
|
||||||
|
return new RestTemplate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.junchi.app.mcp.dal.dataobject;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报表数据源 DO
|
||||||
|
*
|
||||||
|
* <p>用于封装报表数据源信息,对应数据库表 ydoyun_report_database。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportDatabaseDO {
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源名称
|
||||||
|
*/
|
||||||
|
private String dbName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库类型(mysql、pgsql、oracle、sqlserver)
|
||||||
|
*/
|
||||||
|
private String dbType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库地址
|
||||||
|
*/
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库端口
|
||||||
|
*/
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库账号
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库密码(建议加密存储)
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库名称
|
||||||
|
*/
|
||||||
|
private String databaseName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注说明
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private String creator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private String updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
private Boolean deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户编号
|
||||||
|
*/
|
||||||
|
private Long tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.junchi.app.mcp.dal.mapper;
|
||||||
|
|
||||||
|
import com.junchi.app.mcp.dal.dataobject.ReportDatabaseDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报表数据源 Mapper 接口
|
||||||
|
*
|
||||||
|
* <p>用于查询报表数据源信息。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface ReportDatabaseMapper {
|
||||||
|
/**
|
||||||
|
* 根据 report_id 查询绑定的数据源信息
|
||||||
|
*
|
||||||
|
* @param reportId 报表ID
|
||||||
|
* @return 数据源信息
|
||||||
|
*/
|
||||||
|
ReportDatabaseDO selectReportDatabaseByReportId(@Param("reportId") Long reportId);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package com.junchi.app.mcp.mcptool;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.junchi.app.mcp.report.ReportService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.ai.tool.annotation.Tool;
|
||||||
|
import org.springframework.ai.tool.annotation.ToolParam;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报表 MCP 工具服务类
|
||||||
|
*
|
||||||
|
* <p>提供 MCP 工具方法,供 AI 模型调用执行报表存储过程。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ReportMcpToolsService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ReportMcpToolsService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReportService reportService;
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行报表存储过程
|
||||||
|
*/
|
||||||
|
@Tool(
|
||||||
|
description = "根据【报表ID(reportId) + 存储过程名称(name)】执行对应的数据库存储过程,"
|
||||||
|
+ "并按 params 中提供的动态参数查询报表数据。"
|
||||||
|
+ "该工具用于获取指定报表在指定条件下的原始数据结果,"
|
||||||
|
+ "不做统计、不做分析,只负责数据查询。"
|
||||||
|
)
|
||||||
|
public String executeReportProcedure(
|
||||||
|
@ToolParam(description = "报表ID,用于确定使用哪一个报表配置及其数据库连接")
|
||||||
|
Long reportId,
|
||||||
|
@ToolParam(description = "存储过程名称,对应数据库中的具体存储过程名")
|
||||||
|
String name,
|
||||||
|
@ToolParam(
|
||||||
|
description = "存储过程参数 JSON 字符串,"
|
||||||
|
+ "示例:{\"rq\":\"2025-12-15\",\"ckdm\":\"001\"}"
|
||||||
|
)
|
||||||
|
String params
|
||||||
|
) {
|
||||||
|
LinkedHashMap<String, Object> paramMap;
|
||||||
|
try {
|
||||||
|
if (params == null || params.isBlank()) {
|
||||||
|
paramMap = new LinkedHashMap<>();
|
||||||
|
} else {
|
||||||
|
paramMap = objectMapper.readValue(
|
||||||
|
params,
|
||||||
|
new TypeReference<LinkedHashMap<String, Object>>() {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("存储过程参数 params JSON 解析失败:" + params, e);
|
||||||
|
}
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// ===== MCP 调用开始 =====
|
||||||
|
log.info("[MCP] BEGIN executeReportProcedure, reportId={}, name={}", reportId, name);
|
||||||
|
log.debug("[MCP] params={}", paramMap);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 参数校验
|
||||||
|
if (name == null || name.isBlank()) {
|
||||||
|
log.warn("[MCP] 参数校验失败:name 为空");
|
||||||
|
throw new IllegalArgumentException("参数 name(存储过程名称) 不能为空");
|
||||||
|
}
|
||||||
|
if (reportId == null) {
|
||||||
|
log.warn("[MCP] 参数校验失败:reportId 为空");
|
||||||
|
throw new IllegalArgumentException("参数 reportId(报表ID) 不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行存储过程
|
||||||
|
Object result = reportService.executeProcedure(reportId, name, paramMap);
|
||||||
|
String resultStr = result == null ? "" : result.toString();
|
||||||
|
|
||||||
|
long cost = System.currentTimeMillis() - startTime;
|
||||||
|
|
||||||
|
log.info("[MCP] END executeReportProcedure success, cost={}ms, resultLength={}",
|
||||||
|
cost, resultStr.length());
|
||||||
|
|
||||||
|
log.debug("[MCP] result={}", resultStr);
|
||||||
|
|
||||||
|
return resultStr;
|
||||||
|
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
long cost = System.currentTimeMillis() - startTime;
|
||||||
|
log.warn("[MCP] PARAM ERROR, cost={}ms, msg={}", cost, e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
long cost = System.currentTimeMillis() - startTime;
|
||||||
|
log.error("[MCP] ERROR executeReportProcedure, cost={}ms", cost, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
258
src/main/java/com/junchi/app/mcp/report/ReportService.java
Normal file
258
src/main/java/com/junchi/app/mcp/report/ReportService.java
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
package com.junchi.app.mcp.report;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.junchi.app.mcp.dal.dataobject.ReportDatabaseDO;
|
||||||
|
import com.junchi.app.mcp.dal.mapper.ReportDatabaseMapper;
|
||||||
|
import com.junchi.app.mcp.report.vo.ProcedureRequestVO;
|
||||||
|
import com.junchi.app.mcp.report.vo.ReportDatabaseRespVO;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报表服务类
|
||||||
|
*
|
||||||
|
* <p>负责调用远程存储过程 API,处理日期参数解析和响应数据格式化。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class ReportService {
|
||||||
|
/** 日志记录器 */
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ReportService.class);
|
||||||
|
|
||||||
|
/** 日期格式化器,用于将日期格式化为 yyyy-MM-dd 格式 */
|
||||||
|
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
|
/** HTTP 客户端,用于调用远程 API */
|
||||||
|
private final RestTemplate restTemplate;
|
||||||
|
|
||||||
|
/** 报表数据源 Mapper */
|
||||||
|
@Autowired
|
||||||
|
private ReportDatabaseMapper reportDatabaseMapper;
|
||||||
|
|
||||||
|
/** 远程存储过程执行接口地址,从配置文件读取 */
|
||||||
|
@Value("${ydoyun.mssqljdbc.report.api.execute-url}")
|
||||||
|
private String executeUrl;
|
||||||
|
|
||||||
|
/** API 密钥,从配置文件读取 */
|
||||||
|
@Value("${ydoyun.mssqljdbc.report.api.key}")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param restTemplate HTTP 客户端实例
|
||||||
|
*/
|
||||||
|
public ReportService(RestTemplate restTemplate) {
|
||||||
|
this.restTemplate = restTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期关键字或日期字符串
|
||||||
|
*
|
||||||
|
* <p>将日期关键字(today/yesterday/tomorrow)转换为具体的日期格式(yyyy-MM-dd),
|
||||||
|
* 如果输入已经是日期格式,则直接返回。</p>
|
||||||
|
*
|
||||||
|
* @param dateKeyword 日期关键字(today/yesterday/tomorrow)或日期字符串(yyyy-MM-dd)
|
||||||
|
* @return 格式化后的日期字符串(yyyy-MM-dd 格式)
|
||||||
|
*/
|
||||||
|
private String resolveDate(String dateKeyword) {
|
||||||
|
LocalDate date = LocalDate.now();
|
||||||
|
switch (dateKeyword.toLowerCase()) {
|
||||||
|
case "yesterday":
|
||||||
|
// 昨天:当前日期减一天
|
||||||
|
date = date.minusDays(1);
|
||||||
|
break;
|
||||||
|
case "today":
|
||||||
|
// 今天:使用当前日期
|
||||||
|
break;
|
||||||
|
case "tomorrow":
|
||||||
|
// 明天:当前日期加一天
|
||||||
|
date = date.plusDays(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 如果不是关键字,直接返回原值(假设是日期格式)
|
||||||
|
return dateKeyword;
|
||||||
|
}
|
||||||
|
return date.format(FORMATTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行存储过程
|
||||||
|
*
|
||||||
|
* <p>调用远程存储过程 API,执行指定的存储过程并返回结果。
|
||||||
|
* 该方法会处理日期参数解析、构建请求、发送 HTTP 请求、解析响应等步骤。</p>
|
||||||
|
*
|
||||||
|
* @param name 存储过程名称
|
||||||
|
* @param rq 日期(支持 today/yesterday/tomorrow 或 yyyy-MM-dd 格式)
|
||||||
|
* @param ckdm 仓库代码
|
||||||
|
* @param reportId 报表ID
|
||||||
|
* @param p 秘钥
|
||||||
|
* @return 执行结果,可能是 List、Map、String 或其他对象类型
|
||||||
|
* @throws RuntimeException 当远程接口返回为空、调用失败或解析失败时抛出
|
||||||
|
*/
|
||||||
|
public Object executeProcedure(Long reportId, String name,LinkedHashMap<String, Object> params) {
|
||||||
|
log.info("【报表服务】开始执行存储过程: {}", name);
|
||||||
|
|
||||||
|
// 根据 reportId 查询数据源信息
|
||||||
|
ReportDatabaseRespVO reportDatabase = null;
|
||||||
|
try {
|
||||||
|
log.info("【报表服务】开始查询报表数据源信息,reportId: {}", reportId);
|
||||||
|
ReportDatabaseDO databaseDO = reportDatabaseMapper.selectReportDatabaseByReportId(reportId);
|
||||||
|
|
||||||
|
if (databaseDO != null) {
|
||||||
|
// 将 DO 转换为 VO
|
||||||
|
reportDatabase = new ReportDatabaseRespVO();
|
||||||
|
BeanUtils.copyProperties(databaseDO, reportDatabase);
|
||||||
|
log.info("【报表服务】数据源信息查询成功: id={}, dbName={}, dbType={}, host={}, port={}",
|
||||||
|
reportDatabase.getId(), reportDatabase.getDbName(), reportDatabase.getDbType(),
|
||||||
|
reportDatabase.getHost(), reportDatabase.getPort());
|
||||||
|
} else {
|
||||||
|
log.warn("【报表服务】未找到报表ID对应的数据源信息: reportId={}", reportId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【报表服务】查询数据源信息失败: reportId={}", reportId, e);
|
||||||
|
// 查询失败不影响主流程,继续执行
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建请求对象
|
||||||
|
ProcedureRequestVO req = new ProcedureRequestVO();
|
||||||
|
req.setProcedureName(name);
|
||||||
|
req.setParams(params);
|
||||||
|
// 设置数据源信息
|
||||||
|
req.setReportDatabase(reportDatabase);
|
||||||
|
log.debug("【报表服务】请求对象构建完成: procedureName={}, reportDatabase={}",
|
||||||
|
name, reportDatabase != null ? "已设置" : "未设置");
|
||||||
|
|
||||||
|
// 设置 HTTP 请求头
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.set("X-API-KEY", apiKey); // 设置 API 密钥
|
||||||
|
log.debug("【报表服务】HTTP 请求头设置完成");
|
||||||
|
|
||||||
|
// 创建 HTTP 实体
|
||||||
|
HttpEntity<ProcedureRequestVO> entity = new HttpEntity<>(req, headers);
|
||||||
|
|
||||||
|
// 发送 POST 请求
|
||||||
|
log.info("【报表服务】开始调用远程接口: {}", executeUrl);
|
||||||
|
long requestStartTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
String rawResponse = restTemplate.postForObject(executeUrl, entity, String.class);
|
||||||
|
|
||||||
|
long requestEndTime = System.currentTimeMillis();
|
||||||
|
long requestDuration = requestEndTime - requestStartTime;
|
||||||
|
log.info("【报表服务】远程接口调用完成,耗时: {} 毫秒", requestDuration);
|
||||||
|
|
||||||
|
// 检查响应是否为空
|
||||||
|
if (rawResponse == null || rawResponse.trim().isEmpty()) {
|
||||||
|
log.error("【报表服务】远程接口返回为空");
|
||||||
|
throw new RuntimeException("远程接口返回为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录原始响应(调试级别)
|
||||||
|
log.info("【报表服务】收到远程接口响应,响应长度: {} 字符", rawResponse.length());
|
||||||
|
log.debug("========================================");
|
||||||
|
log.debug("【报表服务】原始响应 JSON: {}", rawResponse);
|
||||||
|
log.debug("========================================");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 解析 JSON 响应
|
||||||
|
log.debug("【报表服务】开始解析 JSON 响应");
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {};
|
||||||
|
Map<String, Object> responseMap = objectMapper.readValue(rawResponse, typeRef);
|
||||||
|
log.info("【报表服务】JSON 响应解析成功");
|
||||||
|
|
||||||
|
// 记录解析后的信息(调试级别)
|
||||||
|
log.debug("【报表服务】解析后的 responseMap: {}", responseMap);
|
||||||
|
log.debug("【报表服务】data 字段值: {}", responseMap.get("data"));
|
||||||
|
log.debug("【报表服务】data 字段类型: {}",
|
||||||
|
responseMap.get("data") != null ? responseMap.get("data").getClass().getName() : "null");
|
||||||
|
|
||||||
|
if (responseMap.get("data") instanceof List) {
|
||||||
|
int listSize = ((List<?>) responseMap.get("data")).size();
|
||||||
|
log.info("【报表服务】data 是 List 类型,包含 {} 条记录", listSize);
|
||||||
|
log.debug("【报表服务】data 是 List,大小: {}", listSize);
|
||||||
|
}
|
||||||
|
log.debug("========================================");
|
||||||
|
|
||||||
|
// 检查响应码
|
||||||
|
Object codeObj = responseMap.get("code");
|
||||||
|
if (codeObj != null) {
|
||||||
|
int code = (codeObj instanceof Integer) ? (Integer) codeObj : Integer.parseInt(String.valueOf(codeObj));
|
||||||
|
log.info("【报表服务】响应码: {}", code);
|
||||||
|
if (code != 0) {
|
||||||
|
// 响应码不为 0,表示调用失败
|
||||||
|
Object msgObj = responseMap.get("msg");
|
||||||
|
String msg = (msgObj != null) ? String.valueOf(msgObj) : "未知错误";
|
||||||
|
log.error("【报表服务】远程接口调用失败: code={}, msg={}", code, msg);
|
||||||
|
throw new RuntimeException("远程接口调用失败: code=" + code + ", msg=" + msg);
|
||||||
|
}
|
||||||
|
log.info("【报表服务】响应码检查通过(code=0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理响应数据
|
||||||
|
Object data = responseMap.get("data");
|
||||||
|
|
||||||
|
// 如果 data 为 null,返回空列表
|
||||||
|
if (data == null) {
|
||||||
|
log.warn("【报表服务】响应数据为 null,返回空列表");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 data 是字符串,尝试解析为 JSON
|
||||||
|
if (data instanceof String) {
|
||||||
|
String dataStr = (String) data;
|
||||||
|
log.debug("【报表服务】响应数据是字符串类型,长度: {}", dataStr.length());
|
||||||
|
// 空字符串返回空列表
|
||||||
|
if (dataStr.isEmpty() || dataStr.trim().isEmpty()) {
|
||||||
|
log.warn("【报表服务】响应数据为空字符串,返回空列表");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果字符串看起来像 JSON(以 [ 或 { 开头),尝试解析
|
||||||
|
String trimmed = dataStr.trim();
|
||||||
|
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
||||||
|
log.debug("【报表服务】检测到 JSON 格式字符串,尝试解析");
|
||||||
|
try {
|
||||||
|
Object parsedData = objectMapper.readValue(dataStr, Object.class);
|
||||||
|
log.info("【报表服务】JSON 字符串解析成功");
|
||||||
|
return parsedData;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【报表服务】解析 data JSON 字符串失败: {}", e.getMessage());
|
||||||
|
return dataStr; // 解析失败,返回原始字符串
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug("【报表服务】响应数据不是 JSON 格式,返回原始字符串");
|
||||||
|
return dataStr; // 不是 JSON 格式,返回原始字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他类型直接返回
|
||||||
|
log.info("【报表服务】响应数据处理完成,类型: {}", data.getClass().getSimpleName());
|
||||||
|
return data;
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("【报表服务】解析响应 JSON 失败: {}", e.getMessage());
|
||||||
|
log.error("【报表服务】原始响应: {}", rawResponse);
|
||||||
|
throw new RuntimeException("解析响应 JSON 失败: " + e.getMessage() + ", 原始响应: " + rawResponse, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.junchi.app.mcp.report.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储过程请求值对象
|
||||||
|
*
|
||||||
|
* <p>用于封装调用远程存储过程 API 的请求参数。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProcedureRequestVO {
|
||||||
|
/** 存储过程名称 */
|
||||||
|
private String procedureName;
|
||||||
|
|
||||||
|
/** 存储过程参数映射,key 为参数名,value 为参数值 */
|
||||||
|
private Map<String, Object> params;
|
||||||
|
|
||||||
|
/** 报表数据源信息 */
|
||||||
|
private ReportDatabaseRespVO reportDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.junchi.app.mcp.report.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储过程响应值对象
|
||||||
|
*
|
||||||
|
* <p>用于封装远程存储过程 API 返回的响应数据。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProcedureResponseVO {
|
||||||
|
/** 响应码,0 表示成功,非 0 表示失败 */
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/** 响应消息,通常用于描述错误信息 */
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/** 响应数据,可能是 List、Map、String 或其他类型 */
|
||||||
|
private Object data;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.junchi.app.mcp.report.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报表数据源响应 VO
|
||||||
|
*
|
||||||
|
* <p>用于封装报表数据源信息,用于传递给存储过程接口。</p>
|
||||||
|
*
|
||||||
|
* @author 欧浩蓝
|
||||||
|
* @since 2025-01-27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReportDatabaseRespVO {
|
||||||
|
/** 主键ID */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 数据源名称 */
|
||||||
|
private String dbName;
|
||||||
|
|
||||||
|
/** 数据库类型(mysql、pgsql、oracle、sqlserver) */
|
||||||
|
private String dbType;
|
||||||
|
|
||||||
|
/** 数据库地址 */
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
/** 数据库端口 */
|
||||||
|
private Integer port;
|
||||||
|
|
||||||
|
/** 数据库账号 */
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/** 数据库密码(建议加密存储) */
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/** 数据库名称 */
|
||||||
|
private String databaseName;
|
||||||
|
|
||||||
|
/** 备注说明 */
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
|
|
||||||
78
src/main/resources/application.yml
Normal file
78
src/main/resources/application.yml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# 服务器配置
|
||||||
|
server:
|
||||||
|
# 服务端口号
|
||||||
|
port: 48090
|
||||||
|
|
||||||
|
# Spring 应用配置
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
# 应用名称
|
||||||
|
name: ydoyun-report-mcp
|
||||||
|
# 禁用条件评估报告(避免启动时输出大量自动配置信息)
|
||||||
|
main:
|
||||||
|
log-startup-info: true # 只显示启动信息,不显示条件评估报告
|
||||||
|
# 数据源配置
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://118.253.178.8:23306/ydoyun-report-ai?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true
|
||||||
|
username: root
|
||||||
|
password: mysql_Fa4wpQ
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
ai:
|
||||||
|
mcp:
|
||||||
|
server:
|
||||||
|
# MCP 服务基础信息
|
||||||
|
name: ydoyun-report-mcp
|
||||||
|
version: 1.0.0
|
||||||
|
# 服务类型:ASYNC 表示异步模式
|
||||||
|
type: ASYNC
|
||||||
|
# 服务说明:描述 MCP 服务器的功能
|
||||||
|
instructions: "MCP server for Ydoyun report tools"
|
||||||
|
# SSE 消息推送接口地址:用于推送消息给客户端
|
||||||
|
sse-message-endpoint: /mcp/messages
|
||||||
|
# SSE 订阅端点:客户端订阅服务器端事件的端点
|
||||||
|
sse-endpoint: /mcp/sse
|
||||||
|
# MCP 能力声明:定义服务器支持的能力
|
||||||
|
capabilities:
|
||||||
|
tool: true # 支持工具调用
|
||||||
|
resource: false # 不支持资源访问
|
||||||
|
prompt: false # 不支持提示模板
|
||||||
|
completion: false # 不支持补全功能
|
||||||
|
|
||||||
|
# 云朵云报表服务配置
|
||||||
|
ydoyun:
|
||||||
|
mssqljdbc:
|
||||||
|
report:
|
||||||
|
api:
|
||||||
|
# API 密钥:用于调用远程存储过程接口的认证密钥
|
||||||
|
key: your_secret_api_key_123456
|
||||||
|
# 存储过程执行接口地址
|
||||||
|
execute-url: http://118.253.178.8:49090/api/procedure/execute
|
||||||
|
|
||||||
|
# MyBatis 配置
|
||||||
|
mybatis:
|
||||||
|
# Mapper XML 文件位置
|
||||||
|
mapper-locations: classpath*:mapper/**/*.xml
|
||||||
|
# 实体类包路径
|
||||||
|
type-aliases-package: com.junchi.app.mcp.dal.dataobject
|
||||||
|
configuration:
|
||||||
|
# 开启驼峰命名转换
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
# 日志实现
|
||||||
|
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO # 根日志级别
|
||||||
|
# MCP 相关包的日志级别设置为 DEBUG,便于调试
|
||||||
|
com.junchi.app.mcp: DEBUG
|
||||||
|
org:
|
||||||
|
# Spring 框架日志级别:INFO 用于正常运行时
|
||||||
|
# 注意:如果设置为 DEBUG,会输出条件评估报告(CONDITIONS EVALUATION REPORT)
|
||||||
|
springframework: INFO
|
||||||
|
# 特别控制自动配置包的日志级别,避免输出条件评估报告
|
||||||
|
springframework.boot.autoconfigure: WARN
|
||||||
|
# 日志输出格式配置(可选,使用默认格式)
|
||||||
|
pattern:
|
||||||
|
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
|
||||||
|
|
||||||
36
src/main/resources/mapper/ReportDatabaseMapper.xml
Normal file
36
src/main/resources/mapper/ReportDatabaseMapper.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.junchi.app.mcp.dal.mapper.ReportDatabaseMapper">
|
||||||
|
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="ReportDatabaseResultMap" type="com.junchi.app.mcp.dal.dataobject.ReportDatabaseDO">
|
||||||
|
<id column="id" property="id"/>
|
||||||
|
<result column="db_name" property="dbName"/>
|
||||||
|
<result column="db_type" property="dbType"/>
|
||||||
|
<result column="host" property="host"/>
|
||||||
|
<result column="port" property="port"/>
|
||||||
|
<result column="username" property="username"/>
|
||||||
|
<result column="password" property="password"/>
|
||||||
|
<result column="database_name" property="databaseName"/>
|
||||||
|
<result column="remark" property="remark"/>
|
||||||
|
<result column="creator" property="creator"/>
|
||||||
|
<result column="create_time" property="createTime"/>
|
||||||
|
<result column="updater" property="updater"/>
|
||||||
|
<result column="update_time" property="updateTime"/>
|
||||||
|
<result column="deleted" property="deleted"/>
|
||||||
|
<result column="tenant_id" property="tenantId"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 根据 report_id 查询绑定的数据源信息 -->
|
||||||
|
<select id="selectReportDatabaseByReportId"
|
||||||
|
resultMap="ReportDatabaseResultMap"
|
||||||
|
parameterType="java.lang.Long">
|
||||||
|
SELECT db.*
|
||||||
|
FROM ydoyun_report_database db
|
||||||
|
INNER JOIN ydoyun_report r ON r.database_id = db.id
|
||||||
|
WHERE r.id = #{reportId}
|
||||||
|
AND db.deleted = 0
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
|
|
||||||
Reference in New Issue
Block a user