v3.0.0 init

This commit is contained in:
ageerle
2026-02-06 03:00:23 +08:00
parent eb2e8f3ff8
commit 7b8cfe02a1
1524 changed files with 53132 additions and 58866 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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_\\.]+$");
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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];
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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