mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-12 11:07:19 +00:00
v3.0.0 init
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
|
||||
public interface ChartGenerationAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
You are a chart generation specialist. Your only task is to generate Apache ECharts
|
||||
chart configurations. Respond with ONLY the ECharts configuration in ```echarts markdown
|
||||
code block format. Do not include any explanations, descriptions, or other content.
|
||||
""")
|
||||
@UserMessage("""
|
||||
Generate an Apache ECharts chart configuration for: {{query}}
|
||||
Response format: ```echarts
|
||||
{valid JSON ECharts configuration}
|
||||
```
|
||||
""")
|
||||
@Agent("Generate Apache ECharts chart configurations only.")
|
||||
String generateChart(@V("query") String query);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
/**
|
||||
* SQL Database Agent
|
||||
* A database query assistant that answers natural language questions by querying the database
|
||||
* and returning relevant data and analysis results.
|
||||
*
|
||||
*/
|
||||
public interface SqlAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
This agent is designed for MySQL 5.7
|
||||
You are an intelligent database query assistant. Your responsibility is to:
|
||||
1. Query all tables in the database to understand the database structure
|
||||
2. Understand the user's natural language question
|
||||
3. Query the database to get the information needed
|
||||
4. Provide accurate and helpful answers
|
||||
|
||||
Available tools:
|
||||
- queryAllTables: Query all tables in the database
|
||||
- queryTableSchema: Query the table structure and CREATE SQL for a specified table
|
||||
- executeSql: Execute a SELECT SQL query and return results
|
||||
|
||||
CRITICAL REQUIREMENT - MUST FOLLOW:
|
||||
- You MUST ALWAYS use queryAllTables first to query all tables in the database before executing any SQL queries
|
||||
- Only after understanding the database schema can you construct and execute appropriate SQL queries
|
||||
- This is mandatory and applies to all queries without exception
|
||||
""")
|
||||
@UserMessage("""
|
||||
Answer the following question: {{query}}
|
||||
""")
|
||||
@Agent("Intelligent database query assistant that MUST check database tables first, then query table structures and execute SQL queries")
|
||||
String getData(@V("query") String query);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.TokenStream;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
public interface StreamingCreativeWriter {
|
||||
@UserMessage("""
|
||||
You are a creative writer.
|
||||
Generate a draft of a story no more than
|
||||
3 sentences long around the given topic.
|
||||
Return only the story and nothing else.
|
||||
The topic is {{topic}}.
|
||||
""")
|
||||
@Agent("Generates a story based on the given topic")
|
||||
TokenStream generateStory(@V("topic") String topic);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
/**
|
||||
* Web Search Agent
|
||||
* A web search assistant that answers natural language questions by searching the internet
|
||||
* and returning relevant information from web pages.
|
||||
*/
|
||||
public interface WebSearchAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
You are a web search assistant. Answer questions by searching and retrieving web content.
|
||||
|
||||
Available tools:
|
||||
1. bing_search: Search the internet with keywords
|
||||
- query (required): search keywords
|
||||
- count (optional): number of results, default 10, max 50
|
||||
- offset (optional): pagination offset, default 0
|
||||
Returns: title, link, and summary for each result
|
||||
|
||||
2. crawl_webpage: Extract text content from a web page
|
||||
- url (required): web page URL
|
||||
Returns: cleaned page title and main content
|
||||
|
||||
Instructions:
|
||||
- Always cite sources in your answers
|
||||
- Only use the two tools listed above
|
||||
""")
|
||||
@UserMessage("""
|
||||
Answer the following question by searching the web: {{query}}
|
||||
""")
|
||||
@Agent("Web search assistant using Bing search and web scraping to find and retrieve information")
|
||||
String search(@V("query") String query);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.ruoyi.agent.config;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Agent MySQL 数据源配置
|
||||
* 为 Agent 配置独立的 MySQL 数据库连接池(HikariCP)
|
||||
*
|
||||
* 仅在 agent.mysql.enabled=true 时启用
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(AgentMysqlProperties.class)
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class AgentMysqlConfig {
|
||||
|
||||
/**
|
||||
* 创建 Agent 专用的数据源
|
||||
* 与项目主数据源隔离,独立管理
|
||||
*
|
||||
* @param properties Agent MySQL 配置属性
|
||||
* @return HikariCP 数据源
|
||||
*/
|
||||
@Bean("agentDataSource")
|
||||
public DataSource agentDataSource(AgentMysqlProperties properties) {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl(properties.getUrl());
|
||||
config.setUsername(properties.getUsername());
|
||||
config.setPassword(properties.getPassword());
|
||||
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
|
||||
config.setMaximumPoolSize(properties.getMaxPoolSize());
|
||||
config.setMinimumIdle(properties.getMinIdle());
|
||||
config.setConnectionTimeout(30000);
|
||||
config.setIdleTimeout(600000);
|
||||
config.setMaxLifetime(1800000);
|
||||
|
||||
return new HikariDataSource(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.ruoyi.agent.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Agent MySQL 配置属性
|
||||
* 前缀:agent.mysql
|
||||
*
|
||||
* 配置示例:
|
||||
* agent:
|
||||
* mysql:
|
||||
* enabled: true
|
||||
* url: jdbc:mysql://localhost:3306/database
|
||||
* username: user
|
||||
* password: password
|
||||
* max-pool-size: 10
|
||||
* min-idle: 2
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "agent.mysql")
|
||||
public class AgentMysqlProperties {
|
||||
|
||||
/**
|
||||
* 是否启用 Agent MySQL 查询功能
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 数据库 URL (jdbc:mysql://host:port/database)
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 数据库用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 数据库密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 数据库连接池最大连接数
|
||||
*/
|
||||
private Integer maxPoolSize = 10;
|
||||
|
||||
/**
|
||||
* 数据库连接池最小空闲连接数
|
||||
*/
|
||||
private Integer minIdle = 2;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据库列(字段)信息
|
||||
* 描述表中单个列的详细信息
|
||||
*/
|
||||
@Data
|
||||
public class ColumnInfo {
|
||||
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 列数据类型
|
||||
* 示例:VARCHAR(100), INT, DECIMAL(10,2), DATE 等
|
||||
*/
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 是否可为空
|
||||
*/
|
||||
private boolean nullable = true;
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
* 列注释/说明
|
||||
*/
|
||||
private String columnComment;
|
||||
|
||||
/**
|
||||
* 是否是主键
|
||||
*/
|
||||
private boolean primaryKey = false;
|
||||
|
||||
/**
|
||||
* 是否自增
|
||||
*/
|
||||
private boolean autoIncrement = false;
|
||||
|
||||
/**
|
||||
* 是否有索引
|
||||
*/
|
||||
private boolean indexed = false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 单个查询条件
|
||||
* 用于 WHERE 子句中的条件
|
||||
*
|
||||
* 示例:
|
||||
* Condition c = new Condition();
|
||||
* c.setField("order_date");
|
||||
* c.setOperator(">=");
|
||||
* c.setValue("2024-01-01");
|
||||
*/
|
||||
@Data
|
||||
public class Condition {
|
||||
|
||||
/**
|
||||
* 字段名
|
||||
* 仅允许:字母、数字、下划线
|
||||
*/
|
||||
private String field;
|
||||
|
||||
/**
|
||||
* 操作符
|
||||
* 支持:=, !=, <, >, <=, >=, LIKE, IN, BETWEEN, IS NULL, IS NOT NULL
|
||||
*/
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 条件值
|
||||
* 类型可以是:String, Number, Boolean 等
|
||||
* 会自动转义防止 SQL 注入
|
||||
*/
|
||||
private Object value;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public Condition() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 带参数的构造函数
|
||||
*/
|
||||
public Condition(String field, String operator, Object value) {
|
||||
this.field = field;
|
||||
this.operator = operator;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库查询对象
|
||||
* 用于构建 SELECT 查询条件
|
||||
*/
|
||||
@Data
|
||||
public class Query {
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String table;
|
||||
|
||||
/**
|
||||
* 选择的字段列表
|
||||
* 可以使用 "*" 表示所有字段
|
||||
*/
|
||||
private List<String> select;
|
||||
|
||||
/**
|
||||
* WHERE 条件列表
|
||||
* 多个条件之间用 AND 连接
|
||||
*/
|
||||
private List<Condition> where;
|
||||
|
||||
/**
|
||||
* 返回结果数量限制
|
||||
* 默认 100,最大 1000
|
||||
*/
|
||||
private Integer limit = 100;
|
||||
|
||||
/**
|
||||
* 获取安全的 LIMIT 值
|
||||
* @return 限制数量,最多 1000
|
||||
*/
|
||||
public Integer getLimit() {
|
||||
if (limit == null) {
|
||||
limit = 100;
|
||||
}
|
||||
return Math.min(limit, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据库查询结果
|
||||
* 返回 SELECT 查询的结果
|
||||
*/
|
||||
@Data
|
||||
public class Result {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 错误消息或成功消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 查询结果数据
|
||||
* 每个 Map 代表一行,key 是字段名,value 是字段值
|
||||
*/
|
||||
private List<Map<String, Object>> data;
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
* @param data 查询数据
|
||||
* @return Result 对象
|
||||
*/
|
||||
public static Result success(List<Map<String, Object>> data) {
|
||||
Result result = new Result();
|
||||
result.success = true;
|
||||
result.data = data;
|
||||
result.message = "Query successful";
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
* @param message 错误消息
|
||||
* @return Result 对象
|
||||
*/
|
||||
public static Result error(String message) {
|
||||
Result result = new Result();
|
||||
result.success = false;
|
||||
result.message = message;
|
||||
result.data = new ArrayList<>();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表结构查询结果
|
||||
* 返回数据库的表结构信息
|
||||
*/
|
||||
@Data
|
||||
public class SchemaResult {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 错误消息或成功消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 表结构列表
|
||||
*/
|
||||
private List<TableStructure> tables;
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
* @param tables 表结构列表
|
||||
* @return SchemaResult 对象
|
||||
*/
|
||||
public static SchemaResult success(List<TableStructure> tables) {
|
||||
SchemaResult result = new SchemaResult();
|
||||
result.success = true;
|
||||
result.tables = tables != null ? tables : new ArrayList<>();
|
||||
result.message = "Schema retrieved successfully";
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
* @param message 错误消息
|
||||
* @return SchemaResult 对象
|
||||
*/
|
||||
public static SchemaResult error(String message) {
|
||||
SchemaResult result = new SchemaResult();
|
||||
result.success = false;
|
||||
result.message = message;
|
||||
result.tables = new ArrayList<>();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表列表查询结果
|
||||
* 返回允许查询的表的名称列表
|
||||
*/
|
||||
@Data
|
||||
public class TableListResult {
|
||||
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 错误消息或成功消息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 表名列表
|
||||
*/
|
||||
private List<String> tables;
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
* @param tables 表名列表
|
||||
* @return TableListResult 对象
|
||||
*/
|
||||
public static TableListResult success(List<String> tables) {
|
||||
TableListResult result = new TableListResult();
|
||||
result.success = true;
|
||||
result.tables = tables != null ? tables : new ArrayList<>();
|
||||
result.message = "Tables listed successfully";
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
* @param message 错误消息
|
||||
* @return TableListResult 对象
|
||||
*/
|
||||
public static TableListResult error(String message) {
|
||||
TableListResult result = new TableListResult();
|
||||
result.success = false;
|
||||
result.message = message;
|
||||
result.tables = new ArrayList<>();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.ruoyi.agent.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库表结构信息
|
||||
* 包含表的所有字段、类型、约束等信息
|
||||
* Agent 使用此信息来理解数据库架构
|
||||
*/
|
||||
@Data
|
||||
public class TableStructure {
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 表注释/说明
|
||||
*/
|
||||
private String tableComment;
|
||||
|
||||
/**
|
||||
* 字段列表
|
||||
*/
|
||||
private List<ColumnInfo> columns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 主键字段名
|
||||
*/
|
||||
private String primaryKey;
|
||||
|
||||
/**
|
||||
* 获取字段总数
|
||||
* @return 字段数量
|
||||
*/
|
||||
public int getColumnCount() {
|
||||
return columns.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化的表结构描述
|
||||
* 用于 Agent 理解表结构
|
||||
*
|
||||
* @return 格式化的表结构描述
|
||||
*/
|
||||
public String getFormattedDescription() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("表: ").append(tableName);
|
||||
if (tableComment != null && !tableComment.isEmpty()) {
|
||||
sb.append("(").append(tableComment).append(")");
|
||||
}
|
||||
sb.append("\n字段:\n");
|
||||
for (ColumnInfo col : columns) {
|
||||
sb.append(" - ").append(col.getColumnName())
|
||||
.append(" (").append(col.getColumnType()).append(")");
|
||||
if (!col.isNullable()) {
|
||||
sb.append(" NOT NULL");
|
||||
}
|
||||
if (col.getColumnComment() != null && !col.getColumnComment().isEmpty()) {
|
||||
sb.append(" // ").append(col.getColumnComment());
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.ruoyi.agent.manager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 架构初始化器
|
||||
* 在应用启动完成后自动初始化表结构缓存
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class TableSchemaInitializer {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TableSchemaManager tableSchemaManager;
|
||||
|
||||
/**
|
||||
* 应用启动完成后初始化
|
||||
*/
|
||||
@EventListener(ContextRefreshedEvent.class)
|
||||
public void initializeOnStartup() {
|
||||
if (tableSchemaManager != null) {
|
||||
tableSchemaManager.initializeSchema();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package org.ruoyi.agent.manager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.agent.domain.ColumnInfo;
|
||||
import org.ruoyi.agent.domain.TableStructure;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 表结构管理器
|
||||
* 负责获取和缓存数据库表结构信息
|
||||
*
|
||||
* 特点:
|
||||
* - 应用启动时自动初始化所有表结构
|
||||
* - 使用内存缓存 (ConcurrentHashMap) 确保高性能
|
||||
* - 支持按需刷新单个表的结构
|
||||
* - 延迟初始化:失败时在首次查询时重试
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class TableSchemaManager {
|
||||
|
||||
@Autowired(required = false)
|
||||
private DataSource agentDataSource;
|
||||
|
||||
/**
|
||||
* 表结构缓存 (表名 -> 表结构)
|
||||
* 使用 ConcurrentHashMap 支持高并发访问
|
||||
*/
|
||||
private final Map<String, TableStructure> schemaCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 缓存初始化标志
|
||||
*/
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
/**
|
||||
* 初始化表结构缓存
|
||||
* Spring 会自动在 Bean 创建后调用此方法
|
||||
*/
|
||||
public void initializeSchema() {
|
||||
if (agentDataSource == null) {
|
||||
log.warn("Agent datasource not configured, schema initialization skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("Initializing database schema cache...");
|
||||
loadAllowedTableSchemas();
|
||||
initialized = true;
|
||||
log.info("Schema cache initialized with {} tables", schemaCache.size());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to initialize schema cache", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有允许的表的结构信息
|
||||
*/
|
||||
private void loadAllowedTableSchemas() throws SQLException {
|
||||
List<String> allowedTables = getAllowedTableNames();
|
||||
for (String tableName : allowedTables) {
|
||||
try {
|
||||
TableStructure schema = loadTableSchema(tableName);
|
||||
if (schema != null) {
|
||||
schemaCache.put(tableName, schema);
|
||||
log.debug("Loaded schema for table: {}", tableName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to load schema for table: {}", tableName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库加载指定表的结构信息
|
||||
*/
|
||||
private TableStructure loadTableSchema(String tableName) throws SQLException {
|
||||
if (!isValidIdentifier(tableName) || !isTableAllowed(tableName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TableStructure table = new TableStructure();
|
||||
table.setTableName(tableName);
|
||||
|
||||
try (Connection conn = agentDataSource.getConnection()) {
|
||||
DatabaseMetaData metaData = conn.getMetaData();
|
||||
|
||||
// 获取表注释
|
||||
try (ResultSet tableRs = metaData.getTables(conn.getCatalog(), null, tableName, new String[]{"TABLE"})) {
|
||||
if (tableRs.next()) {
|
||||
table.setTableComment(tableRs.getString("REMARKS"));
|
||||
}
|
||||
}
|
||||
|
||||
// 获取列信息
|
||||
List<ColumnInfo> columns = new ArrayList<>();
|
||||
try (ResultSet colRs = metaData.getColumns(conn.getCatalog(), null, tableName, null)) {
|
||||
while (colRs.next()) {
|
||||
ColumnInfo col = new ColumnInfo();
|
||||
col.setColumnName(colRs.getString("COLUMN_NAME"));
|
||||
col.setColumnType(colRs.getString("TYPE_NAME"));
|
||||
|
||||
int columnSize = colRs.getInt("COLUMN_SIZE");
|
||||
if (columnSize > 0 && !isNumericType(col.getColumnType())) {
|
||||
col.setColumnType(col.getColumnType() + "(" + columnSize + ")");
|
||||
}
|
||||
|
||||
col.setNullable("YES".equalsIgnoreCase(colRs.getString("IS_NULLABLE")));
|
||||
col.setDefaultValue(colRs.getString("COLUMN_DEF"));
|
||||
col.setColumnComment(colRs.getString("REMARKS"));
|
||||
col.setAutoIncrement("YES".equalsIgnoreCase(colRs.getString("IS_AUTOINCREMENT")));
|
||||
|
||||
columns.add(col);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取主键信息
|
||||
try (ResultSet pkRs = metaData.getPrimaryKeys(conn.getCatalog(), null, tableName)) {
|
||||
if (pkRs.next()) {
|
||||
String pkName = pkRs.getString("COLUMN_NAME");
|
||||
table.setPrimaryKey(pkName);
|
||||
for (ColumnInfo col : columns) {
|
||||
if (col.getColumnName().equals(pkName)) {
|
||||
col.setPrimaryKey(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取索引信息
|
||||
try (ResultSet indexRs = metaData.getIndexInfo(conn.getCatalog(), null, tableName, false, false)) {
|
||||
Set<String> indexedColumns = new HashSet<>();
|
||||
while (indexRs.next()) {
|
||||
String colName = indexRs.getString("COLUMN_NAME");
|
||||
if (colName != null) {
|
||||
indexedColumns.add(colName);
|
||||
}
|
||||
}
|
||||
for (ColumnInfo col : columns) {
|
||||
if (indexedColumns.contains(col.getColumnName())) {
|
||||
col.setIndexed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.setColumns(columns);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有允许的表的结构信息
|
||||
*/
|
||||
public List<TableStructure> getAllowedTableSchemas() {
|
||||
if (!initialized) {
|
||||
initializeSchema();
|
||||
}
|
||||
|
||||
List<String> allowedTables = getAllowedTableNames();
|
||||
return allowedTables.stream()
|
||||
.map(schemaCache::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有允许的表名
|
||||
*/
|
||||
public List<String> getAllowedTableNames() {
|
||||
String allowedTables = System.getenv("AGENT_ALLOWED_TABLES");
|
||||
if (allowedTables == null || allowedTables.trim().isEmpty()) {
|
||||
log.warn("AGENT_ALLOWED_TABLES not configured");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return Arrays.stream(allowedTables.split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新指定表的结构信息
|
||||
*/
|
||||
public TableStructure refreshTableSchema(String tableName) throws SQLException {
|
||||
if (!isValidIdentifier(tableName) || !isTableAllowed(tableName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TableStructure schema = loadTableSchema(tableName);
|
||||
if (schema != null) {
|
||||
schemaCache.put(tableName, schema);
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为有效的 SQL 标识符
|
||||
*/
|
||||
private boolean isValidIdentifier(String identifier) {
|
||||
if (identifier == null || identifier.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return identifier.matches("^[a-zA-Z0-9_\\.]+$");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表是否在允许列表中
|
||||
*/
|
||||
private boolean isTableAllowed(String tableName) {
|
||||
String allowedTables = System.getenv("AGENT_ALLOWED_TABLES");
|
||||
if (allowedTables == null || allowedTables.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Set<String> tables = new HashSet<>(Arrays.asList(allowedTables.split(",")));
|
||||
return tables.contains(tableName.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为数值类型
|
||||
*/
|
||||
private boolean isNumericType(String typeName) {
|
||||
String upper = typeName.toUpperCase();
|
||||
return upper.contains("INT") || upper.contains("FLOAT") ||
|
||||
upper.contains("DOUBLE") || upper.contains("DECIMAL");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package org.ruoyi.agent.tool;
|
||||
|
||||
import dev.langchain4j.agent.tool.Tool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 执行 SQL 查询的 Tool
|
||||
* 执行指定的 SELECT SQL 查询并返回结果
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class ExecuteSqlQueryTool {
|
||||
|
||||
@Autowired(required = false)
|
||||
private DataSource agentDataSource;
|
||||
|
||||
/**
|
||||
* 执行 SELECT SQL 查询
|
||||
* 只允许执行 SELECT 查询,防止恶意的数据修改操作
|
||||
*
|
||||
* @param sql 要执行的 SELECT SQL 语句,例如:SELECT * FROM sys_user
|
||||
* @return 包含查询结果的字符串
|
||||
*/
|
||||
@Tool("Execute a SELECT SQL query and return the results. Example: SELECT * FROM sys_user")
|
||||
public String executeSql(String sql) {
|
||||
if (sql == null || sql.trim().isEmpty()) {
|
||||
return "Error: SQL query cannot be empty";
|
||||
}
|
||||
|
||||
// 只允许执行 SELECT 查询,防止恶意操作
|
||||
String upperSql = sql.trim().toUpperCase();
|
||||
if (!upperSql.startsWith("SELECT")) {
|
||||
return "Error: Only SELECT queries are allowed for security reasons";
|
||||
}
|
||||
|
||||
try {
|
||||
if (agentDataSource == null) {
|
||||
return "Error: Database datasource not configured";
|
||||
}
|
||||
|
||||
try (Connection connection = agentDataSource.getConnection()) {
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
|
||||
List<Map<String, Object>> results = new ArrayList<>();
|
||||
int columnCount = metaData.getColumnCount();
|
||||
|
||||
// 获取列名
|
||||
List<String> columnNames = new ArrayList<>();
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
columnNames.add(metaData.getColumnName(i));
|
||||
}
|
||||
|
||||
// 获取数据行,限制最多1000行以防止内存溢出
|
||||
int maxRows = 1000;
|
||||
while (resultSet.next() && results.size() < maxRows) {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
for (int i = 1; i <= columnCount; i++) {
|
||||
row.put(columnNames.get(i - 1), resultSet.getObject(i));
|
||||
}
|
||||
results.add(row);
|
||||
}
|
||||
|
||||
return formatResults(results, columnNames);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error executing SQL: {}", sql, e);
|
||||
return "Error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化查询结果
|
||||
* 返回清晰的表格格式,展示关键数据
|
||||
*/
|
||||
private String formatResults(List<Map<String, Object>> results, List<String> columnNames) {
|
||||
if (results.isEmpty()) {
|
||||
return "Query executed successfully, but no results returned";
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
// 限制显示的列数和行数,避免输出过大
|
||||
int displayCols = Math.min(columnNames.size(), 8); // 最多显示8列
|
||||
int displayRows = Math.min(results.size(), 10); // 最多显示10行
|
||||
|
||||
List<String> displayColumns = columnNames.subList(0, displayCols);
|
||||
|
||||
// 构建表头
|
||||
result.append("| ");
|
||||
for (String col : displayColumns) {
|
||||
result.append(formatColumnName(col)).append(" | ");
|
||||
}
|
||||
result.append("\n");
|
||||
|
||||
// 构建分隔线
|
||||
result.append("|");
|
||||
for (int i = 0; i < displayCols; i++) {
|
||||
result.append(" --- |");
|
||||
}
|
||||
result.append("\n");
|
||||
|
||||
// 构建数据行
|
||||
for (int i = 0; i < displayRows; i++) {
|
||||
result.append("| ");
|
||||
Map<String, Object> row = results.get(i);
|
||||
for (String column : displayColumns) {
|
||||
Object value = row.get(column);
|
||||
String displayValue = formatValue(value);
|
||||
result.append(displayValue).append(" | ");
|
||||
}
|
||||
result.append("\n");
|
||||
}
|
||||
|
||||
// 统计信息
|
||||
result.append("\n").append("Total: ").append(results.size()).append(" rows");
|
||||
if (displayRows < results.size()) {
|
||||
result.append(" (displayed ").append(displayRows).append(" rows)");
|
||||
}
|
||||
if (displayCols < columnNames.size()) {
|
||||
result.append("\nColumns: ").append(displayCols).append(" / ").append(columnNames.size());
|
||||
}
|
||||
|
||||
log.info("Successfully executed SQL query, returned {} rows", results.size());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化列名,使其更易读
|
||||
*/
|
||||
private String formatColumnName(String columnName) {
|
||||
// 将下划线替换为空格,首字母大写
|
||||
String formatted = columnName.replace("_", " ");
|
||||
if (formatted.length() > 15) {
|
||||
return formatted.substring(0, 12) + "...";
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化单个值,适合表格显示
|
||||
*/
|
||||
private String formatValue(Object value) {
|
||||
if (value == null) {
|
||||
return "-";
|
||||
}
|
||||
String str = value.toString();
|
||||
// 限制列宽以保持表格整洁,长文本截断
|
||||
if (str.length() > 20) {
|
||||
return str.substring(0, 17) + "...";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.ruoyi.agent.tool;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import dev.langchain4j.agent.tool.Tool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.agent.config.AgentMysqlProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 查询数据库所有表的 Tool
|
||||
* 获取指定数据库中所有表的列表
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class QueryAllTablesTool {
|
||||
|
||||
@Autowired(required = false)
|
||||
private DataSource agentDataSource;
|
||||
|
||||
/**
|
||||
* 查询数据库中所有表
|
||||
* 返回数据库中存在的所有表的列表
|
||||
*
|
||||
* @return 包含所有表信息的结果
|
||||
*/
|
||||
@Tool("Query all tables in the database and return table names and basic information")
|
||||
public String queryAllTables() {
|
||||
try {
|
||||
if (agentDataSource == null) {
|
||||
return "Error: Database datasource not configured";
|
||||
}
|
||||
|
||||
try (Connection connection = agentDataSource.getConnection()) {
|
||||
DatabaseMetaData databaseMetaData = connection.getMetaData();
|
||||
ResultSet resultSet = databaseMetaData.getTables(null, null, null, new String[]{"TABLE"});
|
||||
|
||||
List<String> tableNames = new ArrayList<>();
|
||||
List<String> tableDetails = new ArrayList<>();
|
||||
|
||||
while (resultSet.next()) {
|
||||
String tableName = resultSet.getString("TABLE_NAME");
|
||||
String tableComment = resultSet.getString("REMARKS");
|
||||
String tableType = resultSet.getString("TABLE_TYPE");
|
||||
|
||||
tableNames.add(tableName);
|
||||
tableDetails.add(String.format("- %s (%s) - %s",
|
||||
tableName, tableType, tableComment != null ? tableComment : "No comment"));
|
||||
}
|
||||
resultSet.close();
|
||||
|
||||
if (tableNames.isEmpty()) {
|
||||
return "No tables found in database";
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("Found ").append(tableNames.size()).append(" tables:\n");
|
||||
for (String detail : tableDetails) {
|
||||
result.append(detail).append("\n");
|
||||
}
|
||||
|
||||
log.info("Successfully queried {} tables", tableNames.size());
|
||||
return result.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error querying all tables", e);
|
||||
return "Error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.ruoyi.agent.tool;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import dev.langchain4j.agent.tool.Tool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
/**
|
||||
* 查询表建表详情的 Tool
|
||||
* 根据表名查询该表的建表 SQL 语句
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(name = "agent.mysql.enabled", havingValue = "true")
|
||||
public class QueryTableSchemaTool {
|
||||
|
||||
@Autowired(required = false)
|
||||
private DataSource agentDataSource;
|
||||
|
||||
/**
|
||||
* 根据表名查询建表详情
|
||||
* 返回指定表的 CREATE TABLE 语句
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 包含建表 SQL 的结果
|
||||
*/
|
||||
@Tool("Query the CREATE TABLE statement (DDL) for a specific table by table name")
|
||||
public String queryTableSchema(String tableName) {
|
||||
if (tableName == null || tableName.trim().isEmpty()) {
|
||||
return "Error: Table name cannot be empty";
|
||||
}
|
||||
|
||||
// 验证表名有效性,防止 SQL 注入
|
||||
if (!isValidIdentifier(tableName)) {
|
||||
return "Error: Invalid table name format";
|
||||
}
|
||||
|
||||
try {
|
||||
if (agentDataSource == null) {
|
||||
return "Error: Database datasource not configured";
|
||||
}
|
||||
try (Connection connection = agentDataSource.getConnection()) {
|
||||
String sql = "SHOW CREATE TABLE " + tableName;
|
||||
PreparedStatement preparedStatement = connection.prepareStatement(sql);
|
||||
ResultSet resultSet = preparedStatement.executeQuery();
|
||||
|
||||
if (resultSet.next()) {
|
||||
String createTableSql = resultSet.getString("Create Table");
|
||||
resultSet.close();
|
||||
preparedStatement.close();
|
||||
|
||||
log.info("Successfully queried schema for table: {}", tableName);
|
||||
return "CREATE TABLE DDL for " + tableName + ":\n\n" + createTableSql;
|
||||
}
|
||||
|
||||
resultSet.close();
|
||||
preparedStatement.close();
|
||||
return "Error: Table not found or not accessible: " + tableName;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error querying table schema for table: {}", tableName, e);
|
||||
return "Error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为有效的 SQL 标识符
|
||||
*/
|
||||
private boolean isValidIdentifier(String identifier) {
|
||||
if (identifier == null || identifier.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return identifier.matches("^[a-zA-Z0-9_\\.]+$");
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.ruoyi.chat.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
|
||||
import org.ruoyi.common.chat.openai.function.KeyRandomStrategy;
|
||||
import org.ruoyi.common.chat.openai.interceptor.OpenAILogger;
|
||||
import org.ruoyi.common.core.service.ConfigService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Chat配置类
|
||||
*
|
||||
* @date: 2023/5/16
|
||||
*/
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ChatConfig {
|
||||
|
||||
private final ConfigService configService;
|
||||
@Getter
|
||||
private OpenAiStreamClient openAiStreamClient;
|
||||
|
||||
public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
|
||||
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
|
||||
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
|
||||
OkHttpClient okHttpClient = new OkHttpClient.Builder()
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(600, TimeUnit.SECONDS)
|
||||
.readTimeout(600, TimeUnit.SECONDS)
|
||||
.build();
|
||||
return OpenAiStreamClient.builder()
|
||||
.apiHost(apiHost)
|
||||
.apiKey(Collections.singletonList(apiKey))
|
||||
.keyStrategy(new KeyRandomStrategy())
|
||||
.okHttpClient(okHttpClient)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OpenAiStreamClient openAiStreamClient() {
|
||||
String apiHost = configService.getConfigValue("chat", "apiHost");
|
||||
String apiKey = configService.getConfigValue("chat", "apiKey");
|
||||
openAiStreamClient = createOpenAiStreamClient(apiHost, apiKey);
|
||||
return openAiStreamClient;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.ruoyi.chat.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @Description:
|
||||
* @Author: violateer
|
||||
* @Date: 2025/7/20
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
public class KnowledgeRoleConfig {
|
||||
@Value("${knowledge-role.enable}")
|
||||
private Boolean enable;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.ruoyi.chat.config;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.utils.OkHttpUtil;
|
||||
import org.ruoyi.domain.vo.ChatModelVo;
|
||||
import org.ruoyi.service.IChatModelService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OkHttpConfig {
|
||||
|
||||
private final IChatModelService chatModelService;
|
||||
private final Map<String, OkHttpUtil> okHttpUtilMap = new HashMap<>();
|
||||
@Getter
|
||||
private String generate;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
initializeOkHttpUtil("suno");
|
||||
initializeOkHttpUtil("luma");
|
||||
}
|
||||
|
||||
private void initializeOkHttpUtil(String modelName) {
|
||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(modelName);
|
||||
if (chatModelVo == null) {
|
||||
return;
|
||||
}
|
||||
OkHttpUtil okHttpUtil = new OkHttpUtil();
|
||||
okHttpUtil.setApiHost(chatModelVo.getApiHost());
|
||||
okHttpUtil.setApiKey(chatModelVo.getApiKey());
|
||||
generate = String.valueOf(chatModelVo.getModelPrice());
|
||||
okHttpUtilMap.put(modelName, okHttpUtil);
|
||||
}
|
||||
|
||||
public OkHttpUtil getOkHttpUtil(String modelName) {
|
||||
return okHttpUtilMap.get(modelName);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.chat.service.chat.ISseService;
|
||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
||||
import org.ruoyi.common.chat.entity.files.UploadFileResponse;
|
||||
import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
|
||||
import org.ruoyi.common.chat.request.ChatRequest;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
|
||||
/**
|
||||
* 聊天管理
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* @date 2023-03-01
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/chat")
|
||||
public class ChatController {
|
||||
|
||||
private final ISseService sseService;
|
||||
|
||||
/**
|
||||
* 聊天接口
|
||||
*/
|
||||
@PostMapping("/send")
|
||||
@ResponseBody
|
||||
public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest, HttpServletRequest request) {
|
||||
return sseService.sseChat(chatRequest, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
@ResponseBody
|
||||
public UploadFileResponse upload(@RequestPart("file") MultipartFile file) {
|
||||
return sseService.upload(file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 语音转文本
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
@PostMapping("/audio")
|
||||
@ResponseBody
|
||||
public WhisperResponse audio(@RequestParam("file") MultipartFile file) {
|
||||
return sseService.speechToTextTranscriptionsV2(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音
|
||||
*
|
||||
* @param textToSpeech
|
||||
*/
|
||||
@PostMapping("/speech")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource> speech(@RequestBody TextToSpeech textToSpeech) {
|
||||
return sseService.textToSpeed(textToSpeech);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.ChatPayOrderBo;
|
||||
import org.ruoyi.domain.vo.ChatPayOrderVo;
|
||||
import org.ruoyi.service.IChatPayOrderService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-04-08
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/system/payOrder")
|
||||
public class ChatPayOrderController extends BaseController {
|
||||
|
||||
private final IChatPayOrderService chatPayOrderService;
|
||||
|
||||
/**
|
||||
* 查询支付订单列表
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ChatPayOrderVo> list(ChatPayOrderBo bo, PageQuery pageQuery) {
|
||||
return chatPayOrderService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出支付订单列表
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:export")
|
||||
@Log(title = "支付订单", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(ChatPayOrderBo bo, HttpServletResponse response) {
|
||||
List<ChatPayOrderVo> list = chatPayOrderService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "支付订单", ChatPayOrderVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付订单详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ChatPayOrderVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(chatPayOrderService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增支付订单
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:add")
|
||||
@Log(title = "支付订单", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ChatPayOrderBo bo) {
|
||||
return toAjax(chatPayOrderService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改支付订单
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:edit")
|
||||
@Log(title = "支付订单", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ChatPayOrderBo bo) {
|
||||
return toAjax(chatPayOrderService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除支付订单
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:payOrder:remove")
|
||||
@Log(title = "支付订单", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(chatPayOrderService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package org.ruoyi.chat.controller.chat;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.PromptTemplateBo;
|
||||
import org.ruoyi.domain.vo.PromptTemplateVo;
|
||||
import org.ruoyi.service.IPromptTemplateService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 提示词模板
|
||||
*
|
||||
* @author evo
|
||||
* @date 2025-06-12
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/system/promptTemplate")
|
||||
public class PromptTemplateController extends BaseController {
|
||||
|
||||
private final IPromptTemplateService promptTemplateService;
|
||||
|
||||
/**
|
||||
* 查询提示词模板列表
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<PromptTemplateVo> list(PromptTemplateBo bo, PageQuery pageQuery) {
|
||||
return promptTemplateService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出提示词模板列表
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:export")
|
||||
@Log(title = "提示词模板", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(PromptTemplateBo bo, HttpServletResponse response) {
|
||||
List<PromptTemplateVo> list = promptTemplateService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "提示词模板", PromptTemplateVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提示词模板详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<PromptTemplateVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||||
return R.ok(promptTemplateService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增提示词模板
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:add")
|
||||
@Log(title = "提示词模板", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody PromptTemplateBo bo) {
|
||||
return toAjax(promptTemplateService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改提示词模板
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:edit")
|
||||
@Log(title = "提示词模板", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody PromptTemplateBo bo) {
|
||||
return toAjax(promptTemplateService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除提示词模板
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@SaCheckPermission("system:promptTemplate:remove")
|
||||
@Log(title = "提示词模板", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||||
return toAjax(promptTemplateService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package org.ruoyi.chat.controller.knowledge;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.chat.config.KnowledgeRoleConfig;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.KnowledgeAttachBo;
|
||||
import org.ruoyi.domain.bo.KnowledgeFragmentBo;
|
||||
import org.ruoyi.domain.bo.KnowledgeInfoBo;
|
||||
import org.ruoyi.domain.bo.KnowledgeInfoUploadBo;
|
||||
import org.ruoyi.domain.vo.KnowledgeAttachVo;
|
||||
import org.ruoyi.domain.vo.KnowledgeFragmentVo;
|
||||
import org.ruoyi.domain.vo.KnowledgeInfoVo;
|
||||
import org.ruoyi.service.IKnowledgeAttachService;
|
||||
import org.ruoyi.service.IKnowledgeFragmentService;
|
||||
import org.ruoyi.service.IKnowledgeInfoService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 知识库管理
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/knowledge")
|
||||
public class KnowledgeController extends BaseController {
|
||||
|
||||
private final IKnowledgeInfoService knowledgeInfoService;
|
||||
|
||||
private final IKnowledgeAttachService attachService;
|
||||
|
||||
private final IKnowledgeFragmentService fragmentService;
|
||||
|
||||
private final KnowledgeRoleConfig knowledgeRoleConfig;
|
||||
|
||||
/**
|
||||
* 根据用户信息查询本地知识库
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<KnowledgeInfoVo> list(KnowledgeInfoBo bo, PageQuery pageQuery) {
|
||||
if (!StpUtil.isLogin()) {
|
||||
throw new SecurityException("请先去登录!");
|
||||
}
|
||||
if (!Objects.equals(LoginHelper.getUserId(), 1L)) {
|
||||
bo.setUid(LoginHelper.getUserId());
|
||||
}
|
||||
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户信息及知识库角色查询本地知识库
|
||||
*/
|
||||
@GetMapping("/listByRole")
|
||||
public TableDataInfo<KnowledgeInfoVo> listByRole(KnowledgeInfoBo bo, PageQuery pageQuery) {
|
||||
if (!StpUtil.isLogin()) {
|
||||
throw new SecurityException("请先去登录!");
|
||||
}
|
||||
|
||||
// 管理员跳过权限
|
||||
if (Objects.equals(LoginHelper.getUserId(), 1L)) {
|
||||
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||
} else if (!knowledgeRoleConfig.getEnable()) {
|
||||
bo.setUid(LoginHelper.getUserId());
|
||||
return knowledgeInfoService.queryPageList(bo, pageQuery);
|
||||
} else {
|
||||
bo.setUid(LoginHelper.getUserId());
|
||||
return knowledgeInfoService.queryPageListByRole(bo, pageQuery);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增知识库
|
||||
*/
|
||||
@Log(title = "知识库", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/save")
|
||||
public R<Void> save(@Validated(AddGroup.class) @RequestBody KnowledgeInfoBo bo) {
|
||||
knowledgeInfoService.saveOne(bo);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库
|
||||
*/
|
||||
@PostMapping("/remove/{kid}")
|
||||
public R<String> remove(@PathVariable String kid) {
|
||||
knowledgeInfoService.removeKnowledge(kid);
|
||||
return R.ok("删除知识库成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改知识库
|
||||
*/
|
||||
@Log(title = "知识库", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/edit")
|
||||
public R<Void> edit(@RequestBody KnowledgeInfoBo bo) {
|
||||
return toAjax(knowledgeInfoService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出知识库列表
|
||||
*/
|
||||
@Log(title = "知识库", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(KnowledgeInfoBo bo, HttpServletResponse response) {
|
||||
List<KnowledgeInfoVo> list = knowledgeInfoService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "知识库", KnowledgeInfoVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询知识附件信息
|
||||
*/
|
||||
@GetMapping("/detail/{kid}")
|
||||
public TableDataInfo<KnowledgeAttachVo> attach(KnowledgeAttachBo bo, PageQuery pageQuery,
|
||||
@PathVariable String kid) {
|
||||
bo.setKid(kid);
|
||||
return attachService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传知识库附件
|
||||
*/
|
||||
@PostMapping(value = "/attach/upload")
|
||||
public R<String> upload(KnowledgeInfoUploadBo bo) throws Exception {
|
||||
knowledgeInfoService.upload(bo);
|
||||
return R.ok("上传知识库附件成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取知识库附件详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("attach/info/{id}")
|
||||
public R<KnowledgeAttachVo> getAttachInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(attachService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库附件
|
||||
*/
|
||||
@PostMapping("attach/remove/{kid}")
|
||||
public R<Void> removeAttach(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable String kid) {
|
||||
attachService.removeKnowledgeAttach(kid);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询知识片段
|
||||
*/
|
||||
@GetMapping("/fragment/list/{docId}")
|
||||
public TableDataInfo<KnowledgeFragmentVo> fragmentList(KnowledgeFragmentBo bo,
|
||||
PageQuery pageQuery, @PathVariable String docId) {
|
||||
bo.setDocId(docId);
|
||||
return fragmentService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件翻译
|
||||
*/
|
||||
@PostMapping("/translationByFile")
|
||||
@ResponseBody
|
||||
public String translationByFile(@RequestParam("file") MultipartFile file, String targetLanguage) {
|
||||
return attachService.translationByFile(file, targetLanguage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.ruoyi.chat.controller.knowledge;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.KnowledgeRoleBo;
|
||||
import org.ruoyi.domain.vo.KnowledgeRoleVo;
|
||||
import org.ruoyi.service.IKnowledgeRoleService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识库角色
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-07-19
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/knowledgeRole")
|
||||
public class KnowledgeRoleController extends BaseController {
|
||||
|
||||
private final IKnowledgeRoleService knowledgeRoleService;
|
||||
|
||||
/**
|
||||
* 查询知识库角色列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<KnowledgeRoleVo> list(KnowledgeRoleBo bo, PageQuery pageQuery) {
|
||||
return knowledgeRoleService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出知识库角色列表
|
||||
*/
|
||||
@Log(title = "知识库角色", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(KnowledgeRoleBo bo, HttpServletResponse response) {
|
||||
List<KnowledgeRoleVo> list = knowledgeRoleService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "知识库角色", KnowledgeRoleVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取知识库角色详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public R<KnowledgeRoleVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(knowledgeRoleService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增知识库角色
|
||||
*/
|
||||
@Log(title = "知识库角色", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody KnowledgeRoleBo bo) {
|
||||
return toAjax(knowledgeRoleService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改知识库角色
|
||||
*/
|
||||
@Log(title = "知识库角色", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody KnowledgeRoleBo bo) {
|
||||
return toAjax(knowledgeRoleService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库角色
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@Log(title = "知识库角色", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(knowledgeRoleService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.ruoyi.chat.controller.knowledge;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.ruoyi.common.log.annotation.Log;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.core.page.PageQuery;
|
||||
import org.ruoyi.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.KnowledgeRoleGroupBo;
|
||||
import org.ruoyi.domain.vo.KnowledgeRoleGroupVo;
|
||||
import org.ruoyi.service.IKnowledgeRoleGroupService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 知识库角色组
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-07-19
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/knowledgeRoleGroup")
|
||||
public class KnowledgeRoleGroupController extends BaseController {
|
||||
|
||||
private final IKnowledgeRoleGroupService knowledgeRoleGroupService;
|
||||
|
||||
/**
|
||||
* 查询知识库角色组列表
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<KnowledgeRoleGroupVo> list(KnowledgeRoleGroupBo bo, PageQuery pageQuery) {
|
||||
return knowledgeRoleGroupService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出知识库角色组列表
|
||||
*/
|
||||
@Log(title = "知识库角色组", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(KnowledgeRoleGroupBo bo, HttpServletResponse response) {
|
||||
List<KnowledgeRoleGroupVo> list = knowledgeRoleGroupService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "知识库角色组", KnowledgeRoleGroupVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取知识库角色组详细信息
|
||||
*
|
||||
* @param id 主键
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public R<KnowledgeRoleGroupVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long id) {
|
||||
return R.ok(knowledgeRoleGroupService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增知识库角色组
|
||||
*/
|
||||
@Log(title = "知识库角色组", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody KnowledgeRoleGroupBo bo) {
|
||||
return toAjax(knowledgeRoleGroupService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改知识库角色组
|
||||
*/
|
||||
@Log(title = "知识库角色组", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody KnowledgeRoleGroupBo bo) {
|
||||
return toAjax(knowledgeRoleGroupService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库角色组
|
||||
*
|
||||
* @param ids 主键串
|
||||
*/
|
||||
@Log(title = "知识库角色组", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(knowledgeRoleGroupService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.ruoyi.chat.controller.tripartite;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.ruoyi.chat.domain.InsightFace;
|
||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||
import org.ruoyi.chat.util.MjOkHttpUtil;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
/**
|
||||
* 绘画(换脸)任务查询
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Tag(name = "任务查询")
|
||||
@RestController
|
||||
@RequestMapping("/mj")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class FaceController {
|
||||
|
||||
private final IChatCostService chatCostService;
|
||||
|
||||
private final MjOkHttpUtil mjOkHttpUtil;
|
||||
|
||||
@Operation(summary = "换脸")
|
||||
@PostMapping("/insight-face/swap")
|
||||
public String insightFace(@RequestBody InsightFace insightFace) {
|
||||
// 扣除接口费用并且保存消息记录
|
||||
chatCostService.taskDeduct("mj", "Face Changing", 0.0);
|
||||
// 创建请求体(这里使用JSON作为媒体类型)
|
||||
String insightFaceJson = JSONUtil.toJsonStr(insightFace);
|
||||
String url = "mj/insight-face/swap";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, insightFaceJson);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.ruoyi.chat.controller.tripartite;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.chat.config.OkHttpConfig;
|
||||
import org.ruoyi.chat.domain.bo.GenerateLuma;
|
||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||
import org.ruoyi.common.core.utils.OkHttpUtil;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 文生视频
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/luma")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class LumaController {
|
||||
|
||||
private final OkHttpConfig okHttpConfig;
|
||||
private final IChatCostService chatCostService;
|
||||
|
||||
|
||||
@Operation(summary = "文生视频")
|
||||
@PostMapping("/generations/")
|
||||
public String generateVideo(@RequestBody GenerateLuma generateLuma) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("luma");
|
||||
|
||||
chatCostService.taskDeduct("luma", "文生视频", NumberUtils.toDouble(okHttpConfig.getGenerate(), 0.3));
|
||||
String generateJson = JSONUtil.toJsonStr(generateLuma);
|
||||
String url = "luma/generations";
|
||||
Request request = okHttpUtil.createPostRequest(url, generateJson);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "文生视频任务查询")
|
||||
@GetMapping("/generations/{taskId}")
|
||||
public String getGenerationTask(@PathVariable String taskId) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("luma");
|
||||
String url = "luma/generations/" + taskId;
|
||||
Request request = okHttpUtil.createGetRequest(url);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.ruoyi.chat.controller.tripartite;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.ruoyi.chat.domain.dto.*;
|
||||
import org.ruoyi.chat.enums.ActionType;
|
||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||
import org.ruoyi.chat.util.MjOkHttpUtil;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 绘画任务提交
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Tag(name = "任务提交")
|
||||
@RestController
|
||||
@RequestMapping("/mj/submit")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SubmitController {
|
||||
|
||||
private final IChatCostService chatCostService;
|
||||
private final MjOkHttpUtil mjOkHttpUtil;
|
||||
|
||||
@Operation(summary = "绘图变化")
|
||||
@PostMapping("/change")
|
||||
public String change(@RequestBody SubmitChangeDTO changeDTO) {
|
||||
String jsonStr = JSONUtil.toJsonStr(changeDTO);
|
||||
String url = "mj/submit/change";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "执行动作")
|
||||
@PostMapping("/action")
|
||||
public String action(@RequestBody SubmitActionDTO changeDTO) {
|
||||
ActionType actionType = ActionType.fromCustomId(getAction(changeDTO.getCustomId()));
|
||||
Optional.ofNullable(actionType).ifPresentOrElse(
|
||||
type -> {
|
||||
switch (type) {
|
||||
case UP_SAMPLE:
|
||||
chatCostService.taskDeduct("mj", "enlarge", 0.0);
|
||||
break;
|
||||
case IN_PAINT:
|
||||
// 局部重绘已经扣费,不执行任何操作
|
||||
break;
|
||||
default:
|
||||
chatCostService.taskDeduct("mj", "change", 0.0);
|
||||
break;
|
||||
}
|
||||
},
|
||||
() -> chatCostService.taskDeduct("mj", "change", 0.0)
|
||||
);
|
||||
|
||||
String jsonStr = JSONUtil.toJsonStr(changeDTO);
|
||||
String url = "mj/submit/action";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "绘图变化-simple")
|
||||
@PostMapping("/simple-change")
|
||||
public String simpleChange(@RequestBody SubmitSimpleChangeDTO simpleChangeDTO) {
|
||||
String jsonStr = JSONUtil.toJsonStr(simpleChangeDTO);
|
||||
String url = "mj/submit/simple-change";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交图生图、混图任务")
|
||||
@PostMapping("/blend")
|
||||
public String blend(@RequestBody SubmitBlendDTO blendDTO) {
|
||||
chatCostService.taskDeduct("mj", "blend", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(blendDTO);
|
||||
String url = "mj/submit/blend";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交图生文任务")
|
||||
@PostMapping("/describe")
|
||||
public String describe(@RequestBody SubmitDescribeDTO describeDTO) {
|
||||
chatCostService.taskDeduct("mj", "describe", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(describeDTO);
|
||||
String url = "mj/submit/describe";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交文生图任务")
|
||||
@PostMapping("/imagine")
|
||||
public String imagine(@RequestBody SubmitImagineDTO imagineDTO) {
|
||||
chatCostService.taskDeduct("mj", imagineDTO.getPrompt(), 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(imagineDTO);
|
||||
String url = "mj/submit/imagine";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交局部重绘任务")
|
||||
@PostMapping("/modal")
|
||||
public String modal(@RequestBody SubmitModalDTO submitModalDTO) {
|
||||
chatCostService.taskDeduct("mj", "repaint ", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(submitModalDTO);
|
||||
String url = "mj/submit/modal";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "提交提示词分析任务")
|
||||
@PostMapping("/shorten")
|
||||
public String shorten(@RequestBody SubmitShortenDTO submitShortenDTO) {
|
||||
chatCostService.taskDeduct("mj", "shorten", 0.0);
|
||||
String jsonStr = JSONUtil.toJsonStr(submitShortenDTO);
|
||||
String url = "mj/submit/shorten";
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, jsonStr);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
public String getAction(String customId) {
|
||||
if (customId == null || customId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = customId.split("::");
|
||||
return customId.endsWith("SOLO") ? parts[1] : parts[2];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package org.ruoyi.chat.controller.tripartite;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.ruoyi.chat.config.OkHttpConfig;
|
||||
import org.ruoyi.chat.domain.bo.GenerateLyric;
|
||||
import org.ruoyi.chat.domain.bo.GenerateSuno;
|
||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||
import org.ruoyi.common.core.utils.OkHttpUtil;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 文生歌曲任务提交
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/sunoapi")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SunoController {
|
||||
|
||||
private final OkHttpConfig okHttpConfig;
|
||||
private final IChatCostService chatCostService;
|
||||
|
||||
@Operation(summary = "文生歌曲")
|
||||
@PostMapping("/generate")
|
||||
public String generate(@RequestBody GenerateSuno generateSuno) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("suno");
|
||||
// 扣除接口费用并且保存消息记录
|
||||
chatCostService.taskDeduct("suno", "文生歌曲", NumberUtils.toDouble(okHttpConfig.getGenerate(), 0.3));
|
||||
// 创建请求体(这里使用JSON作为媒体类型)
|
||||
String generateJson = JSONUtil.toJsonStr(generateSuno);
|
||||
String url = "suno/generate";
|
||||
Request request = okHttpUtil.createPostRequest(url, generateJson);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "生成歌词")
|
||||
@PostMapping("/generate/lyrics/")
|
||||
public String generate(@RequestBody GenerateLyric generateLyric) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("suno");
|
||||
String generateJson = JSONUtil.toJsonStr(generateLyric);
|
||||
String url = "task/suno/v1/submit/lyrics";
|
||||
Request request = okHttpUtil.createPostRequest(url, generateJson);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "查询歌词任务")
|
||||
@GetMapping("/lyrics/{taskId}")
|
||||
public String lyrics(@PathVariable String taskId) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("suno");
|
||||
String url = "task/suno/v1/fetch/" + taskId;
|
||||
Request request = okHttpUtil.createGetRequest(url);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "查询歌曲任务")
|
||||
@GetMapping("/feed/{taskId}")
|
||||
public String feed(@PathVariable String taskId) {
|
||||
OkHttpUtil okHttpUtil = okHttpConfig.getOkHttpUtil("suno");
|
||||
String url = "suno/feed/" + taskId;
|
||||
Request request = okHttpUtil.createGetRequest(url);
|
||||
return okHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.ruoyi.chat.controller.tripartite;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import org.ruoyi.chat.domain.dto.TaskConditionDTO;
|
||||
import org.ruoyi.chat.util.MjOkHttpUtil;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 绘画任务查询
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-05-03
|
||||
*/
|
||||
@Tag(name = "任务查询")
|
||||
@RestController
|
||||
@RequestMapping("/mj/task")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TaskController {
|
||||
|
||||
private final MjOkHttpUtil mjOkHttpUtil;
|
||||
|
||||
@Operation(summary = "指定ID获取任务")
|
||||
@GetMapping("/{id}/fetch")
|
||||
public String fetch(@Parameter(description = "任务ID") @PathVariable String id) {
|
||||
String url = "mj/task/" + id + "/fetch";
|
||||
Request request = mjOkHttpUtil.createGetRequest(url);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID列表查询任务")
|
||||
@PostMapping("/list-by-condition")
|
||||
public String listByIds(@RequestBody TaskConditionDTO conditionDTO) {
|
||||
String url = "mj/task/list-by-condition";
|
||||
String conditionJson = JSONUtil.toJsonStr(conditionDTO);
|
||||
Request request = mjOkHttpUtil.createPostRequest(url, conditionJson);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取任务图片的seed")
|
||||
@GetMapping("/{id}/image-seed")
|
||||
public String getSeed(@Parameter(description = "任务ID") @PathVariable String id) {
|
||||
String url = "mj/task/" + id + "/image-seed";
|
||||
Request request = mjOkHttpUtil.createGetRequest(url);
|
||||
return mjOkHttpUtil.executeRequest(request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package org.ruoyi.chat.domain;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class DomainObject implements Serializable {
|
||||
@JsonIgnore
|
||||
private final transient Object lock = new Object();
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "ID")
|
||||
protected String id;
|
||||
@Setter
|
||||
protected Map<String, Object> properties; // 扩展属性,仅支持基本类型
|
||||
|
||||
public void sleep() throws InterruptedException {
|
||||
synchronized (this.lock) {
|
||||
this.lock.wait();
|
||||
}
|
||||
}
|
||||
|
||||
public void awake() {
|
||||
synchronized (this.lock) {
|
||||
this.lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public DomainObject setProperty(String name, Object value) {
|
||||
getProperties().put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DomainObject removeProperty(String name) {
|
||||
getProperties().remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getProperty(String name) {
|
||||
return getProperties().get(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getPropertyGeneric(String name) {
|
||||
return (T) getProperty(name);
|
||||
}
|
||||
|
||||
public <T> T getProperty(String name, Class<T> clz) {
|
||||
return getProperty(name, clz, null);
|
||||
}
|
||||
|
||||
public <T> T getProperty(String name, Class<T> clz, T defaultValue) {
|
||||
Object value = getProperty(name);
|
||||
return value == null ? defaultValue : clz.cast(value);
|
||||
}
|
||||
|
||||
public Map<String, Object> getProperties() {
|
||||
if (this.properties == null) {
|
||||
this.properties = new HashMap<>();
|
||||
}
|
||||
return this.properties;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.ruoyi.chat.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author WangLe
|
||||
*/
|
||||
@Data
|
||||
@Schema(name = "Discord账号")
|
||||
public class InsightFace implements Serializable {
|
||||
/**
|
||||
* 本人头像json
|
||||
*/
|
||||
@Schema(description = "本人头像json")
|
||||
private String sourceBase64;
|
||||
|
||||
/**
|
||||
* 明星头像json
|
||||
*/
|
||||
@Schema(description = "明星头像json")
|
||||
private String targetBase64;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.ruoyi.chat.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文生视频请求对象
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* date 2024/6/27
|
||||
*/
|
||||
@Data
|
||||
public class GenerateLuma {
|
||||
|
||||
private String aspect_ratio;
|
||||
|
||||
private boolean expand_prompt;
|
||||
|
||||
private String image_url;
|
||||
|
||||
private String user_prompt;
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.ruoyi.chat.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 生成歌词
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* date 2024/6/27
|
||||
*/
|
||||
@Data
|
||||
public class GenerateLyric {
|
||||
|
||||
/**
|
||||
* 歌词提示词
|
||||
*/
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
private String notify_hook;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package org.ruoyi.chat.domain.bo;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author WangLe
|
||||
*/
|
||||
@Data
|
||||
public class GenerateSuno implements Serializable {
|
||||
|
||||
/**
|
||||
* 歌词 (自定义模式专用)
|
||||
*/
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* mv模型,chirp-v3-0、chirp-v3-5。不写默认 chirp-v3-0
|
||||
*/
|
||||
private String mv;
|
||||
|
||||
/**
|
||||
* 标题(自定义模式专用)
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 风格标签(自定义模式专用)
|
||||
*/
|
||||
private String tags;
|
||||
|
||||
/**
|
||||
* 是否生成纯音乐,true 为生成纯音乐
|
||||
*/
|
||||
private boolean make_instrumental;
|
||||
|
||||
/**
|
||||
* 任务id,用于对之前的任务再操作
|
||||
*/
|
||||
private String task_id;
|
||||
|
||||
/**
|
||||
* float,歌曲延长时间,单位秒
|
||||
*/
|
||||
private int continue_at;
|
||||
|
||||
/**
|
||||
* 歌曲id,需要续写哪首歌
|
||||
*/
|
||||
private String continue_clip_id;
|
||||
|
||||
/**
|
||||
* 灵感模式提示词(灵感模式专用)
|
||||
*/
|
||||
private String gpt_description_prompt;
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
private String notify_hook;
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class BaseSubmitDTO {
|
||||
|
||||
@Schema(description = "自定义参数")
|
||||
protected String state;
|
||||
|
||||
@Schema(description = "回调地址, 为空时使用全局notifyHook")
|
||||
protected String notifyHook;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
@Schema(name = "变化任务提交参数")
|
||||
public class SubmitActionDTO {
|
||||
|
||||
private String customId;
|
||||
|
||||
private String taskId;
|
||||
|
||||
private String state;
|
||||
|
||||
private String notifyHook;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.chat.enums.BlendDimensions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(name = "Blend提交参数")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SubmitBlendDTO extends BaseSubmitDTO {
|
||||
|
||||
@ArraySchema(arraySchema = @Schema(description = "图片base64数组", requiredMode = Schema.RequiredMode.REQUIRED), schema = @Schema(example = "data:image/png;base64,xxx1"))
|
||||
private List<String> base64Array;
|
||||
|
||||
@Schema(description = "比例: PORTRAIT(2:3); SQUARE(1:1); LANDSCAPE(3:2)", example = "SQUARE")
|
||||
private BlendDimensions dimensions = BlendDimensions.SQUARE;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.chat.enums.TaskAction;
|
||||
|
||||
|
||||
@Data
|
||||
@Schema(name = "变化任务提交参数")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SubmitChangeDTO extends BaseSubmitDTO {
|
||||
|
||||
@Schema(description = "任务ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "\"1320098173412546\"")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "UPSCALE(放大); VARIATION(变换); REROLL(重新生成)", requiredMode = Schema.RequiredMode.REQUIRED, allowableValues = {"UPSCALE", "VARIATION", "REROLL"}, example = "UPSCALE")
|
||||
private TaskAction action;
|
||||
|
||||
@Schema(description = "序号(1~4), action为UPSCALE,VARIATION时必传", minimum = "1", maximum = "4", example = "1")
|
||||
private Integer index;
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@Schema(name = "Describe提交参数")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SubmitDescribeDTO extends BaseSubmitDTO {
|
||||
|
||||
@Schema(description = "图片base64", requiredMode = Schema.RequiredMode.REQUIRED, example = "data:image/png;base64,xxx")
|
||||
private String base64;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Data
|
||||
@Schema(name = "Imagine提交参数")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SubmitImagineDTO extends BaseSubmitDTO {
|
||||
|
||||
@Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "Cat")
|
||||
private String prompt;
|
||||
|
||||
@Schema(description = "垫图base64数组")
|
||||
private List<String> base64Array;
|
||||
|
||||
@Schema(hidden = true)
|
||||
@Deprecated(since = "3.0", forRemoval = true)
|
||||
private String base64;
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "局部重绘提交参数")
|
||||
public class SubmitModalDTO extends BaseSubmitDTO {
|
||||
|
||||
private String maskBase64;
|
||||
|
||||
private String taskId;
|
||||
|
||||
private String prompt;
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "prompt分析提交参数")
|
||||
public class SubmitShortenDTO extends BaseSubmitDTO {
|
||||
|
||||
private String botType;
|
||||
|
||||
private String prompt;
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
|
||||
@Data
|
||||
@Schema(name = "变化任务提交参数-simple")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SubmitSimpleChangeDTO extends BaseSubmitDTO {
|
||||
|
||||
@Schema(description = "变化描述: ID $action$index", requiredMode = Schema.RequiredMode.REQUIRED, example = "1320098173412546 U2")
|
||||
private String content;
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.ruoyi.chat.domain.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(name = "任务查询参数")
|
||||
public class TaskConditionDTO {
|
||||
|
||||
@ArraySchema(arraySchema = @Schema(description = "任务ID列表"), schema = @Schema(example = "1320098173412546"))
|
||||
private List<String> ids;
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.ruoyi.chat.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author WangLe
|
||||
*/
|
||||
@Getter
|
||||
public enum ActionType {
|
||||
IN_PAINT("Inpaint"), // 局部重绘操作
|
||||
RE_ROLL("reroll"), // 重绘操作
|
||||
UP_SAMPLE("upsample"), // 放大操作
|
||||
ZOOM("zoom"), // 变焦操作
|
||||
UPSCALE("upscale"), // 高清放大操作
|
||||
VARIATION("variation"); // 变化操作
|
||||
|
||||
private final String action;
|
||||
|
||||
ActionType(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public static ActionType fromCustomId(String customId) {
|
||||
for (ActionType type : values()) {
|
||||
if (type.getAction().equals(customId)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.ruoyi.chat.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum BillingType {
|
||||
TOKEN("1", "token扣费"),
|
||||
TIMES("2", "次数扣费");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
BillingType(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static BillingType fromCode(String code) {
|
||||
for (BillingType type : values()) {
|
||||
if (type.getCode().equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.ruoyi.chat.enums;
|
||||
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum BlendDimensions {
|
||||
|
||||
PORTRAIT("2:3"),
|
||||
|
||||
SQUARE("1:1"),
|
||||
|
||||
LANDSCAPE("3:2");
|
||||
|
||||
private final String value;
|
||||
|
||||
BlendDimensions(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user