mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-09-13 08:25:00 +00:00
Merge upstream/main into feature/common-trace-rag-chat
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.agentic.Agent;
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
|
||||
/**
|
||||
* 闲聊兜底 Agent
|
||||
* 负责问候、日常闲聊和常识性问答,不挂任何工具。
|
||||
* 作为 supervisor 的默认落脚点,避免简单任务无子 Agent 可用导致输出为空。
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
*/
|
||||
public interface ChitChatAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
你是一个友好、自然的对话助手,负责问候、闲聊和常识性问答。
|
||||
要求:
|
||||
- 用与用户相同的语言回答,简洁自然
|
||||
- 不要编造需要实时数据或专业工具才能得到的事实
|
||||
- 如果用户的问题实际需要联网搜索、查数据库、执行技能或生成图表,直接说明这超出你的职责,
|
||||
让用户重新描述需求
|
||||
""")
|
||||
@UserMessage("{{query}}")
|
||||
@Agent("闲聊兜底助手:仅用于问候、日常闲聊和不需要联网搜索、数据库、技能或图表的通用问题")
|
||||
String chat(@V("query") String query);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.ruoyi.agent;
|
||||
|
||||
import dev.langchain4j.service.SystemMessage;
|
||||
import dev.langchain4j.service.UserMessage;
|
||||
import dev.langchain4j.service.V;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaScriptResult;
|
||||
|
||||
/**
|
||||
* 短剧剧本打磨 Agent —— 使用 langchain4j AiServices 结构化输出
|
||||
* <p>
|
||||
* 框架自动生成 JSON Schema 并强制 LLM 返回符合结构的数据,无需手工 parse。
|
||||
*
|
||||
* @author ageerle
|
||||
*/
|
||||
public interface ShortDramaScriptAgent {
|
||||
|
||||
@SystemMessage("""
|
||||
你是顶级短剧编剧和创意总监。请根据用户的一个创意想法,创作完整的短剧剧本。
|
||||
|
||||
【核心原则 - 最高优先级】
|
||||
1. 剧本必须完整、有张力、有画面感
|
||||
2. 角色要有鲜明性格,不是工具人
|
||||
3. 情节紧凑,每句台词都推动剧情
|
||||
4. 场景描写具体,让分镜师能直接画出画面
|
||||
5. 对话自然有力,避免废话
|
||||
|
||||
【剧本格式】
|
||||
使用标准剧本格式,包含以下元素:
|
||||
1. 场景头(Scene Heading):内景/外景+地点+时间,如"内景 客厅 清晨"
|
||||
2. 场景描述(Scene Description):简洁描述场景环境、布局、关键道具
|
||||
3. 动作描述(Action):描述角色的动作、表情、行为,连续段落形式
|
||||
4. 对话(Dialogue):角色名: 台词内容
|
||||
5. 画外音(Voiceover):旁白、独白、回忆中的声音
|
||||
|
||||
【剧本长度要求】
|
||||
- scriptText:1000-3000字,含完整的开场、冲突发展、高潮、结尾
|
||||
- outlineText:400-800字,概述完整故事线
|
||||
|
||||
【角色塑造要求】
|
||||
- 每个角色要有明确的性格标签(如:霸道总裁、温柔女医、腹黑谋士)
|
||||
- 角色之间要有清晰的关系和冲突
|
||||
- 对话要符合角色性格
|
||||
|
||||
【情节要求】
|
||||
- 必须有清晰的冲突和反转
|
||||
- 情绪节奏要有起伏(紧张→舒缓→爆发)
|
||||
- 结尾要有记忆点(反转/留白/情感升华)
|
||||
""")
|
||||
@UserMessage("用户期望项目名:{{projectName}}\n用户创意:{{idea}}")
|
||||
ShortDramaScriptResult polish(@V("projectName") String projectName, @V("idea") String idea);
|
||||
}
|
||||
@@ -30,6 +30,9 @@ public interface SqlAgent {
|
||||
- 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
|
||||
- If queryAllTables returns NO tables or an empty list, you MUST NOT call executeSql or queryTableSchema
|
||||
- When no tables are available, inform the user: "当前未配置可查询的数据库表,请联系管理员配置"
|
||||
- NEVER attempt to execute any SQL query (including SELECT * FROM xxx) without first confirming available tables
|
||||
""")
|
||||
@UserMessage("""
|
||||
Answer the following question: {{query}}
|
||||
|
||||
@@ -8,9 +8,14 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.ruoyi.agent.manager.TableSchemaManager;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -54,6 +59,22 @@ public class ExecuteSqlQueryTool implements BuiltinToolProvider {
|
||||
return "Error: Only SELECT queries are allowed for security reasons";
|
||||
}
|
||||
|
||||
// 校验表白名单:未配置表时直接拒绝,已配置则校验 SQL 中引用的表
|
||||
TableSchemaManager schemaManager = SpringUtils.getBean(TableSchemaManager.class);
|
||||
List<String> allowedTables = schemaManager.getAllowedTableNames();
|
||||
if (allowedTables.isEmpty()) {
|
||||
return "Error: 当前未配置可查询的数据库表,无法执行任何SQL查询。请联系管理员配置 AGENT_ALLOWED_TABLES";
|
||||
}
|
||||
Set<String> allowedSet = allowedTables.stream()
|
||||
.map(String::toLowerCase)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> referencedTables = extractTableNames(upperSql);
|
||||
for (String table : referencedTables) {
|
||||
if (!allowedSet.contains(table.toLowerCase())) {
|
||||
return "Error: 表 " + table + " 不在允许查询的表列表中。允许查询的表: " + String.join(", ", allowedTables);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
DataSource dataSource = getDataSource();
|
||||
if (dataSource == null) {
|
||||
@@ -99,6 +120,21 @@ public class ExecuteSqlQueryTool implements BuiltinToolProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 SQL 中提取引用的表名(FROM / JOIN 后的标识符)
|
||||
* 覆盖 FROM t1, t2 / FROM t1 JOIN t2 / FROM `t1` 等常见写法
|
||||
*/
|
||||
private Set<String> extractTableNames(String upperSql) {
|
||||
Set<String> tables = new java.util.HashSet<>();
|
||||
// 匹配 FROM 或 JOIN 后面的表名(支持反引号包裹)
|
||||
Pattern pattern = Pattern.compile("(?:FROM|JOIN)\\s+`?([A-Z0-9_]+)`?", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(upperSql);
|
||||
while (matcher.find()) {
|
||||
tables.add(matcher.group(1));
|
||||
}
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化查询结果
|
||||
* 返回清晰的表格格式,展示关键数据
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.ruoyi.config.agent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* 磁盘 Skills 目录路径解析器
|
||||
* <p>
|
||||
* langchain4j 的 ShellSkills 通过 FileSystemSkillLoader 从磁盘加载 SKILL.md,
|
||||
* 路径硬编码在 ChatServiceFacade 中。抽到此工具类供智能体管理端与聊天流程共用,
|
||||
* 避免两处路径漂移。
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
public final class SkillsPathResolver {
|
||||
|
||||
private SkillsPathResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* skills 目录相对项目根目录的路径
|
||||
*/
|
||||
private static final String SKILLS_RELATIVE_PATH = "ruoyi-admin/src/main/resources/skills";
|
||||
|
||||
/**
|
||||
* 返回磁盘 skills 目录的绝对路径
|
||||
*/
|
||||
public static Path resolveSkillsPath() {
|
||||
String userDir = System.getProperty("user.dir");
|
||||
return Path.of(userDir, SKILLS_RELATIVE_PATH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.ruoyi.constant;
|
||||
|
||||
/**
|
||||
* 短剧图片资产常量 — 三视图 prompt 工程
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public final class ShortDramaImageConstants {
|
||||
|
||||
private ShortDramaImageConstants() {}
|
||||
|
||||
/**
|
||||
* 角色三视图 prompt 前缀,生图时自动拼到用户 prompt 之前。
|
||||
* 把风格和构图指令放最前面,避免被角色描述稀释导致画风漂移。
|
||||
*/
|
||||
public static final String CHARACTER_PROMPT_PREFIX =
|
||||
"character design sheet, multiple views reference sheet, " +
|
||||
"front view / side view / back view full body, " +
|
||||
"clean white background, no props, no text. ";
|
||||
|
||||
/**
|
||||
* 角色三视图 prompt 后缀,生图时自动追加到用户 prompt 之后。
|
||||
* 左侧1/3正面特写 + 右侧2/3三视图横向排列(正面全身、侧面全身、背面全身)。
|
||||
*/
|
||||
public static final String CHARACTER_PROMPT_SUFFIX =
|
||||
"。角色设定图,画面分为左右两个区域:" +
|
||||
"【左侧区域】占约1/3宽度,是角色的正面特写" +
|
||||
"(完整正脸,最具辨识度的正面形态);" +
|
||||
"【右侧区域】占约2/3宽度,是角色三视图横向排列" +
|
||||
"(从左到右依次为:正面全身、侧面全身、背面全身)," +
|
||||
"三视图高度一致。纯白色背景,无其他元素。";
|
||||
|
||||
/** 场景图 prompt 前缀 */
|
||||
public static final String LOCATION_PROMPT_PREFIX = "宽广空间全景,";
|
||||
|
||||
/** 场景图 prompt 后缀 */
|
||||
public static final String LOCATION_PROMPT_SUFFIX = ",禁止出现任何角色,纯背景板";
|
||||
|
||||
/** 每个资产最多保留的图片变体数量 */
|
||||
public static final int MAX_IMAGE_VARIANTS = 20;
|
||||
|
||||
// ==================== 视觉风格 ====================
|
||||
|
||||
/** 项目视觉风格 → 生图 prompt 后缀映射,确保同项目所有图片风格一致 */
|
||||
public static final java.util.Map<String, String> ART_STYLE_PROMPTS = java.util.Map.of(
|
||||
"american-comic", "美式漫画风格,粗线条,高饱和度色彩,强烈光影对比",
|
||||
"chinese-comic", "现代国漫动画风格,Chinese donghua 2D comic style,赛璐璐平涂上色,干净锐利的黑色线稿,平面化光影无真实景深,动漫人物比例(略放大双眼、修长身形),皮肤平滑无毛孔无写实肤质,国风服饰剪裁与材质细节清晰,色彩饱满通透,画面精致干净;禁止真人写实、摄影实拍、3D渲染、CGI、厚涂油画、写实皮肤纹理、景深虚化",
|
||||
"japanese-anime", "现代日系动漫风格,赛璐璐上色,清晰干净的线条,视觉小说CG感,高质量2D风格",
|
||||
"realistic", "真实电影级画面质感,真实现实场景,色彩饱满通透,画面干净精致,真实感"
|
||||
);
|
||||
|
||||
/** 默认视觉风格 */
|
||||
public static final String DEFAULT_ART_STYLE = "realistic";
|
||||
|
||||
/** 查找 artStyle 对应的 prompt 后缀,找不到返回空字符串 */
|
||||
public static String artStylePrompt(String artStyle) {
|
||||
if (artStyle == null) return "";
|
||||
return ART_STYLE_PROMPTS.getOrDefault(artStyle, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 由项目 id 派生一个稳定的生图随机种子。
|
||||
* 同项目内所有角色/形象共用同一颗种子,渲染基调(光影、配色、笔触)更趋一致;
|
||||
* 不同项目派生不同种子,避免跨项目撞图。返回值落在 [0, 2_000_000_000),兼容各供应商。
|
||||
*/
|
||||
public static Integer styleSeed(Long projectId) {
|
||||
if (projectId == null) return null;
|
||||
long h = projectId;
|
||||
h ^= (h >>> 32);
|
||||
return (int) Math.floorMod(h * 2654435761L, 2_000_000_000L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.ruoyi.controller.agent;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
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.mybatis.core.page.PageQuery;
|
||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||
import org.ruoyi.common.web.core.BaseController;
|
||||
import org.ruoyi.domain.bo.agent.AgentBo;
|
||||
import org.ruoyi.domain.vo.agent.AgentVo;
|
||||
import org.ruoyi.domain.vo.agent.SkillOptionVo;
|
||||
import org.ruoyi.service.agent.IAgentService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 智能体管理 Controller
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/agent/agent")
|
||||
public class AgentController extends BaseController {
|
||||
|
||||
private final IAgentService agentService;
|
||||
|
||||
/**
|
||||
* 分页查询智能体列表
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<AgentVo> list(AgentBo bo, PageQuery pageQuery) {
|
||||
return agentService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询智能体列表(不分页,用于导出)
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:list")
|
||||
@GetMapping("/queryList")
|
||||
public R<List<AgentVo>> queryList(AgentBo bo) {
|
||||
return R.ok(agentService.queryList(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出智能体列表
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:export")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(AgentBo bo, HttpServletResponse response) {
|
||||
List<AgentVo> list = agentService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "智能体", AgentVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取智能体详情
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<AgentVo> getInfo(@PathVariable Long id) {
|
||||
return R.ok(agentService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增智能体
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:add")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit
|
||||
@PostMapping
|
||||
public R<Void> add(@Validated @RequestBody AgentBo bo) {
|
||||
return toAjax(agentService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改智能体
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:edit")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit
|
||||
@PutMapping
|
||||
public R<Void> edit(@Validated @RequestBody AgentBo bo) {
|
||||
return toAjax(agentService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除智能体
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:remove")
|
||||
@Log(title = "智能体管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@PathVariable Long[] ids) {
|
||||
return toAjax(agentService.deleteByIds(List.of(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户端聊天页智能体下拉选项(启用状态,不需权限校验)
|
||||
*/
|
||||
@GetMapping("/agentOptions")
|
||||
public R<List<AgentVo>> agentOptions() {
|
||||
return R.ok(agentService.queryEnabledOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出磁盘上可用的 Skills(供管理端表单勾选)
|
||||
*/
|
||||
@SaCheckPermission("agent:agent:list")
|
||||
@GetMapping("/skillOptions")
|
||||
public R<List<SkillOptionVo>> skillOptions() {
|
||||
return R.ok(agentService.listSkillOptions());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ModelBatchKeyBo;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
import org.ruoyi.enums.ChatModeType;
|
||||
import org.ruoyi.enums.ModelType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -54,7 +56,9 @@ public class ChatModelController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/modelList")
|
||||
public R<List<ChatModelVo>> modelList(ChatModelBo bo) {
|
||||
bo.setCategory(ModelType.CHAT.getKey());
|
||||
if (StringUtils.isBlank(bo.getCategory())) {
|
||||
bo.setCategory(ModelType.CHAT.getKey());
|
||||
}
|
||||
return R.ok(chatModelService.queryList(bo));
|
||||
}
|
||||
|
||||
@@ -118,6 +122,17 @@ public class ChatModelController extends BaseController {
|
||||
return toAjax(chatModelService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按厂商批量更新密钥
|
||||
*/
|
||||
@SaCheckPermission("system:model:edit")
|
||||
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping("/batchKeyByProvider")
|
||||
public R<Void> batchKeyByProvider(@Validated @RequestBody ModelBatchKeyBo bo) {
|
||||
return toAjax(chatModelService.updateApiKeyByProvider(bo.getProviderCode(), bo.getApiKey()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除模型管理
|
||||
*
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.ruoyi.controller.chat;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.entity.audio.AudioContext;
|
||||
import org.ruoyi.common.chat.entity.image.ImageContext;
|
||||
import org.ruoyi.common.chat.entity.media.MediaGenerationResponse;
|
||||
import org.ruoyi.common.chat.entity.video.VideoContext;
|
||||
import org.ruoyi.common.chat.factory.AudioServiceFactory;
|
||||
import org.ruoyi.common.chat.factory.ImageServiceFactory;
|
||||
import org.ruoyi.common.chat.factory.VideoServiceFactory;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.domain.bo.media.ImageGenerationRequest;
|
||||
import org.ruoyi.domain.bo.media.SpeechGenerationRequest;
|
||||
import org.ruoyi.domain.bo.media.VideoGenerationRequest;
|
||||
import org.ruoyi.enums.ModelType;
|
||||
import org.ruoyi.service.media.AtlasPredictionService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/media")
|
||||
public class MediaGenerationController {
|
||||
|
||||
private final IChatModelService chatModelService;
|
||||
private final AudioServiceFactory audioServiceFactory;
|
||||
private final ImageServiceFactory imageServiceFactory;
|
||||
private final VideoServiceFactory videoServiceFactory;
|
||||
private final AtlasPredictionService atlasPredictionService;
|
||||
|
||||
@PostMapping("/speech")
|
||||
public R<MediaGenerationResponse> speech(@Valid @RequestBody SpeechGenerationRequest request) {
|
||||
ChatModelVo model = loadModel(request.getModel(), ModelType.AUDIO.getKey());
|
||||
MediaGenerationResponse response = audioServiceFactory.getOriginalService(model.getProviderCode())
|
||||
.generateSpeech(AudioContext.builder()
|
||||
.chatModelVo(model)
|
||||
.input(request.getInput())
|
||||
.voice(request.getVoice())
|
||||
.responseFormat(request.getResponseFormat())
|
||||
.speed(request.getSpeed())
|
||||
.instructions(request.getInstructions())
|
||||
.build());
|
||||
return R.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/image")
|
||||
public R<MediaGenerationResponse> image(@Valid @RequestBody ImageGenerationRequest request) {
|
||||
ChatModelVo model = loadModel(request.getModel(), ModelType.IMAGE.getKey());
|
||||
String result = imageServiceFactory.getOriginalService(model.getProviderCode())
|
||||
.generateImage(ImageContext.builder()
|
||||
.chatModelVo(model)
|
||||
.prompt(request.getPrompt())
|
||||
.size(request.getSize())
|
||||
.seed(request.getSeed())
|
||||
.build());
|
||||
return R.ok(toImageResponse(result));
|
||||
}
|
||||
|
||||
@PostMapping("/video")
|
||||
public R<MediaGenerationResponse> video(@Valid @RequestBody VideoGenerationRequest request) {
|
||||
ChatModelVo model = loadModel(request.getModel(), ModelType.VIDEO.getKey());
|
||||
MediaGenerationResponse response = videoServiceFactory.getOriginalService(model.getProviderCode())
|
||||
.generateVideo(VideoContext.builder()
|
||||
.chatModelVo(model)
|
||||
.prompt(request.getPrompt())
|
||||
.size(request.getSize())
|
||||
.seconds(request.getSeconds())
|
||||
.quality(request.getQuality())
|
||||
.build());
|
||||
return R.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping("/video")
|
||||
public R<MediaGenerationResponse> videoResult(@NotBlank(message = "模型不能为空") @RequestParam String model,
|
||||
@NotBlank(message = "videoId不能为空") @RequestParam String videoId) {
|
||||
ChatModelVo chatModelVo = loadModel(model, ModelType.VIDEO.getKey());
|
||||
MediaGenerationResponse response = videoServiceFactory.getOriginalService(chatModelVo.getProviderCode())
|
||||
.retrieveVideo(VideoContext.builder()
|
||||
.chatModelVo(chatModelVo)
|
||||
.prompt("retrieve")
|
||||
.videoId(videoId)
|
||||
.build());
|
||||
return R.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping("/prediction")
|
||||
public R<MediaGenerationResponse> prediction(@NotBlank(message = "模型不能为空") @RequestParam String model,
|
||||
@NotBlank(message = "predictionId不能为空") @RequestParam String predictionId) {
|
||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(model);
|
||||
if (chatModelVo == null) {
|
||||
throw new IllegalArgumentException("未找到模型配置: " + model);
|
||||
}
|
||||
return R.ok(atlasPredictionService.retrieve(chatModelVo, predictionId));
|
||||
}
|
||||
|
||||
private ChatModelVo loadModel(String modelName, String category) {
|
||||
ChatModelVo model = chatModelService.selectModelByName(modelName);
|
||||
if (model == null) {
|
||||
throw new IllegalArgumentException("未找到模型配置: " + modelName);
|
||||
}
|
||||
if (!category.equals(model.getCategory())) {
|
||||
throw new IllegalArgumentException("模型分类不匹配,期望: " + category + ", 实际: " + model.getCategory());
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private MediaGenerationResponse toImageResponse(String result) {
|
||||
if (result != null && result.startsWith("{")) {
|
||||
try {
|
||||
return atlasPredictionService.toResponse(result, "image");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("图片生成响应解析失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (result != null && result.startsWith("data:")) {
|
||||
String mimeType = result.substring("data:".length(), result.indexOf(";base64,"));
|
||||
String b64 = result.substring(result.indexOf(";base64,") + ";base64,".length());
|
||||
return MediaGenerationResponse.builder()
|
||||
.type("image")
|
||||
.mimeType(mimeType)
|
||||
.b64Json(b64)
|
||||
.dataUrl(result)
|
||||
.build();
|
||||
}
|
||||
return MediaGenerationResponse.builder()
|
||||
.type("image")
|
||||
.mimeType("image/png")
|
||||
.url(result)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.ruoyi.controller.coding;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.domain.bo.coding.CodingRequestBo;
|
||||
import org.ruoyi.enums.ModelType;
|
||||
import org.ruoyi.service.coding.CodingWorkspaceService;
|
||||
import org.ruoyi.service.coding.ICodingService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 编程能力接口(B 路径,不走 Supervisor 调度)
|
||||
*
|
||||
* <p>第一阶段 {@code /coding/**} 在 {@code application.yml} 的 security.excludes 中,
|
||||
* 免鉴权直连。Controller 只做参数绑定 + 同步取 userId(Sa-Token 异步上下文丢失,
|
||||
* 见 SecurityConfig 注释)+ 转发 Service。
|
||||
*
|
||||
* @author ageerle
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/coding")
|
||||
public class CodingController {
|
||||
|
||||
private final ICodingService codingService;
|
||||
private final CodingWorkspaceService workspaceService;
|
||||
private final IChatModelService chatModelService;
|
||||
|
||||
/**
|
||||
* 编程对话(SSE 流式)
|
||||
*
|
||||
* @param bo 请求参数(prompt / model / workspacePath)
|
||||
* @return SseEmitter
|
||||
*/
|
||||
@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter chat(@Valid @RequestBody CodingRequestBo bo) {
|
||||
// 同步线程取 userId;第一阶段免鉴权,可能为 null
|
||||
Long userId = LoginHelper.getUserId();
|
||||
return codingService.chat(bo, userId);
|
||||
}
|
||||
|
||||
@GetMapping("/workspace")
|
||||
public R<CodingWorkspaceService.WorkspaceResult> workspace(
|
||||
@RequestParam(required = false) String workspacePath) throws Exception {
|
||||
return R.ok(workspaceService.list(workspacePath));
|
||||
}
|
||||
|
||||
@GetMapping("/models")
|
||||
public R<List<ModelOption>> models() {
|
||||
// 编程对话只能用聊天模型,按 category=chat 过滤
|
||||
ChatModelBo bo = new ChatModelBo();
|
||||
bo.setCategory(ModelType.CHAT.getKey());
|
||||
List<ModelOption> models = chatModelService.queryList(bo).stream()
|
||||
.map(model -> new ModelOption(model.getId(), model.getModelName(), model.getProviderCode()))
|
||||
.toList();
|
||||
return R.ok(models);
|
||||
}
|
||||
|
||||
@GetMapping("/file")
|
||||
public R<CodingWorkspaceService.FileContent> file(
|
||||
@RequestParam(required = false) String workspacePath,
|
||||
@RequestParam String path) throws Exception {
|
||||
return R.ok(workspaceService.read(workspacePath, path));
|
||||
}
|
||||
|
||||
@PutMapping("/file")
|
||||
public R<CodingWorkspaceService.FileContent> saveFile(@RequestBody FileWriteRequest request) throws Exception {
|
||||
return R.ok(workspaceService.write(request.workspacePath(), request.path(), request.content()));
|
||||
}
|
||||
|
||||
@PostMapping("/command")
|
||||
public R<CodingWorkspaceService.CommandResult> command(@RequestBody CommandRequest request) {
|
||||
return R.ok(workspaceService.execute(request.workspacePath(), request.command()));
|
||||
}
|
||||
|
||||
public record FileWriteRequest(String workspacePath, String path, String content) { }
|
||||
public record CommandRequest(String workspacePath, String command) { }
|
||||
public record ModelOption(Long id, String name, String provider) { }
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.domain.bo.knowledge.KnowledgeAttachBo;
|
||||
import org.ruoyi.domain.bo.knowledge.KnowledgeInfoUploadBo;
|
||||
import org.ruoyi.domain.vo.knowledge.KnowledgeAttachVo;
|
||||
import org.ruoyi.domain.vo.knowledge.KnowledgeReparseVo;
|
||||
import org.ruoyi.service.knowledge.IKnowledgeAttachService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -106,7 +107,10 @@ public class KnowledgeAttachController extends BaseController {
|
||||
|
||||
/**
|
||||
* 上传知识库附件
|
||||
* 注意:multipart 上传不能加 @RepeatSubmit(其参数序列化不支持 MultipartFile)
|
||||
*/
|
||||
@SaCheckPermission("system:attach:add")
|
||||
@Log(title = "知识库附件", businessType = BusinessType.INSERT)
|
||||
@PostMapping(value = "/upload")
|
||||
public R<String> upload(KnowledgeInfoUploadBo bo){
|
||||
knowledgeAttachService.upload(bo);
|
||||
@@ -118,9 +122,20 @@ public class KnowledgeAttachController extends BaseController {
|
||||
*
|
||||
* @param id 附件ID
|
||||
*/
|
||||
@SaCheckPermission("system:attach:edit")
|
||||
@Log(title = "知识库附件", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/parse/{id}")
|
||||
@RepeatSubmit()
|
||||
public R<Void> parse(@PathVariable Long id) {
|
||||
knowledgeAttachService.parse(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@SaCheckPermission("system:attach:edit")
|
||||
@Log(title = "知识库附件批量重新解析", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/reparse/knowledge/{knowledgeId}")
|
||||
@RepeatSubmit()
|
||||
public R<KnowledgeReparseVo> reparseKnowledge(@PathVariable Long knowledgeId) {
|
||||
return R.ok(knowledgeAttachService.reparseKnowledge(knowledgeId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,9 @@ public class KnowledgeFragmentController extends BaseController {
|
||||
/**
|
||||
* 检索测试
|
||||
*/
|
||||
@SaCheckPermission("system:fragment:list")
|
||||
@PostMapping("/retrieval")
|
||||
@RepeatSubmit()
|
||||
public R<List<KnowledgeRetrievalVo>> retrieval(@RequestBody KnowledgeFragmentBo bo) {
|
||||
return R.ok(knowledgeFragmentService.retrieval(bo));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
package org.ruoyi.controller.shortdrama;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.ruoyi.common.chat.entity.media.MediaGenerationResponse;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.common.core.exception.ServiceException;
|
||||
import org.ruoyi.common.core.service.OssService;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaCharacterBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaCharacterAppearanceBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaComposeVideoBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaAudioBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaLocationBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaProjectBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaScriptBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaStoryboardBo;
|
||||
import org.ruoyi.domain.bo.shortdrama.ShortDramaIdeaBo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaCharacterVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaCharacterAppearanceVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaComposeVideoVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaDetailVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaAudioVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaLocationVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaProjectVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaScriptVo;
|
||||
import org.ruoyi.domain.vo.shortdrama.ShortDramaStoryboardVo;
|
||||
import org.ruoyi.service.shortdrama.IShortDramaService;
|
||||
import org.ruoyi.service.shortdrama.IShortDramaVideoComposeService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
@Validated
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/short-drama")
|
||||
public class ShortDramaController {
|
||||
|
||||
private final IShortDramaService shortDramaService;
|
||||
|
||||
private final IShortDramaVideoComposeService videoComposeService;
|
||||
|
||||
private final OssService ossService;
|
||||
|
||||
// ==================== 项目 ====================
|
||||
|
||||
@GetMapping("/projects")
|
||||
public R<List<ShortDramaProjectVo>> projects() {
|
||||
return R.ok(shortDramaService.listProjects(LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}")
|
||||
public R<ShortDramaDetailVo> detail(@PathVariable Long projectId) {
|
||||
return R.ok(shortDramaService.getDetail(projectId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/create-from-idea")
|
||||
public R<ShortDramaDetailVo> createFromIdea(@Valid @RequestBody ShortDramaIdeaBo bo) {
|
||||
return R.ok(shortDramaService.createFromIdea(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
/** SSE 流式创建:逐阶段推送进度,避免用户等待焦虑 */
|
||||
@PostMapping("/create-from-idea/stream")
|
||||
public SseEmitter createFromIdeaStream(@Valid @RequestBody ShortDramaIdeaBo bo) {
|
||||
return shortDramaService.createFromIdeaStream(bo, LoginHelper.getUserId());
|
||||
}
|
||||
|
||||
@PostMapping("/project")
|
||||
public R<Long> saveProject(@Valid @RequestBody ShortDramaProjectBo bo) {
|
||||
return R.ok(shortDramaService.saveProject(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/project")
|
||||
public R<Long> updateProject(@Valid @RequestBody ShortDramaProjectBo bo) {
|
||||
return R.ok(shortDramaService.saveProject(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/project/{projectId}")
|
||||
public R<Void> deleteProject(@NotNull @PathVariable Long projectId) {
|
||||
shortDramaService.deleteProject(projectId, LoginHelper.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 剧本 ====================
|
||||
|
||||
@PostMapping("/script")
|
||||
public R<ShortDramaScriptVo> saveScript(@Valid @RequestBody ShortDramaScriptBo bo) {
|
||||
return R.ok(shortDramaService.saveScript(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 分镜 ====================
|
||||
|
||||
@PostMapping("/storyboards/generate")
|
||||
public R<List<ShortDramaStoryboardVo>> generate(@NotNull @RequestParam Long projectId,
|
||||
@NotNull @RequestParam Long scriptId,
|
||||
@RequestParam(required = false) String model) {
|
||||
return R.ok(shortDramaService.generateStoryboards(projectId, scriptId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/storyboard")
|
||||
public R<ShortDramaStoryboardVo> saveStoryboard(@Valid @RequestBody ShortDramaStoryboardBo bo) {
|
||||
return R.ok(shortDramaService.saveStoryboard(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/storyboard/{storyboardId}/generate-video")
|
||||
public R<ShortDramaStoryboardVo> generateVideo(@NotNull @PathVariable Long storyboardId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.generateVideo(storyboardId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/storyboard/{storyboardId}/video-result")
|
||||
public R<ShortDramaStoryboardVo> videoResult(@NotNull @PathVariable Long storyboardId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.retrieveVideo(storyboardId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/generate-all-videos")
|
||||
public R<List<ShortDramaStoryboardVo>> generateAllVideos(@NotNull @PathVariable Long projectId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.generateAllVideos(projectId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/compose-video")
|
||||
public R<ShortDramaComposeVideoVo> composeVideo(@NotNull @PathVariable Long projectId,
|
||||
@Valid @RequestBody ShortDramaComposeVideoBo bo) {
|
||||
return R.ok(videoComposeService.composeVideo(projectId, bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/compose-video")
|
||||
public R<ShortDramaComposeVideoVo> getComposedVideo(@NotNull @PathVariable Long projectId) {
|
||||
return R.ok(videoComposeService.getComposedVideo(projectId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/{projectId}/compose-video/download")
|
||||
public void downloadComposedVideo(@NotNull @PathVariable Long projectId, HttpServletResponse response)
|
||||
throws IOException {
|
||||
ShortDramaComposeVideoVo composition = videoComposeService.getComposedVideo(projectId, LoginHelper.getUserId());
|
||||
if (composition == null || !"done".equals(composition.getStatus())) {
|
||||
throw new ServiceException("成片尚未生成完成");
|
||||
}
|
||||
if (composition.getVideoOssId() != null) {
|
||||
ossService.downloadFile(composition.getVideoOssId(), response);
|
||||
return;
|
||||
}
|
||||
Path localVideo = videoComposeService.getLocalComposedVideo(projectId, LoginHelper.getUserId());
|
||||
response.setContentType("video/mp4");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=short-drama-" + projectId + ".mp4");
|
||||
response.setContentLengthLong(Files.size(localVideo));
|
||||
Files.copy(localVideo, response.getOutputStream());
|
||||
}
|
||||
|
||||
// ==================== 阶段式流水线端点 ====================
|
||||
|
||||
/** Phase 1: 剧本打磨 */
|
||||
@PostMapping("/{projectId}/polish-script")
|
||||
public R<ShortDramaDetailVo> polishScript(@NotNull @PathVariable Long projectId) {
|
||||
return R.ok(shortDramaService.polishScript(projectId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 资产分析 ====================
|
||||
|
||||
/** Phase 2: 资产分析(角色+场景提取) */
|
||||
@PostMapping("/{projectId}/analyze-assets")
|
||||
public R<ShortDramaDetailVo> analyzeAssets(@NotNull @PathVariable Long projectId,
|
||||
@NotNull @RequestParam Long scriptId) {
|
||||
return R.ok(shortDramaService.analyzeAssets(projectId, scriptId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 分镜流水线 ====================
|
||||
|
||||
/** Phase 3-6: 分镜规划+摄影规则+表演指导+分镜细化 */
|
||||
@PostMapping("/{projectId}/plan-storyboard")
|
||||
public R<List<ShortDramaStoryboardVo>> planStoryboard(@NotNull @PathVariable Long projectId,
|
||||
@NotNull @RequestParam Long scriptId,
|
||||
@RequestParam(required = false) String model) {
|
||||
return R.ok(shortDramaService.planStoryboard(projectId, scriptId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
/** Phase 3-6: SSE 流式生成分镜,持续推送规划和细化进度 */
|
||||
@PostMapping("/{projectId}/plan-storyboard/stream")
|
||||
public SseEmitter planStoryboardStream(@NotNull @PathVariable Long projectId,
|
||||
@NotNull @RequestParam Long scriptId,
|
||||
@RequestParam(required = false) String model) {
|
||||
return shortDramaService.planStoryboardStream(projectId, scriptId, model, LoginHelper.getUserId());
|
||||
}
|
||||
|
||||
/** Phase 4: 重新生成摄影规则 */
|
||||
@PostMapping("/{projectId}/photography-rules")
|
||||
public R<List<ShortDramaStoryboardVo>> generatePhotographyRules(@NotNull @PathVariable Long projectId,
|
||||
@NotNull @RequestParam Long scriptId) {
|
||||
return R.ok(shortDramaService.generatePhotographyRules(projectId, scriptId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
/** Phase 5: 重新生成表演指导 */
|
||||
@PostMapping("/{projectId}/acting-directions")
|
||||
public R<List<ShortDramaStoryboardVo>> generateActingDirections(@NotNull @PathVariable Long projectId,
|
||||
@NotNull @RequestParam Long scriptId) {
|
||||
return R.ok(shortDramaService.generateActingDirections(projectId, scriptId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 角色管理 ====================
|
||||
|
||||
@PostMapping("/character")
|
||||
public R<ShortDramaCharacterVo> saveCharacter(@Valid @RequestBody ShortDramaCharacterBo bo) {
|
||||
return R.ok(shortDramaService.saveCharacter(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/character")
|
||||
public R<ShortDramaCharacterVo> updateCharacter(@Valid @RequestBody ShortDramaCharacterBo bo) {
|
||||
return R.ok(shortDramaService.saveCharacter(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/character/{characterId}")
|
||||
public R<Void> deleteCharacter(@NotNull @PathVariable Long characterId) {
|
||||
shortDramaService.deleteCharacter(characterId, LoginHelper.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/character/{characterId}/generate-image")
|
||||
public R<ShortDramaCharacterVo> generateCharacterImage(@NotNull @PathVariable Long characterId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.generateCharacterImage(characterId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 角色形象管理 ====================
|
||||
|
||||
@PostMapping("/character-appearance")
|
||||
public R<ShortDramaCharacterAppearanceVo> saveAppearance(@Valid @RequestBody ShortDramaCharacterAppearanceBo bo) {
|
||||
return R.ok(shortDramaService.saveAppearance(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/character-appearance")
|
||||
public R<ShortDramaCharacterAppearanceVo> updateAppearance(@Valid @RequestBody ShortDramaCharacterAppearanceBo bo) {
|
||||
return R.ok(shortDramaService.saveAppearance(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/character-appearance/{appearanceId}")
|
||||
public R<Void> deleteAppearance(@NotNull @PathVariable Long appearanceId) {
|
||||
shortDramaService.deleteAppearance(appearanceId, LoginHelper.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/character-appearance/{appearanceId}/generate-image")
|
||||
public R<ShortDramaCharacterAppearanceVo> generateAppearanceImage(@NotNull @PathVariable Long appearanceId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.generateAppearanceImage(appearanceId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/character-appearance/{appearanceId}/regenerate")
|
||||
public R<ShortDramaCharacterAppearanceVo> regenerateAppearanceImage(@NotNull @PathVariable Long appearanceId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.regenerateAppearanceImage(appearanceId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/character-appearance/{appearanceId}/select-image")
|
||||
public R<ShortDramaCharacterAppearanceVo> selectAppearanceImage(@NotNull @PathVariable Long appearanceId,
|
||||
@NotNull @RequestParam Integer index) {
|
||||
return R.ok(shortDramaService.selectAppearanceImage(appearanceId, index, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/character-appearance/{appearanceId}/image")
|
||||
public R<ShortDramaCharacterAppearanceVo> deleteAppearanceImage(@NotNull @PathVariable Long appearanceId,
|
||||
@NotNull @RequestParam Integer index) {
|
||||
return R.ok(shortDramaService.deleteAppearanceImage(appearanceId, index, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/character-appearance/{appearanceId}/undo-image")
|
||||
public R<ShortDramaCharacterAppearanceVo> undoAppearanceImage(@NotNull @PathVariable Long appearanceId) {
|
||||
return R.ok(shortDramaService.undoAppearanceImage(appearanceId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 场景管理 ====================
|
||||
|
||||
@PostMapping("/location")
|
||||
public R<ShortDramaLocationVo> saveLocation(@Valid @RequestBody ShortDramaLocationBo bo) {
|
||||
return R.ok(shortDramaService.saveLocation(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/location")
|
||||
public R<ShortDramaLocationVo> updateLocation(@Valid @RequestBody ShortDramaLocationBo bo) {
|
||||
return R.ok(shortDramaService.saveLocation(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/location/{locationId}")
|
||||
public R<Void> deleteLocation(@NotNull @PathVariable Long locationId) {
|
||||
shortDramaService.deleteLocation(locationId, LoginHelper.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/location/{locationId}/generate-image")
|
||||
public R<ShortDramaLocationVo> generateLocationImage(@NotNull @PathVariable Long locationId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.generateLocationImage(locationId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/location/{locationId}/regenerate")
|
||||
public R<ShortDramaLocationVo> regenerateLocationImage(@NotNull @PathVariable Long locationId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.regenerateLocationImage(locationId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/location/{locationId}/select-image")
|
||||
public R<ShortDramaLocationVo> selectLocationImage(@NotNull @PathVariable Long locationId,
|
||||
@NotNull @RequestParam Integer index) {
|
||||
return R.ok(shortDramaService.selectLocationImage(locationId, index, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/location/{locationId}/image")
|
||||
public R<ShortDramaLocationVo> deleteLocationImage(@NotNull @PathVariable Long locationId,
|
||||
@NotNull @RequestParam Integer index) {
|
||||
return R.ok(shortDramaService.deleteLocationImage(locationId, index, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/location/{locationId}/undo-image")
|
||||
public R<ShortDramaLocationVo> undoLocationImage(@NotNull @PathVariable Long locationId) {
|
||||
return R.ok(shortDramaService.undoLocationImage(locationId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 语音资产管理 ====================
|
||||
|
||||
@PostMapping("/audio")
|
||||
public R<ShortDramaAudioVo> saveAudio(@Valid @RequestBody ShortDramaAudioBo bo) {
|
||||
return R.ok(shortDramaService.saveAudio(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/audio")
|
||||
public R<ShortDramaAudioVo> updateAudio(@Valid @RequestBody ShortDramaAudioBo bo) {
|
||||
return R.ok(shortDramaService.saveAudio(bo, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/audio/{audioId}")
|
||||
public R<Void> deleteAudio(@NotNull @PathVariable Long audioId) {
|
||||
shortDramaService.deleteAudio(audioId, LoginHelper.getUserId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/audio/list")
|
||||
public R<List<ShortDramaAudioVo>> listAudios(@NotNull @RequestParam Long projectId) {
|
||||
return R.ok(shortDramaService.listAudios(projectId, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
@PostMapping("/audio/{audioId}/generate-speech")
|
||||
public R<ShortDramaAudioVo> generateAudio(@NotNull @PathVariable Long audioId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.generateAudio(audioId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
// ==================== 异步图片生成(轮询进度) ====================
|
||||
|
||||
/** 上传本地照片到图片供应商,返回当前生成会话使用的临时 URL。 */
|
||||
@PostMapping(value = "/image/upload-reference", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public R<String> uploadReferenceImage(@RequestPart("file") MultipartFile file,
|
||||
@NotBlank @RequestParam String model) {
|
||||
String temporaryUrl = shortDramaService.uploadReferenceImage(file, model, LoginHelper.getUserId());
|
||||
return R.ok("上传成功", temporaryUrl);
|
||||
}
|
||||
|
||||
/** 异步启动图片生成,返回 predictionId 供前端轮询 */
|
||||
@PostMapping("/image/start")
|
||||
public R<MediaGenerationResponse> startImage(@NotBlank @RequestParam String assetType,
|
||||
@NotNull @RequestParam Long assetId,
|
||||
@NotBlank @RequestParam String model,
|
||||
@RequestParam(required = false) String referenceImageUrl) {
|
||||
return R.ok(shortDramaService.startImageGeneration(assetType, assetId, model, referenceImageUrl, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
/** 轮询确认形象图片并保存 */
|
||||
@PostMapping("/character-appearance/{id}/confirm-image")
|
||||
public R<ShortDramaCharacterAppearanceVo> confirmAppearanceImage(@NotNull @PathVariable Long id,
|
||||
@NotBlank @RequestParam String predictionId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.confirmAppearanceImage(id, predictionId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
|
||||
/** 轮询确认场景图片并保存 */
|
||||
@PostMapping("/location/{id}/confirm-image")
|
||||
public R<ShortDramaLocationVo> confirmLocationImage(@NotNull @PathVariable Long id,
|
||||
@NotBlank @RequestParam String predictionId,
|
||||
@NotBlank @RequestParam String model) {
|
||||
return R.ok(shortDramaService.confirmLocationImage(id, predictionId, model, LoginHelper.getUserId()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.ruoyi.domain.bo.agent;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import io.github.linpeilie.annotations.AutoMapping;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.agent.Agent;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 智能体业务对象
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = Agent.class, reverseConvertGenerate = false)
|
||||
public class AgentBo extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 智能体ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 智能体名称
|
||||
*/
|
||||
@NotBlank(message = "智能体名称不能为空")
|
||||
@Size(min = 0, max = 200, message = "智能体名称不能超过{max}个字符")
|
||||
private String agentName;
|
||||
|
||||
/**
|
||||
* 智能体描述
|
||||
*/
|
||||
private String agentDescribe;
|
||||
|
||||
/**
|
||||
* 展示图标/头像URL
|
||||
*/
|
||||
private String agentShow;
|
||||
|
||||
/**
|
||||
* 绑定的聊天模型ID
|
||||
*/
|
||||
@NotNull(message = "绑定模型不能为空")
|
||||
private Long modelId;
|
||||
|
||||
/**
|
||||
* 是否启用深度思考:0 否 1 是
|
||||
*/
|
||||
private String enableThinking;
|
||||
|
||||
/**
|
||||
* 自定义系统提示词
|
||||
*/
|
||||
private String systemPrompt;
|
||||
|
||||
/**
|
||||
* 关联MCP工具ID列表
|
||||
*/
|
||||
@AutoMapping(target = "mcpToolIds", expression = "java(org.ruoyi.common.json.utils.JsonUtils.toJsonString(source.getMcpToolIds()))")
|
||||
private List<Long> mcpToolIds;
|
||||
|
||||
/**
|
||||
* 关联磁盘技能名列表
|
||||
*/
|
||||
@AutoMapping(target = "skillNames", expression = "java(org.ruoyi.common.json.utils.JsonUtils.toJsonString(source.getSkillNames()))")
|
||||
private List<String> skillNames;
|
||||
|
||||
/**
|
||||
* 关联知识库ID列表
|
||||
*/
|
||||
@AutoMapping(target = "knowledgeIds", expression = "java(org.ruoyi.common.json.utils.JsonUtils.toJsonString(source.getKnowledgeIds()))")
|
||||
private List<Long> knowledgeIds;
|
||||
|
||||
/**
|
||||
* 状态:0 正常 1 停用
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.ruoyi.domain.bo.coding;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 编程能力对话请求
|
||||
*
|
||||
* @author ageerle
|
||||
*/
|
||||
@Data
|
||||
public class CodingRequestBo {
|
||||
|
||||
/**
|
||||
* 用户指令
|
||||
*/
|
||||
@NotBlank(message = "prompt 不能为空")
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 模型名称(走 IChatModelService.selectModelByName)
|
||||
*/
|
||||
@NotBlank(message = "model 不能为空")
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 工作目录,可选;为空时默认指向 ruoyi-copilot 前端项目
|
||||
*/
|
||||
private String workspacePath;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.ruoyi.domain.bo.media;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ImageGenerationRequest {
|
||||
|
||||
@NotBlank(message = "模型不能为空")
|
||||
private String model;
|
||||
|
||||
@NotBlank(message = "提示词不能为空")
|
||||
private String prompt;
|
||||
|
||||
private String size;
|
||||
|
||||
@Min(value = 0, message = "随机种子不能小于0")
|
||||
@Max(value = 2147483647, message = "随机种子不能大于2147483647")
|
||||
private Integer seed;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.ruoyi.domain.bo.media;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SpeechGenerationRequest {
|
||||
|
||||
@NotBlank(message = "模型不能为空")
|
||||
private String model;
|
||||
|
||||
@NotBlank(message = "输入文本不能为空")
|
||||
private String input;
|
||||
|
||||
private String voice;
|
||||
|
||||
private String responseFormat;
|
||||
|
||||
private Double speed;
|
||||
|
||||
private String instructions;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.ruoyi.domain.bo.media;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class VideoGenerationRequest {
|
||||
|
||||
@NotBlank(message = "模型不能为空")
|
||||
private String model;
|
||||
|
||||
@NotBlank(message = "提示词不能为空")
|
||||
private String prompt;
|
||||
|
||||
private String size;
|
||||
|
||||
private Integer seconds;
|
||||
|
||||
private String quality;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaAudio;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaAudio.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaAudioBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
@NotNull(message = "项目ID不能为空")
|
||||
private Long projectId;
|
||||
|
||||
@NotBlank(message = "语音资产名称不能为空")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "语音类型不能为空")
|
||||
@Pattern(regexp = "narration|dialogue", message = "语音类型只能是 narration 或 dialogue")
|
||||
private String audioType;
|
||||
|
||||
@NotBlank(message = "语音文案不能为空")
|
||||
private String text;
|
||||
|
||||
/** 音色(生成语音时使用,可空,空则用模型默认) */
|
||||
private String voice;
|
||||
|
||||
/** 对白关联的分镜ID(旁白类型留空) */
|
||||
private Long linkedStoryboardId;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaCharacterAppearance;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaCharacterAppearance.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaCharacterAppearanceBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long characterId;
|
||||
|
||||
private Integer appearanceIndex;
|
||||
|
||||
private String changeReason;
|
||||
|
||||
private String description;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
private String imageUrls;
|
||||
|
||||
private String imageDescriptions;
|
||||
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
private String previousImageUrls;
|
||||
|
||||
private String previousDescriptions;
|
||||
|
||||
private String voice;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaCharacter;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaCharacter.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaCharacterBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String aliases;
|
||||
|
||||
private String introduction;
|
||||
|
||||
private String roleLevel;
|
||||
|
||||
private String gender;
|
||||
|
||||
private String ageRange;
|
||||
|
||||
private String personalityTags;
|
||||
|
||||
private Integer costumeTier;
|
||||
|
||||
private String visualDescription;
|
||||
|
||||
private String referenceImageUrl;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ShortDramaComposeVideoBo {
|
||||
|
||||
@NotBlank(message = "转场类型不能为空")
|
||||
@Pattern(regexp = "none|dissolve|fade|slide", message = "不支持的转场类型")
|
||||
private String transitionType = "fade";
|
||||
|
||||
@NotNull(message = "转场时长不能为空")
|
||||
@DecimalMin(value = "0.0", message = "转场时长不能小于0秒")
|
||||
private BigDecimal transitionDurationSeconds = new BigDecimal("0.3");
|
||||
|
||||
@NotBlank(message = "成片画幅不能为空")
|
||||
@Pattern(regexp = "9:16|16:9|4:3|3:4|1:1|21:9", message = "不支持的成片画幅")
|
||||
private String aspectRatio = "9:16";
|
||||
@Size(min = 2, message = "至少选择2个分镜视频")
|
||||
private List<Long> storyboardIds;
|
||||
|
||||
/** 旁白语音资产ID(可选,未传则不混入旁白) */
|
||||
private Long narrationAudioId;
|
||||
|
||||
/** 是否加水印(null 时用后端默认配置 ruoyi-ai) */
|
||||
private Boolean watermark;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ShortDramaIdeaBo {
|
||||
|
||||
@NotBlank(message = "创意想法不能为空")
|
||||
private String idea;
|
||||
|
||||
@NotBlank(message = "模型不能为空")
|
||||
private String model;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String artStyle;
|
||||
|
||||
private String aspectRatio;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaLocation;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaLocation.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaLocationBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String summary;
|
||||
|
||||
private Boolean hasCrowd;
|
||||
|
||||
private String crowdDescription;
|
||||
|
||||
private String availableSlots;
|
||||
|
||||
private String descriptions;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
private String imageUrls;
|
||||
|
||||
private String imageDescriptions;
|
||||
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
private String previousImageUrls;
|
||||
|
||||
private String previousDescriptions;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaProject;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaProject.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaProjectBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String description;
|
||||
|
||||
private String status;
|
||||
|
||||
private String artStyle;
|
||||
|
||||
private String composeAspectRatio;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaScript;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaScript.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaScriptBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String scriptName;
|
||||
|
||||
private String scriptText;
|
||||
|
||||
private String outlineText;
|
||||
|
||||
private String tone;
|
||||
|
||||
private String sourceType;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import dev.langchain4j.model.output.structured.Description;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Phase 1 剧本打磨结构化响应 —— langchain4j AiServices 自动解析用
|
||||
*
|
||||
* @author ageerle
|
||||
*/
|
||||
@Data
|
||||
public class ShortDramaScriptResult {
|
||||
|
||||
@Description("项目名称(有吸引力的短剧名)")
|
||||
private String projectName;
|
||||
|
||||
@Description("一句话简介(20-50字)")
|
||||
private String description;
|
||||
|
||||
@Description("剧本名称")
|
||||
private String scriptName;
|
||||
|
||||
@Description("风格基调(如:都市甜宠/古装虐恋/悬疑惊悚/喜剧爽文)")
|
||||
private String tone;
|
||||
|
||||
@Description("剧情大纲(400-800字,完整故事线)")
|
||||
private String outlineText;
|
||||
|
||||
@Description("完整短剧文本(1000-3000字,标准剧本格式,含场景头、动作描述、对话)")
|
||||
private String scriptText;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.ruoyi.domain.bo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaStoryboard;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ShortDramaStoryboard.class, reverseConvertGenerate = false)
|
||||
public class ShortDramaStoryboardBo extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private Long scriptId;
|
||||
|
||||
private Integer sceneNo;
|
||||
|
||||
private String sceneTitle;
|
||||
|
||||
private String sceneText;
|
||||
|
||||
private String sceneType;
|
||||
|
||||
private String shotType;
|
||||
|
||||
private String cameraMove;
|
||||
|
||||
private String charactersJson;
|
||||
|
||||
private String locationName;
|
||||
|
||||
private String photographyRules;
|
||||
|
||||
private String actingNotes;
|
||||
|
||||
private String continuityJson;
|
||||
|
||||
private String sourceText;
|
||||
|
||||
private String imagePrompt;
|
||||
|
||||
private Integer durationSeconds;
|
||||
|
||||
private String videoPrompt;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.ruoyi.domain.entity.agent;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.tenant.core.TenantEntity;
|
||||
|
||||
/**
|
||||
* 智能体信息实体
|
||||
* <p>
|
||||
* 一个智能体聚合:一个聊天模型 + 一组 MCP 工具 + 一组磁盘技能 + 一组知识库 + 自定义提示词
|
||||
* 关联以 JSON 数组字符串列存储:mcp_tool_ids / skill_names / knowledge_ids
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("agent_info")
|
||||
public class Agent extends TenantEntity {
|
||||
|
||||
/**
|
||||
* 智能体ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 智能体名称
|
||||
*/
|
||||
private String agentName;
|
||||
|
||||
/**
|
||||
* 智能体描述(下拉展示用)
|
||||
*/
|
||||
private String agentDescribe;
|
||||
|
||||
/**
|
||||
* 展示图标/头像URL
|
||||
*/
|
||||
private String agentShow;
|
||||
|
||||
/**
|
||||
* 绑定的聊天模型ID(chat_model.id, category=chat)
|
||||
*/
|
||||
private Long modelId;
|
||||
|
||||
/**
|
||||
* 是否启用深度思考(ReAct多子Agent):0 否 1 是
|
||||
*/
|
||||
private String enableThinking;
|
||||
|
||||
/**
|
||||
* 自定义系统提示词
|
||||
*/
|
||||
private String systemPrompt;
|
||||
|
||||
/**
|
||||
* 关联MCP工具ID列表(JSON数组,[Long])
|
||||
*/
|
||||
private String mcpToolIds;
|
||||
|
||||
/**
|
||||
* 关联磁盘技能名列表(JSON数组,[String])
|
||||
*/
|
||||
private String skillNames;
|
||||
|
||||
/**
|
||||
* 关联知识库ID列表(JSON数组,[Long])
|
||||
*/
|
||||
private String knowledgeIds;
|
||||
|
||||
/**
|
||||
* 状态:0 正常 1 停用
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -37,6 +37,9 @@ public class KnowledgeAttach extends BaseEntity {
|
||||
*/
|
||||
private String docId;
|
||||
|
||||
/** SHA-256 content digest used for upload idempotency. */
|
||||
private String fileHash;
|
||||
|
||||
/**
|
||||
* 附件名称
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,11 @@ public class KnowledgeFragment extends BaseEntity {
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 向量库片段ID(与向量库中的 fid 元数据对应,用于向量定位与混合检索融合)
|
||||
*/
|
||||
private String fid;
|
||||
|
||||
/**
|
||||
* 文档ID-用于关联文本块信息
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_audio")
|
||||
public class ShortDramaAudio extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
/** 语音资产名称 */
|
||||
private String name;
|
||||
|
||||
/** 语音类型:narration(旁白)/dialogue(对白) */
|
||||
private String audioType;
|
||||
|
||||
/** 语音文案(生成语音用的文本) */
|
||||
private String text;
|
||||
|
||||
/** 音色(如 alloy/onyx) */
|
||||
private String voice;
|
||||
|
||||
/** 音频文件OSS ID */
|
||||
private Long audioOssId;
|
||||
|
||||
/** 音频文件URL */
|
||||
private String audioUrl;
|
||||
|
||||
/** 对白关联的分镜ID(NULL=全局旁白) */
|
||||
private Long linkedStoryboardId;
|
||||
|
||||
/** 音频时长(秒) */
|
||||
private Integer durationSeconds;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_character")
|
||||
public class ShortDramaCharacter extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String aliases;
|
||||
|
||||
private String introduction;
|
||||
|
||||
private String roleLevel;
|
||||
|
||||
private String gender;
|
||||
|
||||
private String ageRange;
|
||||
|
||||
private String personalityTags;
|
||||
|
||||
private Integer costumeTier;
|
||||
|
||||
private String visualDescription;
|
||||
|
||||
private String referenceImageUrl;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_character_appearance")
|
||||
public class ShortDramaCharacterAppearance extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long characterId;
|
||||
|
||||
private Integer appearanceIndex;
|
||||
|
||||
private String changeReason;
|
||||
|
||||
private String description;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
/** 生成图片URL列表(JSON数组) */
|
||||
private String imageUrls;
|
||||
|
||||
/** 每张图片对应的提示词(JSON数组) */
|
||||
private String imageDescriptions;
|
||||
|
||||
/** 当前选中的图片索引 */
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
/** 上一轮图片URL列表(撤销用,JSON数组) */
|
||||
private String previousImageUrls;
|
||||
|
||||
/** 上一轮提示词列表(撤销用,JSON数组) */
|
||||
private String previousDescriptions;
|
||||
|
||||
/** 音色名(如 zh_male_taocheng_uranus_bigtts),用于该形象的对白配音 */
|
||||
private String voice;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_location")
|
||||
public class ShortDramaLocation extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String summary;
|
||||
|
||||
private Boolean hasCrowd;
|
||||
|
||||
private String crowdDescription;
|
||||
|
||||
private String availableSlots;
|
||||
|
||||
private String descriptions;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
/** 生成图片URL列表(JSON数组) */
|
||||
private String imageUrls;
|
||||
|
||||
/** 每张图片对应的提示词(JSON数组) */
|
||||
private String imageDescriptions;
|
||||
|
||||
/** 当前选中的图片索引 */
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
/** 上一轮图片URL列表(撤销用,JSON数组) */
|
||||
private String previousImageUrls;
|
||||
|
||||
/** 上一轮提示词列表(撤销用,JSON数组) */
|
||||
private String previousDescriptions;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_project")
|
||||
public class ShortDramaProject extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String description;
|
||||
|
||||
private String status;
|
||||
|
||||
private String artStyle;
|
||||
|
||||
private Long composedVideoOssId;
|
||||
|
||||
private String composeStatus;
|
||||
|
||||
private String composeJobId;
|
||||
|
||||
private Integer composeProgress;
|
||||
|
||||
private String composeTransitionType;
|
||||
|
||||
private BigDecimal composeTransitionDurationSeconds;
|
||||
|
||||
private String composeAspectRatio;
|
||||
|
||||
private BigDecimal composedVideoDurationSeconds;
|
||||
|
||||
private String composeErrorMessage;
|
||||
|
||||
private Date composedAt;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_script")
|
||||
public class ShortDramaScript extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String scriptName;
|
||||
|
||||
private String scriptText;
|
||||
|
||||
private String outlineText;
|
||||
|
||||
private String tone;
|
||||
|
||||
private String sourceType;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.ruoyi.domain.entity.shortdrama;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("short_drama_storyboard")
|
||||
public class ShortDramaStoryboard extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private Long scriptId;
|
||||
|
||||
private Integer sceneNo;
|
||||
|
||||
private String sceneTitle;
|
||||
|
||||
private String sceneText;
|
||||
|
||||
private String sceneType;
|
||||
|
||||
private String shotType;
|
||||
|
||||
private String cameraMove;
|
||||
|
||||
private String charactersJson;
|
||||
|
||||
private String locationName;
|
||||
|
||||
private String photographyRules;
|
||||
|
||||
private String actingNotes;
|
||||
|
||||
private String continuityJson;
|
||||
|
||||
private String sourceText;
|
||||
|
||||
private String imagePrompt;
|
||||
|
||||
private Integer durationSeconds;
|
||||
|
||||
private String videoPrompt;
|
||||
|
||||
private String videoUrl;
|
||||
|
||||
private String videoId;
|
||||
|
||||
private String videoStatus;
|
||||
|
||||
/** 上一镜末帧URL(同场景连续镜头首帧承接用) */
|
||||
private String lastFrameUrl;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.ruoyi.domain.vo.agent;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 智能体视图对象
|
||||
* <p>
|
||||
* 注意:不使用 @AutoMapper,因为 entity 的 mcpToolIds/skillNames/knowledgeIds 是 JSON 字符串列,
|
||||
* 而 VO 是 List 类型,MapStruct 无法双向自动转换。由 AgentServiceImpl.toVo 手动组装。
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class AgentVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 智能体ID
|
||||
*/
|
||||
@ExcelProperty(value = "智能体ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 智能体名称
|
||||
*/
|
||||
@ExcelProperty(value = "智能体名称")
|
||||
private String agentName;
|
||||
|
||||
/**
|
||||
* 智能体描述
|
||||
*/
|
||||
@ExcelProperty(value = "智能体描述")
|
||||
private String agentDescribe;
|
||||
|
||||
/**
|
||||
* 展示图标/头像URL
|
||||
*/
|
||||
private String agentShow;
|
||||
|
||||
/**
|
||||
* 绑定的聊天模型ID
|
||||
*/
|
||||
@ExcelProperty(value = "绑定模型ID")
|
||||
private Long modelId;
|
||||
|
||||
/**
|
||||
* 绑定的聊天模型名称(关联展示)
|
||||
*/
|
||||
@ExcelProperty(value = "绑定模型")
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 是否启用深度思考:0 否 1 是
|
||||
*/
|
||||
@ExcelProperty(value = "深度思考")
|
||||
private String enableThinking;
|
||||
|
||||
/**
|
||||
* 自定义系统提示词
|
||||
*/
|
||||
private String systemPrompt;
|
||||
|
||||
/**
|
||||
* 关联MCP工具ID列表
|
||||
*/
|
||||
private List<Long> mcpToolIds;
|
||||
|
||||
/**
|
||||
* 关联MCP工具名称列表(关联展示)
|
||||
*/
|
||||
private List<String> mcpToolNames;
|
||||
|
||||
/**
|
||||
* 关联磁盘技能名列表
|
||||
*/
|
||||
private List<String> skillNames;
|
||||
|
||||
/**
|
||||
* 关联知识库ID列表
|
||||
*/
|
||||
private List<Long> knowledgeIds;
|
||||
|
||||
/**
|
||||
* 关联知识库名称列表(关联展示)
|
||||
*/
|
||||
private List<String> knowledgeNames;
|
||||
|
||||
/**
|
||||
* 状态:0 正常 1 停用
|
||||
*/
|
||||
@ExcelProperty(value = "状态")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@ExcelProperty(value = "更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.ruoyi.domain.vo.agent;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 磁盘技能可选项
|
||||
*
|
||||
* @author ruoyi team
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SkillOptionVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 技能名称(对应 SKILL.md front-matter name)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 技能描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
||||
@@ -30,6 +30,11 @@ public class KnowledgeFragmentVo implements Serializable {
|
||||
@ExcelProperty(value = "主键")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 向量库片段ID
|
||||
*/
|
||||
private String fid;
|
||||
|
||||
/**
|
||||
* 文档ID-用于关联文本块信息
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.ruoyi.domain.vo.knowledge;
|
||||
|
||||
public record KnowledgeReparseVo(int submitted, int skipped, int total) {
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaAudio;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaAudio.class)
|
||||
public class ShortDramaAudioVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String audioType;
|
||||
|
||||
private String text;
|
||||
|
||||
private String voice;
|
||||
|
||||
private Long audioOssId;
|
||||
|
||||
private String audioUrl;
|
||||
|
||||
private Long linkedStoryboardId;
|
||||
|
||||
private Integer durationSeconds;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaCharacterAppearance;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaCharacterAppearance.class)
|
||||
public class ShortDramaCharacterAppearanceVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long characterId;
|
||||
|
||||
private Integer appearanceIndex;
|
||||
|
||||
private String changeReason;
|
||||
|
||||
private String description;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
private String imageUrls;
|
||||
|
||||
private String imageDescriptions;
|
||||
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
private String previousImageUrls;
|
||||
|
||||
private String previousDescriptions;
|
||||
|
||||
private String voice;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaCharacter;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaCharacter.class)
|
||||
public class ShortDramaCharacterVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String aliases;
|
||||
|
||||
private String introduction;
|
||||
|
||||
private String roleLevel;
|
||||
|
||||
private String gender;
|
||||
|
||||
private String ageRange;
|
||||
|
||||
private String personalityTags;
|
||||
|
||||
private Integer costumeTier;
|
||||
|
||||
private String visualDescription;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
private List<ShortDramaCharacterAppearanceVo> appearances;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ShortDramaComposeVideoVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String status;
|
||||
|
||||
private Integer progress;
|
||||
|
||||
private String transitionType;
|
||||
|
||||
private BigDecimal transitionDurationSeconds;
|
||||
|
||||
private String aspectRatio;
|
||||
|
||||
/** Actual duration measured from the final MP4 with ffprobe. */
|
||||
private BigDecimal outputDurationSeconds;
|
||||
|
||||
private Long videoOssId;
|
||||
|
||||
/** Freshly resolved URL; private-bucket URLs may be short lived. */
|
||||
private String videoUrl;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
private Date composedAt;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ShortDramaDetailVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ShortDramaProjectVo project;
|
||||
|
||||
private ShortDramaScriptVo script;
|
||||
|
||||
private List<ShortDramaCharacterVo> characters;
|
||||
|
||||
private List<ShortDramaLocationVo> locations;
|
||||
|
||||
private List<ShortDramaAudioVo> audios;
|
||||
|
||||
private List<ShortDramaStoryboardVo> storyboards;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaLocation;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaLocation.class)
|
||||
public class ShortDramaLocationVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String summary;
|
||||
|
||||
private Boolean hasCrowd;
|
||||
|
||||
private String crowdDescription;
|
||||
|
||||
private String availableSlots;
|
||||
|
||||
private String descriptions;
|
||||
|
||||
private String referenceImageUrl;
|
||||
|
||||
private String imageUrls;
|
||||
|
||||
private String imageDescriptions;
|
||||
|
||||
private Integer selectedImageIndex;
|
||||
|
||||
private String previousImageUrls;
|
||||
|
||||
private String previousDescriptions;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaProject;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaProject.class)
|
||||
public class ShortDramaProjectVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String description;
|
||||
|
||||
private String status;
|
||||
|
||||
private String artStyle;
|
||||
|
||||
private Long composedVideoOssId;
|
||||
|
||||
private String composeStatus;
|
||||
|
||||
private Integer composeProgress;
|
||||
|
||||
private String composeTransitionType;
|
||||
|
||||
private BigDecimal composeTransitionDurationSeconds;
|
||||
|
||||
private String composeAspectRatio;
|
||||
|
||||
private BigDecimal composedVideoDurationSeconds;
|
||||
|
||||
private String composeErrorMessage;
|
||||
|
||||
private Date composedAt;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.ruoyi.domain.vo.shortdrama;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import org.ruoyi.domain.entity.shortdrama.ShortDramaScript;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = ShortDramaScript.class)
|
||||
public class ShortDramaScriptVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String scriptName;
|
||||
|
||||
private String scriptText;
|
||||
|
||||
private String outlineText;
|
||||
|
||||
private String tone;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user