diff --git a/.github/workflows/app-platform-java-compile.yml b/.github/workflows/app-platform-java-compile.yml index a3971f6360..80a0287024 100644 --- a/.github/workflows/app-platform-java-compile.yml +++ b/.github/workflows/app-platform-java-compile.yml @@ -4,6 +4,7 @@ on: push: branches: - 'main' + - 'develop' paths: - 'app-builder/**' - '!app-builder/fel/python/**' @@ -17,6 +18,7 @@ on: pull_request: branches: - 'main' + - 'develop' paths: - 'app-builder/**' - '!app-builder/fel/python/**' diff --git a/README.md b/README.md index 0174fdf852..821127dabd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AppPlatform -**AppPlatform 是一个前沿的大模型应用工程,旨在通过集成的声明式编程和低代码配置工具,简化和优化大模型的训练与推理应用的开发过程。本工程为软件工程师和产品经理提供一个强大的、可扩展的环境,以支持从概念到部署的全流程 AI 应用开发。** +**AppPlatform 是一个前沿的大模型应用工程,旨在通过集成的声明式编程和低代码配置工具,简化 AI 应用的开发过程。本工程为软件工程师和产品经理提供一个强大的、可扩展的环境,以支持从概念到部署的全流程 AI 应用开发。** [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/license/MIT) [![JDK](https://img.shields.io/badge/JDK-17-green.svg)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) [![Node](https://img.shields.io/badge/node-20-red.svg)](https://nodejs.org/en/download) @@ -24,6 +24,25 @@ 3. **共享与协作**: AppPlatform 的底层包含 Store 模版,用于将所有开发的 AI 应用统一存储,以此支持跨项目的复用和协作。开发者可以根据需要组合这些应用,打造更大的解决方案,或者利用社区提供的工具和模型。在 AppPlatform 中, AI 应用不仅限于传统意义上的 “应用”,它们可以是 “函数”、“RAG”、“智能体”等任何可解释和可执行的组件。这些组件在 Store 中以 “工具” 的形式展现,其元数据不仅提供了必要的解释,还为智能体自动调度这些工具提供了基础。 --------- + +## 安装数据库 + +### Windows 系统 + +- 下载并安装 [PostgresSQL](https://www.postgresql.org/download/) +- 初始化数据。进入 `shell` 目录,使用 `bash` 工具执行 `build_win.sh`(当前不支持 `cmd` 执行,待规划): + +``` +cd shell +sh build_win.sh ${ip} ${port} ${username} ${password} +``` + +其中参数 ip、port、username、password 分别指的是数据库主机地址、数据库端口、数据用户名、数据库密码。该文件会初始化数据库内置数据,以及人工表单功能所需的数据。 + +### Linux 系统 + +待规划 + ## 后端环境配置 开发环境配置 @@ -32,7 +51,7 @@ - Java 17 - 代码格式化文件:[CodeFormatterFromIdea.xml](CodeFormatterFromIdea.xml) - `Maven` 配置:推荐版本 Maven 3.8.8+ -- FIT 框架编译产物(链接待补充) +- FIT 框架编译产物:参考 [FIT 框架](https://github.com/ModelEngine-Group/fit-framework) 的`环境配置`构建编译产物 **构建命令** @@ -52,6 +71,39 @@ build/ > 后端模块基于 [FIT](https://ModelEngine-Group/fit-framework) 框架,启动方式采用了 [FIT 动态插件](https://github.com/ModelEngine-Group/fit-framework/blob/main/docs/framework/fit/java/quick-start-guide/03.%20%E4%BD%BF%E7%94%A8%E6%8F%92%E4%BB%B6%E7%9A%84%E7%83%AD%E6%8F%92%E6%8B%94%E8%83%BD%E5%8A%9B.md) 方式。 +打开框架输出目录的 `conf/fitframework.yml` 文件,找到如下配置项 + +```yml +fit: + beans: + packages: + - 'modelengine.fitframework' + - 'modelengine.fit' +``` + +加入数据库配置项,修改后的配置项如下所示: + +```yml +fit: + beans: + packages: + - 'modelengine.fitframework' + - 'modelengine.fit' + datasource: + primary: 'sample-datasource' # 表示所选用的示例数据源。 + instances: + sample-datasource: + mode: 'shared' # 表示该数据源的模式,可选共享(shared)或独占(exclusive)模式。 + url: 'jdbc:postgresql://${ip}:${port}/' # 将 ip 换成数据库服务器的 ip 地址,将 port 换成数据库服务器监听的端口。 + username: '${username}' # 将 username 替换为数据库的名称。 + password: '${password}' # 将 password 替换为数据库的密码。 + druid: + initialSize: ${initialSize} # 将 initialSize 替换为连接池的初始化连接数。 + minIdle: ${midIdle} # 将 minIdle 替换为连接池的最小空闲连接数。 + maxActive: ${maxActive} # 将 maxActive 替换为数据库连接池的最大活动连接数。 + # 可根据具体需求,添加连接池所需配置项。 +``` + **启动命令** ``` @@ -70,7 +122,22 @@ fit start -Dfit.profiles.active=prod - 环境要求:node.js >= 20 +- ELSA 框架编译产物:参考 [ELSA](https://github.com/ModelEngine-Group/fit-framework/blob/main/framework/elsa/README.md) 的编译构建章节 + +**修改 ELSA 依赖地址** + +进入目录 `app-engine\frontend` ,搜索 `package.json` 文件的 ELSA 依赖地址: + +``` +"dependencies": { + "@fit-elsa/elsa-core": "file:${fitElsaCoreUrl}", + "@fit-elsa/elsa-react": "file:${fitElsaReactUrl}", +``` + +将 `${fitElsaCoreUrl}` 和 `${fitElsaReactUrl}` 分别改成 `ELSA` 框架编译产物 `fit-elsa-core` 和 `fit-react` 的目录地址即可。 + **修改代理文件** + 修改 `AppPlatform/frontend` 目录下的 `proxy.config.json` 文件,可以修改需要访问的后端地址。如本地后端地址是 `http://127.0.0.1:8080` 。可以按照如下示例配置: ```json @@ -89,13 +156,7 @@ fit start -Dfit.profiles.active=prod **依赖安装** ``` -cd framework/elsa/fit-elsa -npm install -npm run build:debug -cd ../fit-elsa-react/ -npm install -npm run build -cd ../../../app-engine/frontend/ +cd app-engine/frontend/ npm install ``` @@ -113,7 +174,19 @@ npm run start --------- ## 快速开始 -**待完善** +**模型配置** + +在对话中使用大模型功能,需要对模型进行配置,包括大模型的地址和鉴权信息。 +首先在首页的`应用市场`一栏中找到 `模型配置应用`,并点击该应用。点击右上角`创意灵感` 的`开始配置`,如下图所示: +![image-20250508203127410](doc\images\readme\model_config_inspiration.png) +然后点击回答的 `添加模型` 按钮,输入模型名称、API Key 和模型地址,并点击确认。此时模型添加成功。 + +**应用创建** + +在首页的`应用开发`一栏中点击`创建空白应用`。如下所示: +![image-20250508204618312](doc\images\readme\app_create.png) +输入所要创建的应用名称和简介,并点击 `创建`按钮,即可创建 AI 应用。接着在跳转后的应用配置页面上,在 `大模型` 一栏中选择自定义配置的模型。此时即可在对话框进行对话。如下所示: +![image-20250508205124203](doc\images\readme\app_chat.png) ## 文档 diff --git a/app-builder/builtin/form/model-config-form/.gitignore b/app-builder/builtin/form/model-config-form/.gitignore new file mode 100644 index 0000000000..593054248d --- /dev/null +++ b/app-builder/builtin/form/model-config-form/.gitignore @@ -0,0 +1,3 @@ +package-lock.json +node_modules/ +output/ \ No newline at end of file diff --git a/app-builder/builtin/form/model-config-form/.npmrc b/app-builder/builtin/form/model-config-form/.npmrc new file mode 100644 index 0000000000..2480139db5 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/.npmrc @@ -0,0 +1,3 @@ +strict-ssl=false +package-lock=true +registry=https://registry.npmjs.org/ \ No newline at end of file diff --git a/app-builder/builtin/form/model-config-form/README.md b/app-builder/builtin/form/model-config-form/README.md new file mode 100644 index 0000000000..1ab62543eb --- /dev/null +++ b/app-builder/builtin/form/model-config-form/README.md @@ -0,0 +1,173 @@ +# 画布 # 自定义组件开发说明 + +## 前提条件 + +* 开发工具建议使用 VSCode + +* 基础环境: Node.js 版本 >= 18, npm 版本 >= 10 + +* React 组件建议使用 Ant Design (版本: 4.24.13) + +## 操作步骤 + +### 约束条件 + +上传的组件包必须是 zip 压缩包,解压后文件大小不得超过 5M,且必须包含三部分: + +* build 文件夹: 表单代码打包后的静态资源 + +* config.json: 表单的输入输出参配置文件 + +* form.jpg/png/jpeg: 表单预览图,大小不得超过 1M + +## 开发组件代码 + +### 创建文件 + +* 在 `/src/components` 目录下创建 `.tsx` 类型的组件文件 + +### 表单获取流程数据 + +用于初始化表单数据: + +```tsx +const { data, terminateClick, resumingClick, restartClick } = useContext(DataContext); +``` + +`data` 为 json 数据,结构与 config.json 的输入参配置一致 + +### 表单调用内置接口 + +**1. 终止对话 `terminateClick()`** + +```tsx + + +const onTerminateClick = () => { + terminateClick({ content: "终止会话" }); +} +``` + +**注意:结束节点不能调用 `terminateClick`** + +**2. 继续对话 `resumingClick()`** + +```tsx + + +const onResumeClick = () => { + resumingClick({ params: { a: "hello", b: 1 } }); +} +``` + +**注意:结束节点不能调用 `resumingClick`** + +**3. 重新对话 `restartClick()`** + +```tsx + + +const onRestartClick = () => { + restartClick({ params: { a: "hello", b: 1 } }); +} +``` + +**注意:如果在智能表单节点使用,需先调 `terminateClick` 再 `restartClick`** + +### 调用外部接口 + +* 要求接口支持跨域 + +### 使用图片 + +* 图片文件放在 `/src/assets/images` + +* 路径: `./src/assets/images/xxx.png` + +```tsx + +``` + +### 表单样式文件 + +* 可以在 `/src/styles` 目录下添加 `.scss` 样式文件 + +### 调试表单 + +```bash +npm install +npm start +``` + +* 模拟数据 `app.tsx`: + +```ts +receiveData: { + data: { a: "你好", b: "Demo1" }, + uniqueId: 10, + origin: "http://127.0.0.1:3350", + tenantId: "fh47kl" +} +``` + +### 打包 + +```bash +npm run build +``` + +## 表单输入输出参 config.json + +### 基础规范 + +* 格式需符合[json schema规范](https://json-schema.apifox.cn/) + +* 格式示例: + +```json +{ + "schema": { + "parameters": { + "type": "object", + "required": ["a", "b"], + "properties": { + "a": { "type": "string", "default": "haha" }, + "b": { "type": "string", "default": "heihei" } + } + }, + "return": { + "type": "object", + "properties": { + "a": { "type": "string" }, + "b": { "type": "string" } + } + } + } +} +``` + +* 最外层 `parameters` 字段是入参,入参第一层必须 `type` 为 `object`。 + +* 必须包含 `name`,支持中文、英文、数字、空格、中划线、下划线组合。 + +* 可以包含 `description`,对参数进行描述。 + +* 必须包含 `parameters`。 + +* 必须包含 `required`,内容不可以为 `properties` 下参数名之外的参数名。 + +* 可以包含 `order`,若写必须为 `properties` 下所有参数名的列表;若不写,则默认按照 `properties` 下所有参数名的顺序。 + +* 必须包含 `return`,`return` 字段是出参。 + +## 表单预览图 + +* 名称: form.jpg/png/jpeg + +* 大小: 不超过 1M + +## 打包规则 + +* 包含 build/、config.json、form.png + +* 将build文件夹、config.json、form.png打成zip压缩包,压缩包名称支持大小写英文、中文和数字的字符串,可以包含中划线(-)和下划线(_),但不能以中划线(-)和下划线(_)开头或结尾。 diff --git a/app-builder/builtin/form/model-config-form/config.json b/app-builder/builtin/form/model-config-form/config.json new file mode 100644 index 0000000000..70424e55b9 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/config.json @@ -0,0 +1,48 @@ +{ + "schema": { + "name": "模型管理表单", + "parameters": { + "type": "object", + "required": ["models"], + "properties": { + "models": { + "type": "array", + "items": { + "type": "object", + "properties": { + "createdAt": { "type": "string" }, + "modelName": { "type": "string" }, + "modelId": { "type": "string" }, + "baseUrl": { "type": "string" }, + "isDefault": { "type": "integer" }, + "userId": { "type": "string" } + }, + "required": ["modelId", "isDefault", "userId"] + } + } + } + }, + "return": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": ["add", "delete", "switch", "quit"] + }, + "info": { + "type": "object", + "properties": { + "modelName": { "type": "string" }, + "modelId": { "type": "string" }, + "baseUrl": { "type": "string" }, + "isDefault": { "type": "integer" }, + "userId": { "type": "string" }, + "apiKey": { "type": "string" } + }, + "required": ["modelName", "modelId", "baseUrl", "isDefault", "userId", "apiKey"] + } + }, + "required": ["action", "info"] + } + } +} diff --git a/app-builder/builtin/form/model-config-form/form.png b/app-builder/builtin/form/model-config-form/form.png new file mode 100644 index 0000000000..b39ef9e073 Binary files /dev/null and b/app-builder/builtin/form/model-config-form/form.png differ diff --git a/app-builder/builtin/form/model-config-form/package.json b/app-builder/builtin/form/model-config-form/package.json new file mode 100644 index 0000000000..3965a296e9 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/package.json @@ -0,0 +1,58 @@ +{ + "name": "remote-component", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "webpack serve --progress --profile --mode development --host 127.0.0.1 --port 3351 --config webpack.dev.js", + "build": "webpack --mode production --config webpack.prod.js" + }, + "license": "ISC", + "devDependencies": { + "@ant-design/icons": "4.8.3", + "@babel/core": "^7.20.12", + "@babel/plugin-transform-runtime": "^7.12.17", + "@babel/preset-env": "^7.12.13", + "@babel/preset-react": "^7.12.13", + "@babel/preset-typescript": "^7.24.7", + "@types/node": "^20.12.12", + "@types/react": "^18.2.74", + "@types/react-dom": "^16.9.2", + "babel-loader": "^8.2.2", + "babel-plugin-dynamic-import-webpack": "^1.1.0", + "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^10.2.0", + "css-loader": "^6.8.1", + "cssnano": "^5.1.15", + "eslint": "^7.32.0", + "file-loader": "^6.2.0", + "happypack": "^5.0.1", + "html-webpack-plugin": "^5.5.0", + "less": "4.2.0", + "less-loader": "12.2.0", + "lodash": "4.17.21", + "mini-css-extract-plugin": "^1.6.2", + "postcss-import": "^14.0.0", + "postcss-loader": "^4.0.4", + "prettier": "3.2.5", + "sass": "^1.69.5", + "sass-loader": "^13.3.2", + "style-loader": "^2.0.0", + "typescript": "^5.4.5", + "uglifyjs-webpack-plugin": "^2.2.0", + "url-loader": "^4.1.1", + "webpack": "5.89.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "4.15.1", + "webpack-merge": "5.10.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "antd": "4.24.13", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "react-i18next": "^11.8.13", + "uuid": "^11.1.0" + } +} diff --git a/app-builder/builtin/form/model-config-form/src/app.tsx b/app-builder/builtin/form/model-config-form/src/app.tsx new file mode 100644 index 0000000000..60a8ee82e8 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/app.tsx @@ -0,0 +1,71 @@ +/*************************************************请勿修改或删除该文件**************************************************/ +import React, { useState, useEffect, useRef } from 'react'; +import { inIframe, getQueryParams } from './utils/index'; +import { DataContext } from './context'; +import Form from './components/form'; + +export default function App() { + const [receiveData, setReceiveData] = useState({}); + const uniqueId = getQueryParams(window.location.href); + const formRef = useRef(null); + + const handleMessage = (event: any) => { + setReceiveData(event.data); + }; + + const terminateClick = (params: any) => { + window.parent.postMessage({ type: 'app-engine-form-terminate', ...params, uniqueId }, receiveData.origin); + }; + + const resumingClick = (params: any) => { + window.parent.postMessage({ type: 'app-engine-form-resuming', ...params, uniqueId }, receiveData.origin); + }; + + const restartClick = (params: any) => { + window.parent.postMessage({ type: 'app-engine-form-restart', ...params, uniqueId }, receiveData.origin); + }; + + useEffect(() => { + if (inIframe()) { + window.addEventListener('message', handleMessage); + window.parent.postMessage({ + type: 'app-engine-form-ready', + uniqueId + }, '*'); + } + + const ro = new ResizeObserver(entries => { + entries.forEach(entry => { + const height = entry.contentRect.height; + window.parent.postMessage({ type: 'app-engine-form-resize', height, uniqueId }, "*"); + }); + }); + + if (formRef.current) { + ro.observe(formRef.current); + } + + return () => { + if (inIframe()) { + window.removeEventListener('message', handleMessage); + } + + if (formRef.current) { + ro.unobserve(formRef.current); + } + ro.disconnect(); + }; + }, []); + + return ( +
+ +
+ +
+ ); +} diff --git a/app-builder/builtin/form/model-config-form/src/assets/images/empty.png b/app-builder/builtin/form/model-config-form/src/assets/images/empty.png new file mode 100644 index 0000000000..fd355f179e Binary files /dev/null and b/app-builder/builtin/form/model-config-form/src/assets/images/empty.png differ diff --git a/app-builder/builtin/form/model-config-form/src/components/form.tsx b/app-builder/builtin/form/model-config-form/src/components/form.tsx new file mode 100644 index 0000000000..7989a8e61d --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/components/form.tsx @@ -0,0 +1,288 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { Button, Table, Card, Typography, Space, Tooltip, Modal, Input, message, Dropdown, Menu } from 'antd'; +import { + PlusOutlined, + EllipsisOutlined, + ExclamationCircleOutlined, + LogoutOutlined +} from '@ant-design/icons'; +import { DataContext } from '../context'; +import '../styles/form.scss'; + +const { Title, Text } = Typography; +const { confirm } = Modal; + +const SmartForm: React.FC = () => { + const { data, resumingClick } = useContext(DataContext); + const [modelList, setModelList] = useState([]); + const [isModalVisible, setIsModalVisible] = useState(false); + const [newModel, setNewModel] = useState({ modelName: '', baseUrl: '', apiKey: '' }); + + const buildOutputInfo = (partial: Partial<{ + modelName: string; + modelId: string; + baseUrl: string; + userId: string; + isDefault: number; + apiKey: string; + }>) => ({ + modelName: partial.modelName || '', + modelId: partial.modelId || '', + baseUrl: partial.baseUrl || '', + userId: partial.userId || '', + isDefault: partial.isDefault ?? 0, + apiKey: partial.apiKey || '' + }); + + useEffect(() => { + if (Array.isArray(data?.models)) { + const enhancedModels = data.models.map((item, index) => ({ + ...item, + serial: index + 1, + })); + setModelList(enhancedModels); + } + }, [data]); + + const handleAdd = () => setIsModalVisible(true); + const handleAddCancel = () => setIsModalVisible(false); + + const handleAddConfirm = () => { + const { modelName, baseUrl, apiKey } = newModel; + if (!modelName.trim() || !baseUrl.trim() || !apiKey.trim()) { + message.warning('请填写完整模型信息'); + return; + } + + const userId = data?.models?.[0]?.userId || ''; + + const payload = buildOutputInfo({ + userId, + modelName, + baseUrl, + apiKey + }); + + resumingClick({ + params: { + action: 'add', + info: payload + } + }); + + setIsModalVisible(false); + setNewModel({ modelName: '', baseUrl: '', apiKey: '' }); + }; + + const handleDelete = (record: any) => { + confirm({ + title: '删除确认', + icon: , + content: ( + <> +

你确定要删除这个模型吗?

+

模型名称:{record.modelName}

+ + ), + okText: '确认删除', + cancelText: '取消', + okType: 'danger', + onOk: () => { + const payload = buildOutputInfo({ + ...record, + apiKey: '' + }); + resumingClick({ + params: { + action: 'delete', + info: payload + } + }); + }, + }); + }; + + const handleSwitchDefault = (record: any) => { + confirm({ + title: '切换默认模型', + icon: , + content: `是否将 "${record.modelName}" 设置为默认模型?`, + okText: '确认', + cancelText: '取消', + onOk: () => { + const payload = buildOutputInfo({ + ...record, + apiKey: '' + }); + resumingClick({ + params: { + action: 'switch', + info: payload + } + }); + } + }); + }; + + const handleExit = () => { + const payload = buildOutputInfo({}) + resumingClick({ + params: { + action: 'quit', + info: payload + } + }); + }; + + const columns = [ + { + title: 'ID', + dataIndex: 'serial', + key: 'serial', + align: 'center', + width: 60, + }, + { + title: '模型名称', + dataIndex: 'modelName', + key: 'modelName', + align: 'center', + render: (text: string) => ( + +
+ {text} +
+
+ ), + }, + { + title: 'Base URL', + dataIndex: 'baseUrl', + key: 'baseUrl', + align: 'center', + render: (text: string) => ( + +
+ {text} +
+
+ ), + }, + { + title: '是否默认', + dataIndex: 'isDefault', + key: 'isDefault', + align: 'center', + render: (value: number) => (value === 1 ? '是' : '否'), + }, + { + title: '操作', + key: 'action', + align: 'center', + width: 80, + render: (_: any, record: any) => { + const menuItems = [ + record.isDefault !== 1 && { + key: 'switch', + label: '设为默认模型', + onClick: () => handleSwitchDefault(record) + }, + { + key: 'delete', + label: '删除模型', + onClick: () => handleDelete(record) + } + ].filter(Boolean); + + return ( + } trigger={['click']}> + + + + + + + +
+ + setNewModel({ ...newModel, modelName: e.target.value })} + placeholder="请输入模型名称" + /> +
+
+ + setNewModel({ ...newModel, apiKey: e.target.value })} + placeholder="请输入 API Key" + /> +
+
+ + setNewModel({ ...newModel, baseUrl: e.target.value })} + placeholder="请输入 Base URL" + /> +
+
+ + ); +}; + +export default SmartForm; diff --git a/app-builder/builtin/form/model-config-form/src/context/index.ts b/app-builder/builtin/form/model-config-form/src/context/index.ts new file mode 100644 index 0000000000..ab7f51b4b2 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/context/index.ts @@ -0,0 +1,9 @@ +/*************************************************请勿修改或删除该文件**************************************************/ +import { createContext } from 'react'; + +export const DataContext = createContext({ + data: {}, + terminateClick: (params) => {}, + resumingClick: (params) => {}, + restartClick: (params) => {}, +}); \ No newline at end of file diff --git a/app-builder/builtin/form/model-config-form/src/index.tsx b/app-builder/builtin/form/model-config-form/src/index.tsx new file mode 100644 index 0000000000..df35256297 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/index.tsx @@ -0,0 +1,8 @@ +/*************************************************请勿修改或删除该文件**************************************************/ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './app'; +import 'antd/dist/antd.less'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render(); diff --git a/app-builder/builtin/form/model-config-form/src/styles/form.scss b/app-builder/builtin/form/model-config-form/src/styles/form.scss new file mode 100644 index 0000000000..4f4ea86257 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/styles/form.scss @@ -0,0 +1,26 @@ +.form-wrap { + height: 100%; + overflow-y: auto; + padding: 24px; + box-sizing: border-box; + + .ant-card { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + border-radius: 12px; + } + + .ant-table-thead > tr > th { + background: #fafafa; + font-weight: bold; + } + + .ant-btn[shape='circle']:hover { + background-color: #ffecec !important; + border-radius: 50%; + } +} + +.form-container { + padding: 24px; + background: #f5f5f5; +} \ No newline at end of file diff --git a/app-builder/builtin/form/model-config-form/src/utils/index.ts b/app-builder/builtin/form/model-config-form/src/utils/index.ts new file mode 100644 index 0000000000..6d7ea3253d --- /dev/null +++ b/app-builder/builtin/form/model-config-form/src/utils/index.ts @@ -0,0 +1,18 @@ +/*************************************************请勿修改或删除getQueryParams方法**************************************************/ +export const getQueryParams = (url) => { + const regex = /uniqueId=([a-zA-Z0-9-]+)/; + const match = url.match(regex); + if (match && match.length > 1) { + return match[1]; + } else { + return null; + } +} + +export const inIframe = () => { + try { + return window.self !== window.top; + } catch (e) { + return true; + } +} diff --git a/app-builder/builtin/form/model-config-form/webpack.common.js b/app-builder/builtin/form/model-config-form/webpack.common.js new file mode 100644 index 0000000000..9fd88e29e1 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/webpack.common.js @@ -0,0 +1,150 @@ +const path = require("path"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const { CleanWebpackPlugin } = require("clean-webpack-plugin"); +const HappyPack = require("happypack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +module.exports = { + module: { + rules: [{ + test: /\.(js|jsx)$/, + exclude: /node_modules/, + include: [path.resolve(__dirname, "src")], + use: { + loader: "happypack/loader?id=babel", + }, + }, + { + test: /\.(png|jpg|gif)$/, + type: 'javascript/auto', + include: [path.resolve(__dirname, "src")], + exclude: /node_modules/, + use: [{ + loader: "url-loader", + options: { + esModule: false + } + }], + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, + { + test: /\.(woff|woff2|eot|otf|ttf)$/, + loader: "file-loader", + options: { + name: "[name]-[hash:5].min.[ext]" + } + }, + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + use: [{ + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + '@babel/preset-react', + '@babel/preset-typescript', + ], + }, + }], + }, + { + test: /\.less$/, + use: [{ + loader: 'style-loader', + }, { + loader: 'css-loader', + }, { + loader: 'less-loader', + options: { + lessOptions: { + modifyVars: { + 'primary-color': '#2673e5', + 'border-radius-base': '4px' + }, + javascriptEnabled: true, + }, + }, + }], + }, + { + test: /\.s(c|a)ss$/, + include: [path.resolve(__dirname, "src")], + exclude: /node_modules/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + url: false, + sourceMap: true, + importLoaders: 2, + modules: { + auto: true, + exportLocalsConvention: 'dashesOnly', + localIdentName: '[local]__[hash:base64:5]', + }, + }, + }, + 'postcss-loader', + { + loader: 'sass-loader', + options: { + api: 'modern' + } + }, + ], + }, + ], + }, + resolve: { + alias: { + '@': path.resolve(__dirname, 'src') + }, + extensions: ['.ts', '.tsx', '.js', '.jsx'], + }, + plugins: [ + new HappyPack({ + id: "babel", + loaders: ["babel-loader?cacheDirectory"] + }), + new MiniCssExtractPlugin({ + filename: "[name].[hash:8].css", + chunkFilename: "[name].[hash:8].css", + }), + new HtmlWebpackPlugin({ + title: "module", + template: path.join(process.cwd(), './src/index.html'), + filename: "index.html", + }), + new CleanWebpackPlugin(), + new CopyWebpackPlugin({ + patterns: [ + { + from: 'config.json', + to: '../config.json' + }, + { + from: 'form.png', + to: '../form.png' + } + ] + }), + ], + optimization: { + splitChunks: { + chunks: 'all', + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + }, + }, + }, +}; diff --git a/app-builder/builtin/form/model-config-form/webpack.dev.js b/app-builder/builtin/form/model-config-form/webpack.dev.js new file mode 100644 index 0000000000..ace654aa8d --- /dev/null +++ b/app-builder/builtin/form/model-config-form/webpack.dev.js @@ -0,0 +1,14 @@ +const { merge } = require('webpack-merge'); +const common = require('./webpack.common.js'); +module.exports = merge(common, { + mode: 'development', + devtool: 'inline-source-map', + plugins: [], + devServer: { + historyApiFallback: true, + hot: true, + open: true, + https: false, + proxy: {} + }, +}); diff --git a/app-builder/builtin/form/model-config-form/webpack.prod.js b/app-builder/builtin/form/model-config-form/webpack.prod.js new file mode 100644 index 0000000000..427dd3fab6 --- /dev/null +++ b/app-builder/builtin/form/model-config-form/webpack.prod.js @@ -0,0 +1,15 @@ + +const path = require("path"); +const { merge } = require("webpack-merge"); +const common = require("./webpack.common.js"); + +module.exports = merge(common, { + entry: { + index: "./src/index.tsx", + }, + output: { + filename: "[name].js", + path: path.resolve(__dirname, "output/build") + }, + plugins: [], +}); diff --git a/app-builder/fel/java/fel-community/model-openai/src/main/java/modelengine/fel/community/model/openai/OpenAiModel.java b/app-builder/fel/java/fel-community/model-openai/src/main/java/modelengine/fel/community/model/openai/OpenAiModel.java index c83099ff1b..a452347b42 100644 --- a/app-builder/fel/java/fel-community/model-openai/src/main/java/modelengine/fel/community/model/openai/OpenAiModel.java +++ b/app-builder/fel/java/fel-community/model-openai/src/main/java/modelengine/fel/community/model/openai/OpenAiModel.java @@ -232,7 +232,6 @@ private HttpClassicClient getHttpClient() { return value; })); - log.info("Create custom HTTPS config: {}", this.serializer.serialize(custom)); return this.httpClientFactory.create(HttpClassicClientFactory.Config.builder() .socketTimeout(this.clientConfig.socketTimeout()) .connectTimeout(this.clientConfig.connectTimeout()) @@ -246,7 +245,6 @@ private HttpClassicClient getHttpClient(SecureConfig secureConfig) { } Map custom = buildHttpsConfig(secureConfig); - log.info("Create custom HTTPS config: {}", this.serializer.serialize(custom)); return this.httpClientFactory.create(HttpClassicClientFactory.Config.builder() .socketTimeout(this.clientConfig.socketTimeout()) .connectTimeout(this.clientConfig.connectTimeout()) diff --git a/app-builder/jane/jober-genericable/src/main/java/modelengine/fit/jober/common/RangedResultSet.java b/app-builder/jane/jober-genericable/src/main/java/modelengine/fit/jober/common/RangedResultSet.java index 1e40c67370..6ed410e978 100644 --- a/app-builder/jane/jober-genericable/src/main/java/modelengine/fit/jober/common/RangedResultSet.java +++ b/app-builder/jane/jober-genericable/src/main/java/modelengine/fit/jober/common/RangedResultSet.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; +import java.util.Optional; /** * 批量返回结构体。 @@ -67,7 +68,25 @@ public static RangedResultSet create(List results, Range range, long t * @return RangedResultSet */ public static RangedResultSet create(List results, RangeResult range) { - return new RangedResultSet(results, range); + return new RangedResultSet<>(results, range); + } + + /** + * 集合是否为空. + * + * @return 返回集合是否是空的 {@code boolean}。 + */ + public boolean isEmpty() { + return this.range.getTotal() == 0; + } + + /** + * 获取第一个元素. + * + * @return {@link Optional}{@code <}{@code T}{@code >} Optional对象. + */ + public Optional getFirst() { + return this.isEmpty() ? Optional.empty() : Optional.of(this.results.get(0)); } public List getResults() { diff --git a/app-builder/jane/jober-multiversion-genericable/src/main/java/modelengine/fit/jane/meta/multiversion/MetaInstanceService.java b/app-builder/jane/jober-multiversion-genericable/src/main/java/modelengine/fit/jane/meta/multiversion/MetaInstanceService.java index 5cc07f4a36..92514e7ea4 100644 --- a/app-builder/jane/jober-multiversion-genericable/src/main/java/modelengine/fit/jane/meta/multiversion/MetaInstanceService.java +++ b/app-builder/jane/jober-multiversion-genericable/src/main/java/modelengine/fit/jane/meta/multiversion/MetaInstanceService.java @@ -76,9 +76,9 @@ void patchMetaInstance(String versionId, String instanceId, InstanceDeclarationI * 查询meta实例。 * * @param versionId 表示实例所属meta唯一标识的 {@link String}。 - * @param filter 表示meta实例过滤器的 {@link MetaInstanceFilter}。 - * @param offset 表示查询到的meta版本的结果集在全量结果集中的偏移量的 64 位整数。 - * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数。 + * @param filter 表示 meta 实例过滤器的 {@link MetaInstanceFilter}。 + * @param offset 表示查询到的meta版本的结果集在全量结果集中的偏移量的 64 位整数的 {@code long}。 + * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数的 {@code int}。 * @param context 表示操作上下文的 {@link OperationContext}。 * @return 表示查询到的结果集的 {@link RangedResultSet}{@code <}{@link Instance}{@code >}。 */ @@ -90,8 +90,8 @@ RangedResultSet list(String versionId, MetaInstanceFilter filter, long * 查询meta实例。 * * @param versionId 表示实例所属meta唯一标识的 {@link String}。 - * @param offset 表示查询到的meta版本的结果集在全量结果集中的偏移量的 64 位整数。 - * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数。 + * @param offset 表示查询到的meta版本的结果集在全量结果集中的偏移量的 64 位整数的 {@code long}。 + * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数的 {@code int}。 * @param context 表示操作上下文的 {@link OperationContext}。 * @return 表示查询到的结果集的 {@link RangedResultSet}{@code <}{@link Instance}{@code >}。 */ @@ -102,7 +102,8 @@ RangedResultSet list(String versionId, MetaInstanceFilter filter, long * 查询meta实例。 * * @param ids 表示实例id集合。 - * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数。 + * @param offset 表示查询到的meta版本的结果集在全量结果集中的偏移量的 64 位整数的 {@code long}。 + * @param limit 表示查询到的meta版本的结果集中的最大数量的 32 位整数的 {@code int}。 * @param context 表示操作上下文的 {@link OperationContext}。 * @return 表示查询到的结果集的 {@link RangedResultSet}{@code <}{@link Instance}{@code >}。 */ diff --git a/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_file_create_table.sql b/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_file_create_table.sql deleted file mode 100644 index ddceb4e1f9..0000000000 --- a/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_file_create_table.sql +++ /dev/null @@ -1,8 +0,0 @@ -create table if not exists aipp_instance_file -( - file_id bigserial not null primary key, - aipp_id varchar(255) not null, - filename varchar(255) not null, - create_at timestamp not null default current_timestamp, - create_by varchar(64) not null -); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_log_create_table.sql b/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_log_create_table.sql deleted file mode 100644 index 951f290849..0000000000 --- a/app-builder/jane/jober/sql/Aipp/aipp/aipp_inst_log_create_table.sql +++ /dev/null @@ -1,13 +0,0 @@ -create table if not exists aipp_instance_log -( - log_id bigserial not null primary key, - aipp_id varchar(255) not null, - version varchar(255), - aipp_type varchar(64), - instance_id varchar(255) not null, - log_data json not null, - log_type varchar(64) not null, - create_at timestamp default current_timestamp, - create_by varchar(64) not null, - reserve json -); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/Aipp/form/form_data_create_table.sql b/app-builder/jane/jober/sql/Aipp/form/form_data_create_table.sql deleted file mode 100644 index b1eb7b15f6..0000000000 --- a/app-builder/jane/jober/sql/Aipp/form/form_data_create_table.sql +++ /dev/null @@ -1,21 +0,0 @@ -create table if not exists form_data( - form_id varchar(64) not null COMMENT '表单id', - form_version varchar(32) not null COMMENT '表单版本', - form_name varchar(256) not null COMMENT '表单名称', - tenant_id varchar(64) COMMENT '租户id', - update_time timestamp COMMENT '更新时间', - update_user varchar(64) COMMENT '更新人', - create_time timestamp not null COMMENT '创建时间', - create_user varchar(64) COMMENT '创建人', - constraint form_pkey primary key (form_id, form_version) COMMENT '主键' -) COMMENT '表单数据表'; - - -create or replace function update_time() returns trigger as $$ -begin - NEW.update_time = now(); - return NEW; -end; -$$ language plpgsql; - -create trigger form_data_updater before UPDATE on form_data for each row execute function update_time(); diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_app_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_app_create_table.sql deleted file mode 100644 index 46b0cc8c45..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_app_create_table.sql +++ /dev/null @@ -1,16 +0,0 @@ -create table if not exists app_builder_app -( - id varchar(64) not null primary key comment '主键id', - name varchar(255) not null comment 'app名称', - create_by varchar(64) not null comment '创建人', - create_at timestamp not null default current_timestamp comment '创建时间', - update_by varchar(64) not null comment '更新人', - update_at timestamp not null default current_timestamp comment '更新时间', - config_id varchar(255) not null comment '关联配置表id', - flow_graph_id varchar(255) not null comment '关联流程图表id', - tenant_id varchar(255) not null comment '租户id', - type varchar(255) not null comment 'app类型', - attributes json not null default '{}' comment '属性', - state varchar(255) not null comment '状态', - app_built_type varchar(8) not null default 'basic' comment '应用构建类型' -) comment 'app主表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_component_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_component_create_table.sql deleted file mode 100644 index e5fe159861..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_component_create_table.sql +++ /dev/null @@ -1,14 +0,0 @@ -create table if not exists app_builder_component -( - id varchar(64) not null primary key COMMENT '主键id', - name varchar(255) not null COMMENT '名称', - type varchar(255) not null COMMENT '类型', - description varchar(255) not null COMMENT '描述', - form_id varchar(255) not null COMMENT '表单id', - service_id varchar(255) not null COMMENT '服务id', - tenant_id varchar(255) not null COMMENT '租户id', - create_by varchar(64) not null COMMENT '创建人', - create_at timestamp not null default current_timestamp COMMENT '创建时间', - update_by varchar(64) not null COMMENT '更新人', - update_at timestamp not null default current_timestamp COMMENT '更新时间' -) COMMENT 'app组件表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_config_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_config_create_table.sql deleted file mode 100644 index 05c6d3c33d..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_config_create_table.sql +++ /dev/null @@ -1,11 +0,0 @@ -create table if not exists app_builder_config -( - id varchar(64) not null primary key COMMENT '主键id', - form_id varchar(255) not null COMMENT '表单表id', - app_id varchar(255) not null COMMENT 'app应用id', - tenant_id varchar(255) not null COMMENT '租户id', - create_by varchar(64) not null COMMENT '创建人', - create_at timestamp not null default current_timestamp COMMENT '创建时间', - update_by varchar(64) not null COMMENT '更新人', - update_at timestamp not null default current_timestamp COMMENT '更新时间' -) COMMENT 'app配置表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_config_property_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_config_property_create_table.sql deleted file mode 100644 index 424f202caa..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_config_property_create_table.sql +++ /dev/null @@ -1,7 +0,0 @@ -create table if not exists app_builder_config_property -( - id varchar(64) not null primary key COMMENT '主键id', - node_id varchar(255) COMMENT 'node_id', - form_property_id varchar(255) not null COMMENT '配置表单项id', - config_id varchar(64) not null COMMENT '关联配置表id' -) COMMENT 'app配置项表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_flow_graph_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_flow_graph_create_table.sql deleted file mode 100644 index 275871c25d..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_flow_graph_create_table.sql +++ /dev/null @@ -1,10 +0,0 @@ -create table if not exists app_builder_flow_graph -( - id varchar(64) not null primary key COMMENT '主键id', - name varchar(255) not null COMMENT '名称', - create_by varchar(64) not null COMMENT '创建人', - create_at timestamp not null default current_timestamp COMMENT '创建时间', - update_by varchar(64) not null COMMENT '更新人', - update_at timestamp not null default current_timestamp COMMENT '更新时间', - appearance TEXT COMMENT '描述' -) COMMENT 'app流程图表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_form_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_form_create_table.sql deleted file mode 100644 index e55af5c12b..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_form_create_table.sql +++ /dev/null @@ -1,12 +0,0 @@ -create table if not exists app_builder_form -( - id varchar(64) not null primary key COMMENT '主键id', - name varchar(255) not null COMMENT '名称', - tenant_id varchar(255) not null COMMENT '租户id', - appearance TEXT COMMENT '描述', - type varchar(64) not null COMMENT '类型', - create_by varchar(64) not null COMMENT '创建人', - create_at timestamp not null default current_timestamp COMMENT '创建时间', - update_by varchar(64) not null COMMENT '更新人', - update_at timestamp not null default current_timestamp COMMENT '更新时间' -) COMMENT 'app表单表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/appbuilder/app_builder_form_property_create_table.sql b/app-builder/jane/jober/sql/appbuilder/app_builder_form_property_create_table.sql deleted file mode 100644 index 501532c314..0000000000 --- a/app-builder/jane/jober/sql/appbuilder/app_builder_form_property_create_table.sql +++ /dev/null @@ -1,12 +0,0 @@ -create table if not exists app_builder_form_property -( - id varchar(64) not null primary key comment '主键id', - form_id varchar(255) not null comment '表单id', - name varchar(255) not null comment '名称', - data_type varchar(255) not null comment '数据类型', - default_value text comment '默认值', - data_from varchar(8) not null comment '数据来源', - in_group varchar(8) not null comment '应用所属的组', - description varchar(64) not null comment '应用描述', - is_deleted smallint default 0 comment '应用是否删除' -) comment 'app表单项表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/dataengine/dataengine.postgres.create.sql b/app-builder/jane/jober/sql/create/dataengine/dataengine.postgres.create.sql deleted file mode 100644 index 0ed35effde..0000000000 --- a/app-builder/jane/jober/sql/create/dataengine/dataengine.postgres.create.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE IF NOT EXISTS time_scheduler -( - scheduler_id VARCHAR(32) PRIMARY KEY, - task_definition_id VARCHAR(32) NOT NULL, - task_source_id VARCHAR(32) NOT NULL, - scheduler_data_type VARCHAR(63) NOT NULL, - source_app VARCHAR(63) NOT NULL, - create_time BIGINT NOT NULL, - end_time BIGINT NOT NULL, - scheduler_interval BIGINT NOT NULL, - latest_executor_time BIGINT NOT NULL, - filter JSONB, - properties JSONB, - modify_time BIGINT NOT NULL, - owner_address VARCHAR(32), - task_type_id VARCHAR(32) -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_SOURCE_ID ON time_scheduler (task_source_id); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.owner.to.list.create.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.owner.to.list.create.sql deleted file mode 100644 index 676d6aa14c..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.owner.to.list.create.sql +++ /dev/null @@ -1,39 +0,0 @@ ---将wide表中的text_4(owner)转为列表,序列 -CREATE SEQUENCE "list_index" START 1 INCREMENT 1; -INSERT INTO list_text(id,instance_id,property_id,index,value) -SELECT generate_uuid_text() AS id, w.id AS instance_id, - (SELECT id FROM task_property WHERE template_id IN (SELECT id FROM task_template_property WHERE task_template_id = '4f91b69973d1453aadf384d508aed894' AND name = 'owner') AND task_id=w.task_id) AS property_id, - CASE - WHEN row_number() OVER (PARTITION BY w.id ORDER BY w.id,s.index)=1 - THEN - SETVAL('list_index', 1) - ELSE - NEXTVAL('list_index') - END AS index, s.value -from task_instance_wide w, - regexp_split_to_table(w.text_4, ',') WITH ORDINALITY AS s(value, index) where w.id in (SELECT s.id from task_instance_wide s INNER JOIN task t ON s.task_id=t.id WHERE t.template_id IN (SELECT find_template_children('4f91b69973d1453aadf384d508aed894') AS id)); -DROP SEQUENCE "list_index"; - ---验证数量是否正确 -SELECT DISTINCT ON (instance_id) id FROM list_text; -SELECT count(1) FROM task_instance_wide; - ---将普通任务模板中的处理人和审批任务中的审批人属性的值.将属性的数据类型修改为LIST_TEXT,sequence修改为0 ---修改task_template_property和task_property表中owner对应的类型 - -UPDATE task_template_property SET data_type='LIST_TEXT',sequence=0 WHERE name='owner'; -UPDATE task_property SET data_type='LIST_TEXT',sequence=0 WHERE template_id = (SELECT id FROM task_template_property WHERE name='owner'); - ---将List_text中的值除index,添加到index_text中 -INSERT INTO index_text(id,instance_id,property_id,value) -SELECT generate_uuid_text(),instance_id,property_id,value -FROM list_text; - - ---回退:清空list_text表中所有数据 -TRUNCATE TABLE list_text; ---回退task_template_property和task_property表 -UPDATE task_template_property SET data_type='TEXT',sequence=4 WHERE name='owner'; -UPDATE task_property SET data_type='TEXT',sequence=4 WHERE template_id = (SELECT id FROM task_template_property WHERE name='owner'); ---回退index_text表 -DELETE FROM index_text WHERE instance_id IN (SELECT instance_id FROM list_text); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create.sql deleted file mode 100644 index fcbfa7de0b..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create.sql +++ /dev/null @@ -1,837 +0,0 @@ -CREATE TABLE IF NOT EXISTS task_tree -( - tree_id VARCHAR(32) PRIMARY KEY, - tree_name VARCHAR(255) NOT NULL, - tenant VARCHAR(255) NOT NULL, - is_deleted BOOLEAN DEFAULT FALSE, - created_by VARCHAR(127) NOT NULL, - updated_by VARCHAR(127), - deleted_by VARCHAR(32), - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone, - deleted_at timestamp without time zone -); -CREATE UNIQUE INDEX IF NOT EXISTS idx_task_tree_name_tenant_deleted_at ON task_tree (tree_name, tenant, deleted_at); - -CREATE TABLE IF NOT EXISTS task_tree_node -( - node_id VARCHAR(32) PRIMARY KEY, - node_name VARCHAR(255) NOT NULL, - tree_id VARCHAR(32) NOT NULL, - parent_id VARCHAR(32), - node_level INT NOT NULL, - is_deleted BOOLEAN DEFAULT FALSE, - created_by VARCHAR(127) NOT NULL, - updated_by VARCHAR(127), - deleted_by VARCHAR(32), - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone, - deleted_at timestamp without time zone -); - -CREATE TABLE IF NOT EXISTS task_definition -( - definition_id VARCHAR(32) PRIMARY KEY, - reference_id VARCHAR(32) NOT NULL, - task_detail TEXT, - task_type VARCHAR(127) NOT NULL, - version INT NOT NULL, - tenant VARCHAR(255) NOT NULL, - is_deleted BOOLEAN DEFAULT FALSE, - created_by VARCHAR(127) NOT NULL, - updated_by VARCHAR(127), - deleted_by VARCHAR(32), - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone, - deleted_at timestamp without time zone -); - -CREATE TABLE IF NOT EXISTS task_definition_props -( - props_id VARCHAR(32) PRIMARY KEY, - definition_id VARCHAR(32) NOT NULL, - props_name VARCHAR(63) NOT NULL, - props_key VARCHAR(63) NOT NULL, - props_icon VARCHAR(63), - props_description VARCHAR(255), - props_value_type VARCHAR(63) NOT NULL, - props_value JSONB, - props_type VARCHAR(127) NOT NULL, - props_order INT NOT NULL, - is_required BOOLEAN DEFAULT TRUE, - is_deleted BOOLEAN DEFAULT FALSE, - display_props JSONB -); - -CREATE TABLE IF NOT EXISTS task_definition_props_handler -( - handler_id VARCHAR(32) PRIMARY KEY, - props_id VARCHAR(32) NOT NULL, - definition_source_id VARCHAR(32) NOT NULL, - props_updated_event VARCHAR(127), - props_updated_api VARCHAR(255), - is_deleted BOOLEAN DEFAULT FALSE -); - -CREATE TABLE IF NOT EXISTS task_definition_source -( - source_id VARCHAR(32) PRIMARY KEY, - definition_id VARCHAR(32) NOT NULL, - source_app VARCHAR(63) NOT NULL, - source_api VARCHAR(255), - source_handler VARCHAR(63) NOT NULL, - properties TEXT, - is_deleted BOOLEAN DEFAULT FALSE -); - -CREATE TABLE IF NOT EXISTS task_instance -( - instance_id VARCHAR(32) PRIMARY KEY, - definition_id VARCHAR(32) NOT NULL, - source_definition_id VARCHAR(32) NOT NULL, - field_value_1 TEXT, - field_value_2 TEXT, - field_value_3 TEXT, - field_value_4 TEXT, - field_value_5 TEXT, - field_value_6 TEXT, - field_value_7 TEXT, - field_value_8 TEXT, - field_value_9 TEXT, - field_value_10 TEXT, - field_value_11 TEXT, - field_value_12 TEXT, - field_value_13 TEXT, - field_value_14 TEXT, - field_value_15 TEXT, - field_value_16 TEXT, - field_value_17 TEXT, - field_value_18 TEXT, - field_value_19 TEXT, - field_value_20 TEXT, - field_value_21 TEXT, - field_value_22 TEXT, - field_value_23 TEXT, - field_value_24 TEXT, - field_value_25 TEXT, - field_value_26 TEXT, - field_value_27 TEXT, - field_value_28 TEXT, - field_value_29 TEXT, - field_value_30 TEXT, - field_value_31 TEXT, - field_value_32 TEXT, - field_value_33 TEXT, - field_value_34 TEXT, - field_value_35 TEXT, - field_value_36 TEXT, - field_value_37 TEXT, - field_value_38 TEXT, - field_value_39 TEXT, - field_value_40 TEXT, - field_value_41 TEXT, - field_value_42 TEXT, - field_value_43 TEXT, - field_value_44 TEXT, - field_value_45 TEXT, - field_value_46 TEXT, - field_value_47 TEXT, - field_value_48 TEXT, - field_value_49 TEXT, - field_value_50 TEXT, - field_value_51 TEXT, - field_value_52 TEXT, - field_value_53 TEXT, - field_value_54 TEXT, - field_value_55 TEXT, - field_value_56 TEXT, - field_value_57 TEXT, - field_value_58 TEXT, - field_value_59 TEXT, - field_value_60 TEXT, - field_value_61 TEXT, - field_value_62 TEXT, - field_value_63 TEXT, - field_value_64 TEXT, - field_value_65 TEXT, - field_value_66 TEXT, - field_value_67 TEXT, - field_value_68 TEXT, - field_value_69 TEXT, - field_value_70 TEXT, - field_value_71 TEXT, - field_value_72 TEXT, - field_value_73 TEXT, - field_value_74 TEXT, - field_value_75 TEXT, - field_value_76 TEXT, - field_value_77 TEXT, - field_value_78 TEXT, - field_value_79 TEXT, - field_value_80 TEXT, - field_value_81 TEXT, - field_value_82 TEXT, - field_value_83 TEXT, - field_value_84 TEXT, - field_value_85 TEXT, - field_value_86 TEXT, - field_value_87 TEXT, - field_value_88 TEXT, - field_value_89 TEXT, - field_value_90 TEXT, - field_value_91 TEXT, - field_value_92 TEXT, - field_value_93 TEXT, - field_value_94 TEXT, - field_value_95 TEXT, - field_value_96 TEXT, - field_value_97 TEXT, - field_value_98 TEXT, - field_value_99 TEXT, - field_value_100 TEXT, - is_deleted BOOLEAN DEFAULT FALSE -); - -CREATE TABLE IF NOT EXISTS task_tag -( - tag_id VARCHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - tenant VARCHAR(255) NOT NULL -); - -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_NAME_TENANT ON task_tag (name, tenant); - -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('41d00a31a9b34aa39b606d80fc426c2e', '未开始', 'public'); -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('8d038441b7514f5b99ea18992126cfa8', '进行中', 'public'); -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('4c4ddb220bfe4fe98374aff710afb3eb', '已完成', 'public'); -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('ad31edc774e24bfbb15e87084744e259', '有风险', 'public'); -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('e72a94b24e704e5abcffc920394f4a19', '无风险', 'public'); - - -CREATE TABLE IF NOT EXISTS task_source_schedule -( - id CHAR(32) PRIMARY KEY, - fitable_id CHAR(32) NOT NULL, - "interval" INTEGER NOT NULL, - filter VARCHAR(512) NOT NULL -); - -CREATE TABLE IF NOT EXISTS task_source -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - app VARCHAR(64) NOT NULL, - type VARCHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_SOURCE ON task_source (task_id, name, app); -CREATE INDEX IF NOT EXISTS IDX_TASK_SOURCE_TASK ON task_source (task_id); - -CREATE TABLE IF NOT EXISTS task_property_trigger -( - id CHAR(32) PRIMARY KEY, - task_source_id CHAR(32) NOT NULL, - task_property_id CHAR(32) NOT NULL, - fitable_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_PROPERTY_TRIGGER ON task_property_trigger (task_source_id, task_property_id, fitable_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_SOURCE ON task_property_trigger (task_source_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_PROPERTY ON task_property_trigger (task_property_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_FITABLE ON task_property_trigger (fitable_id); - -CREATE TABLE IF NOT EXISTS task_property -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - required BOOLEAN NOT NULL, - identifiable BOOLEAN NOT NULL, - description VARCHAR(512) NOT NULL, - scope VARCHAR(16) NOT NULL, - data_type VARCHAR(16) NOT NULL, - sequence INTEGER NOT NULL, - appearance JSON NOT NULL, - template_id CHAR(32) NOT NULL DEFAULT '00000000000000000000000000000000' -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_PROPERTY ON task_property (task_id, name); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_DATA_SEQUENCE ON task_property (task_id, data_type, sequence); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TASK ON task_property (task_id); - -CREATE TABLE IF NOT EXISTS task -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - tenant_id CHAR(32) NOT NULL, - attributes JSON NOT NULL DEFAULT '{}', - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP(9) NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP(9) NOT NULL, - template_id CHAR(32) NOT NULL DEFAULT '00000000000000000000000000000000', - category VARCHAR(32) NOT NULL DEFAULT 'TASK' -); -CREATE UNIQUE INDEX IF NOT EXISTS idx_task_id_name ON task (id, name); - -CREATE TABLE IF NOT EXISTS task_tree_task -( - id CHAR(32) PRIMARY KEY, - tree_id CHAR(32) NOT NULL, - task_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TREE_TASK_TREE_TASK ON task_tree_task (tree_id, task_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TREE_TASK_TREE ON task_tree_task (tree_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TREE_TASK_TASK ON task_tree_task (task_id); - -CREATE TABLE IF NOT EXISTS task_tree_v2 -( - id char(32) PRIMARY KEY, - name varchar(64) NOT NULL, - created_by varchar(127) NOT NULL, - created_at timestamp(6) NOT NULL, - updated_by varchar(127) NOT NULL, - updated_at timestamp(6) NOT NULL -); - -CREATE TABLE IF NOT EXISTS task_instance_wide -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - task_type_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - text_1 TEXT, - text_2 TEXT, - text_3 TEXT, - text_4 TEXT, - text_5 TEXT, - text_6 TEXT, - text_7 TEXT, - text_8 TEXT, - text_9 TEXT, - text_10 TEXT, - text_11 TEXT, - text_12 TEXT, - text_13 TEXT, - text_14 TEXT, - text_15 TEXT, - text_16 TEXT, - text_17 TEXT, - text_18 TEXT, - text_19 TEXT, - text_20 TEXT, - text_21 TEXT, - text_22 TEXT, - text_23 TEXT, - text_24 TEXT, - text_25 TEXT, - text_26 TEXT, - text_27 TEXT, - text_28 TEXT, - text_29 TEXT, - text_30 TEXT, - text_31 TEXT, - text_32 TEXT, - text_33 TEXT, - text_34 TEXT, - text_35 TEXT, - text_36 TEXT, - text_37 TEXT, - text_38 TEXT, - text_39 TEXT, - text_40 TEXT, - text_41 TEXT, - text_42 TEXT, - text_43 TEXT, - text_44 TEXT, - text_45 TEXT, - text_46 TEXT, - text_47 TEXT, - text_48 TEXT, - text_49 TEXT, - text_50 TEXT, - integer_1 INTEGER, - integer_2 INTEGER, - integer_3 INTEGER, - integer_4 INTEGER, - integer_5 INTEGER, - integer_6 INTEGER, - integer_7 INTEGER, - integer_8 INTEGER, - integer_9 INTEGER, - integer_10 INTEGER, - integer_11 INTEGER, - integer_12 INTEGER, - integer_13 INTEGER, - integer_14 INTEGER, - integer_15 INTEGER, - integer_16 INTEGER, - integer_17 INTEGER, - integer_18 INTEGER, - integer_19 INTEGER, - integer_20 INTEGER, - datetime_1 TIMESTAMP, - datetime_2 TIMESTAMP, - datetime_3 TIMESTAMP, - datetime_4 TIMESTAMP, - datetime_5 TIMESTAMP, - datetime_6 TIMESTAMP, - datetime_7 TIMESTAMP, - datetime_8 TIMESTAMP, - datetime_9 TIMESTAMP, - datetime_10 TIMESTAMP, - datetime_11 TIMESTAMP, - datetime_12 TIMESTAMP, - datetime_13 TIMESTAMP, - datetime_14 TIMESTAMP, - datetime_15 TIMESTAMP, - datetime_16 TIMESTAMP, - datetime_17 TIMESTAMP, - datetime_18 TIMESTAMP, - datetime_19 TIMESTAMP, - datetime_20 TIMESTAMP, - boolean_1 BOOLEAN, - boolean_2 BOOLEAN, - boolean_3 BOOLEAN, - boolean_4 BOOLEAN, - boolean_5 BOOLEAN, - boolean_6 BOOLEAN, - boolean_7 BOOLEAN, - boolean_8 BOOLEAN, - boolean_9 BOOLEAN, - boolean_10 BOOLEAN, - boolean_11 BOOLEAN, - boolean_12 BOOLEAN, - boolean_13 BOOLEAN, - boolean_14 BOOLEAN, - boolean_15 BOOLEAN, - boolean_16 BOOLEAN, - boolean_17 BOOLEAN, - boolean_18 BOOLEAN, - boolean_19 BOOLEAN, - boolean_20 BOOLEAN -); - -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_TASK ON task_instance_wide (task_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_SOURCE ON task_instance_wide (source_id); - -CREATE TABLE IF NOT EXISTS task_type -( - id CHAR(32) PRIMARY KEY, - tree_id CHAR(32) NOT NULL, - parent_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL, - task_id CHAR(32) -); -CREATE INDEX IF NOT EXISTS IDX_TASK_TYPE_TREE ON task_type (tree_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TYPE_PARENT ON task_type (parent_id); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TYPE_NAME ON task_type (task_id, name); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TYPE_TREE_NAME ON task_type (tree_id, name); - -CREATE TABLE IF NOT EXISTS task_node_source -( - id CHAR(32) PRIMARY KEY, - node_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_NODE_SOURCE ON task_node_source (node_id, source_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_NODE_SOURCE_NODE ON task_node_source (node_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_NODE_SOURCE_SOURCE ON task_node_source (source_id); - -CREATE TABLE IF NOT EXISTS tag -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG ON tag (name); - -CREATE TABLE IF NOT EXISTS tag_usage -( - id CHAR(32) PRIMARY KEY, - tag_id CHAR(32) NOT NULL, - object_type VARCHAR(16) NOT NULL, - object_id CHAR(32) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG_USAGE ON tag_usage (tag_id, object_id, object_type); -CREATE INDEX IF NOT EXISTS IDX_TAG_OBJECT_ID ON tag_usage (object_id); - -CREATE TABLE IF NOT EXISTS task_category_trigger -( - "id" CHAR(32) PRIMARY KEY, - "task_id" CHAR(32) NOT NULL, - "category_id" CHAR(32) NOT NULL, - "fitable_id" CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS "UK_TASK_CATEGORY_TRIGGER" ON task_category_trigger ("task_id", "category_id", "fitable_id"); -CREATE INDEX IF NOT EXISTS "IDX_TASK_CATEGORY_TRIGGER_TASK" ON task_category_trigger ("task_id"); - -CREATE TABLE IF NOT EXISTS category_group -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_GROUP_NAME ON category_group (name); - -CREATE TABLE IF NOT EXISTS category -( - id CHAR(32) PRIMARY KEY, - category_group_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -comment on table category is '类目'; -comment on column category.id is '表示类目的唯一标识。由32位16进制字符组成。'; -comment on column category.category_group_id is '表示类目组的唯一标识。'; -comment on column category.name is '表示类目的名称类型。'; -comment on column category.description is '表示类目的描述信息。'; -comment on column category.created_by is '表示创建人。'; -comment on column category.created_at is '表示创建时间。'; -comment on column category.updated_by is '表示最近的修改人。未被修改时与创建人相同。'; -comment on column category.updated_at is '表示最近的修改时间。未被修改时与创建时间相同。'; - -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_GROUP ON category (category_group_id); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_NAME ON category (name); - -CREATE TABLE IF NOT EXISTS category_matcher -( - id CHAR(32) PRIMARY KEY, - category_id CHAR(32) NOT NULL, - property_id CHAR(32) NOT NULL, - "value" TEXT NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_MATCHER ON category_matcher (category_id, property_id, "value"); -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_MATCHER_CATEGORY ON category_matcher (category_id); -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_MATCHER_PROPERTY ON category_matcher (property_id); - -CREATE TABLE IF NOT EXISTS task_instance_event ( - id CHAR(32) PRIMARY KEY, - source_id CHAR(32) NOT NULL, - event_type VARCHAR(16) NOT NULL, - fitable_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_INSTANCE_EVENT ON task_instance_event(source_id, event_type, fitable_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_EVENT_SOURCE ON task_instance_event(source_id); - -CREATE TABLE IF NOT EXISTS tenant -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - description TEXT, - avatar_id CHAR(32), - created_by VARCHAR(127) NOT NULL, - updated_by VARCHAR(127), - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone, - access_level VARCHAR(32) DEFAULT 'PRIVATE' -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TENANT ON tenant (name); - -CREATE TABLE IF NOT EXISTS tenant_member -( - id CHAR(32) PRIMARY KEY, - tenant_id CHAR(32) NOT NULL, - user_id VARCHAR(127) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at timestamp without time zone NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TENANT_ID_USER_ID ON tenant_member (tenant_id, user_id); -CREATE INDEX IF NOT EXISTS IDX_TENANT_ID_TENANT_MEMBER ON tenant_member (tenant_id); - -CREATE TABLE IF NOT EXISTS file -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(127) NOT NULL, - content BYTEA, - created_by VARCHAR(127) NOT NULL, - created_at timestamp without time zone NOT NULL, - type VARCHAR(32) -); - -CREATE TABLE IF NOT EXISTS task_source_refresh_in_time ( - id CHAR(32) PRIMARY KEY, - metadata JSONB NOT NULL, - create_fitable_id CHAR(32) NOT NULL, - patch_fitable_id CHAR(32) NOT NULL, - delete_fitable_id CHAR(32) NOT NULL, - retrieve_fitable_id CHAR(32) NOT NULL, - list_fitable_id CHAR(32) NOT NULL -); - -CREATE TABLE "authorization" ( - "id" CHAR(32) PRIMARY KEY, - "system" VARCHAR(64) NOT NULL, - "user" VARCHAR(127) NOT NULL, - "token" TEXT NOT NULL, - "expiration" INT8 NOT NULL, - "created_by" VARCHAR(127) NOT NULL, - "created_at" TIMESTAMP NOT NULL, - "updated_by" VARCHAR(127) NOT NULL, - "updated_at" TIMESTAMP NOT NULL -); - -CREATE TABLE IF NOT EXISTS task_instance_deleted -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - task_type_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - text_1 TEXT, - text_2 TEXT, - text_3 TEXT, - text_4 TEXT, - text_5 TEXT, - text_6 TEXT, - text_7 TEXT, - text_8 TEXT, - text_9 TEXT, - text_10 TEXT, - text_11 TEXT, - text_12 TEXT, - text_13 TEXT, - text_14 TEXT, - text_15 TEXT, - text_16 TEXT, - text_17 TEXT, - text_18 TEXT, - text_19 TEXT, - text_20 TEXT, - text_21 TEXT, - text_22 TEXT, - text_23 TEXT, - text_24 TEXT, - text_25 TEXT, - text_26 TEXT, - text_27 TEXT, - text_28 TEXT, - text_29 TEXT, - text_30 TEXT, - text_31 TEXT, - text_32 TEXT, - text_33 TEXT, - text_34 TEXT, - text_35 TEXT, - text_36 TEXT, - text_37 TEXT, - text_38 TEXT, - text_39 TEXT, - text_40 TEXT, - text_41 TEXT, - text_42 TEXT, - text_43 TEXT, - text_44 TEXT, - text_45 TEXT, - text_46 TEXT, - text_47 TEXT, - text_48 TEXT, - text_49 TEXT, - text_50 TEXT, - integer_1 INTEGER, - integer_2 INTEGER, - integer_3 INTEGER, - integer_4 INTEGER, - integer_5 INTEGER, - integer_6 INTEGER, - integer_7 INTEGER, - integer_8 INTEGER, - integer_9 INTEGER, - integer_10 INTEGER, - integer_11 INTEGER, - integer_12 INTEGER, - integer_13 INTEGER, - integer_14 INTEGER, - integer_15 INTEGER, - integer_16 INTEGER, - integer_17 INTEGER, - integer_18 INTEGER, - integer_19 INTEGER, - integer_20 INTEGER, - datetime_1 TIMESTAMP, - datetime_2 TIMESTAMP, - datetime_3 TIMESTAMP, - datetime_4 TIMESTAMP, - datetime_5 TIMESTAMP, - datetime_6 TIMESTAMP, - datetime_7 TIMESTAMP, - datetime_8 TIMESTAMP, - datetime_9 TIMESTAMP, - datetime_10 TIMESTAMP, - datetime_11 TIMESTAMP, - datetime_12 TIMESTAMP, - datetime_13 TIMESTAMP, - datetime_14 TIMESTAMP, - datetime_15 TIMESTAMP, - datetime_16 TIMESTAMP, - datetime_17 TIMESTAMP, - datetime_18 TIMESTAMP, - datetime_19 TIMESTAMP, - datetime_20 TIMESTAMP, - boolean_1 BOOLEAN, - boolean_2 BOOLEAN, - boolean_3 BOOLEAN, - boolean_4 BOOLEAN, - boolean_5 BOOLEAN, - boolean_6 BOOLEAN, - boolean_7 BOOLEAN, - boolean_8 BOOLEAN, - boolean_9 BOOLEAN, - boolean_10 BOOLEAN, - boolean_11 BOOLEAN, - boolean_12 BOOLEAN, - boolean_13 BOOLEAN, - boolean_14 BOOLEAN, - boolean_15 BOOLEAN, - boolean_16 BOOLEAN, - boolean_17 BOOLEAN, - boolean_18 BOOLEAN, - boolean_19 BOOLEAN, - boolean_20 BOOLEAN - ); - -CREATE TABLE IF NOT EXISTS operation_record -( - id char(32) PRIMARY KEY, - object_type varchar(25) NOT NULL, - object_id char(32) NOT NULL, - operator varchar(127) NOT NULL, - operated_time timestamp without time zone, - message TEXT NOT NULL, - operate char(7) NOT NULL -); - -CREATE INDEX IDX_OPERATION_RECORD_OBJECT_TYPE_OBJECT_ID_OPERATED_TIME ON operation_record (object_type,object_id,operated_time); - -CREATE TABLE IF NOT EXISTS task_source_refresh_in_time ( - id CHAR(32) PRIMARY KEY, - metadata JSONB NOT NULL, - create_fitable_id CHAR(32) NOT NULL, - patch_fitable_id CHAR(32) NOT NULL, - delete_fitable_id CHAR(32) NOT NULL, - retrieve_fitable_id CHAR(32) NOT NULL, - list_fitable_id CHAR(32) NOT NULL - ); - -CREATE TABLE IF NOT EXISTS "category_usage" ( - "id" CHAR(32) PRIMARY KEY, - "object_type" VARCHAR(16) NOT NULL, - "object_id" CHAR(32) NOT NULL, - "category_group_id" CHAR(32) NOT NULL, - "category_id" CHAR(32) NOT NULL, - "created_by" VARCHAR(127) NOT NULL, - "created_at" TIMESTAMP NOT NULL, - "updated_by" VARCHAR(127) NOT NULL, - "updated_at" TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX "UK_CATEGORY_USAGE" ON "category_usage"("object_id", "category_group_id", "object_type"); -CREATE INDEX "IDX_CATEGORY_USAGE_OBJECT_ID" ON "category_usage"("object_id"); - -/* 创建任务模板表 */ -CREATE TABLE IF NOT EXISTS task_template -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL -); -CREATE UNIQUE INDEX UK_TASK_TEMPLATE_NAME ON task_template (name); -COMMENT on column task_template.id is '主键Id'; -COMMENT on column task_template.name is '任务模板名称'; -COMMENT on column task_template.description is '任务模板描述'; - -/* 创建任务模板属性表 */ -CREATE TABLE IF NOT EXISTS task_template_property -( - id CHAR(32) PRIMARY KEY, - task_template_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - data_type VARCHAR(16) NOT NULL, - sequence INTEGER NOT NULL -); -CREATE UNIQUE INDEX UK_TASK_TEMPLATE_PROPERTY_NAME ON task_template_property (task_template_id, name); -CREATE UNIQUE INDEX UK_TASK_TEMPLATE_PROPERTY_COLUMN ON task_template_property (task_template_id, data_type, sequence); - -COMMENT on column task_template_property.id is '主键Id'; -COMMENT on column task_template_property.task_template_id is '任务模板Id'; -COMMENT on column task_template_property.name is '任务模板属性名称'; -COMMENT on column task_template_property.data_type is '任务模板属性数据类型'; -COMMENT on column task_template_property.sequence is '任务模板属性在当前数据类型中的序号'; - -CREATE TABLE "index" ( - "id" CHAR(32) PRIMARY KEY, - "task_id" CHAR(32) NOT NULL, - "name" VARCHAR(128) NOT NULL, - "created_by" VARCHAR(127) NOT NULL, - "created_at" TIMESTAMP NOT NULL, - "updated_by" VARCHAR(127) NOT NULL, - "updated_at" TIMESTAMP NOT NULL -); - -CREATE TABLE "index_property" ( - "id" CHAR(32) PRIMARY KEY, - "index_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL -); -CREATE INDEX "idx_index_property_index" ON "index_property"("index_id"); - -CREATE TABLE "index_text" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" TEXT -); -CREATE INDEX "idx_index_text_instance" ON "index_text"("instance_id"); -CREATE INDEX "idx_index_text_property" ON "index_text"("property_id"); -CREATE INDEX "idx_index_text_value" ON "index_text"("value"); - -CREATE TABLE "index_integer" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" BIGINT -); -CREATE INDEX "idx_index_integer_instance" ON "index_integer"("instance_id"); -CREATE INDEX "idx_index_integer_property" ON "index_integer"("property_id"); -CREATE INDEX "idx_index_integer_value" ON "index_integer"("value"); - -CREATE TABLE "index_datetime" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" TIMESTAMP -); -CREATE INDEX "idx_index_datetime_instance" ON "index_datetime"("instance_id"); -CREATE INDEX "idx_index_datetime_property" ON "index_datetime"("property_id"); -CREATE INDEX "idx_index_datetime_value" ON "index_datetime"("value"); - -CREATE TABLE "list_text" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "index" BIGINT NOT NULL, - "value" TEXT -); -CREATE INDEX "IDX_LIST_TEXT_INSTANCE" ON "list_text"("instance_id"); -CREATE INDEX "IDX_LIST_TEXT_PROPERTY" ON "list_text"("property_id"); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create_compatible_with_h2.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create_compatible_with_h2.sql deleted file mode 100644 index 984bfe1f97..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.create_compatible_with_h2.sql +++ /dev/null @@ -1,66 +0,0 @@ -CREATE -OR REPLACE FUNCTION generate_uuid_text() -RETURNS CHAR(32) AS $$ -BEGIN -RETURN md5(random()::text || clock_timestamp()::text); -END; -$$ -LANGUAGE plpgsql; - -CREATE -OR REPLACE FUNCTION now_utc() -RETURNS TIMESTAMP AS $$ -BEGIN -RETURN CURRENT_TIMESTAMP AT TIME ZONE 'UTC'; -END; -$$ -LANGUAGE plpgsql; - -INSERT INTO category_group(id, name, description, created_by, created_at, updated_by, updated_at) -VALUES (generate_uuid_text(), 'status', '表示系统级别的状态定义。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), 'health', '表示系统级别的健康度定义。', 'admin', now_utc(), 'admin', now_utc()); - -INSERT INTO category(id, category_group_id, name, description, created_by, created_at, updated_by, - updated_at) -VALUES (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '未开始', - '表示任务尚未开始处理。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '处理中', - '表示任务正在处理中。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '已完成', - '表示任务已闭环完成。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'health'), '无风险', - '表示当前任务的进展不存在风险。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'health'), '风险', - '表示当前任务存在风险。', 'admin', now_utc(), 'admin', now_utc()); - -UPDATE task_type -SET task_id = '2479308f35ab4cc492892aea265b2025' -WHERE tree_id = '6a5db7109bb546109f2887347cf42054' -UPDATE task_type -SET task_id = '265520c71c534393af4678129ac90719' -WHERE tree_id = '2abeca128a194064a2777f1882ea2967' - -UPDATE task_type -SET parent_id = tree_id -WHERE parent_id = '00000000000000000000000000000000'; - -CREATE TABLE IF NOT EXISTS task_source_refresh_in_time ( - id CHAR(32) PRIMARY KEY COMMENT '主键id', - create_fitable_id CHAR(32) NOT NULL COMMENT 'create_fitable_id', - patch_fitable_id CHAR(32) NOT NULL COMMENT 'patch_fitable_id', - delete_fitable_id CHAR(32) NOT NULL COMMENT 'delete_fitable_id', - retrieve_fitable_id CHAR(32) NOT NULL COMMENT 'retrieve_fitable_id', - list_fitable_id CHAR(32) NOT NULL COMMENT 'list_fitable_id' -) COMMENT '任务源刷新表'; - -CREATE TABLE `authorization` ( - `id` CHAR(32) PRIMARY KEY COMMENT '主键id', - `system` VARCHAR(64) NOT NULL COMMENT '系统', - `user` VARCHAR(127) NOT NULL COMMENT '用户', - `token` TEXT NOT NULL COMMENT 'token', - `expiration` INTEGER NOT NULL COMMENT '过期时间', - `created_by` VARCHAR(127) NOT NULL COMMENT '创建人', - `created_at` TIMESTAMP NOT NULL COMMENT '创建时间', - `updated_by` VARCHAR(127) NOT NULL COMMENT '更新人', - `updated_at` TIMESTAMP NOT NULL COMMENT '更新时间' -) COMMENT '权限表'; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.data.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.data.sql deleted file mode 100644 index 56be7b64b7..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.data.sql +++ /dev/null @@ -1,18 +0,0 @@ -INSERT INTO task_template ("id", "name", "description") VALUES ('4f91b69973d1453aadf384d508aed894', '普通任务', '最基础的默认模板'); -INSERT INTO task_template ("id", "name", "description") VALUES ('00000000000000000000000000000000', 'empty', 'empty'); - -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('08f8fc32484d408aacbcff35bf8be687', '4f91b69973d1453aadf384d508aed894', 'id', 'TEXT', 1); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('abb064f84f36496bb769321b1f3443c9', '4f91b69973d1453aadf384d508aed894', 'decomposed_from', 'TEXT', 2); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('be686a0325694aadb688539f7523f88a', '4f91b69973d1453aadf384d508aed894', 'title', 'TEXT', 3); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('59e0d16c3fe541899778beb76e56b0e3', '4f91b69973d1453aadf384d508aed894', 'owner', 'TEXT', 4); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('e2af84d4a8714ce8a87be79d19a5eb88', '4f91b69973d1453aadf384d508aed894', 'created_by', 'TEXT', 5); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('4aebbf316cf443d791e0280f56f435d6', '4f91b69973d1453aadf384d508aed894', 'created_date', 'DATETIME', 1); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('387c16ae70ac49f68f670f54e4456fc8', '4f91b69973d1453aadf384d508aed894', 'modified_by', 'TEXT', 6); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('354944b0e9234429aabfbafb08059c2e', '4f91b69973d1453aadf384d508aed894', 'modified_date', 'DATETIME', 2); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('c41b4da4013e484ead6fc8b03192dee2', '4f91b69973d1453aadf384d508aed894', 'status', 'TEXT', 7); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('34a33a519dd64dc5aa4b3c98c70a5989', '4f91b69973d1453aadf384d508aed894', 'target_url', 'TEXT', 8); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('456bc8b7b9754708828c68221af977c9', '4f91b69973d1453aadf384d508aed894', 'progress_feedback', 'TEXT', 9); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('d8e5fad3feed41fe8400bd2f35048abe', '4f91b69973d1453aadf384d508aed894', 'risk', 'TEXT', 10); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('e8cf80fdb51a4d4e9c9a9c660714efc7', '4f91b69973d1453aadf384d508aed894', 'priority', 'TEXT', 11); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('819adccd6214437fadec3936b297007b', '4f91b69973d1453aadf384d508aed894', 'finish_time', 'DATETIME', 3); -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('aac65de27f014c3895228fdb5d32573e', '4f91b69973d1453aadf384d508aed894', 'tag', 'TEXT', 12); diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.function.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.function.sql deleted file mode 100644 index 24482bacb6..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.function.sql +++ /dev/null @@ -1,147 +0,0 @@ --- 移动property_old表的数据到新的property --- 移动instance_old表的数据到新的instance -CREATE OR REPLACE FUNCTION move_old_data_to_new_table(p_task_id CHAR(32)) RETURNS void AS -$BODY$ -DECLARE -d_cur_properties CURSOR (c_task_id CHAR(32)) FOR SELECT * FROM task_property_old WHERE task_id = c_task_id; -d_record_property RECORD; - d_record_template RECORD; - d_sql_insert_into VARCHAR := '('; - d_sql_values VARCHAR := 'SELECT '; - d_index INTEGER := 0; - d_count INTEGER ; - d_TEXT_SEQUENCE INTEGER := 13; - d_DATETIME_SEQUENCE INTEGER := 4; - d_other_SEQUWNCE INTEGER := 1; - d_data_type VARCHAR; - d_sequence INTEGER; - d_template_id CHAR(32); -BEGIN -SELECT COUNT(task_id) INTO d_count FROM task_property_old WHERE task_id = p_task_id; -RAISE NOTICE 'task_id=%, has % properties.',p_task_id, d_count; -OPEN d_cur_properties(p_task_id); -WHILE d_index < d_count LOOP - FETCH NEXT FROM d_cur_properties INTO d_record_property; - d_sql_values := d_sql_values || d_record_property.data_type || '_' || d_record_property.sequence || ','; -SELECT * INTO d_record_template from task_template_property where name = d_record_property.name; -if d_record_template is not null THEN - d_data_type := d_record_template.data_type; - d_sequence := d_record_template.sequence; - d_template_id := d_record_template.id; -ELSE - d_data_type := d_record_property.data_type; - d_template_id := '00000000000000000000000000000000'; - if d_record_property.data_type = 'TEXT' THEN - d_sequence := d_TEXT_SEQUENCE; - d_TEXT_SEQUENCE := d_TEXT_SEQUENCE + 1; - ELSIF d_record_property.data_type = 'DATETIME' THEN - d_sequence := d_DATETIME_SEQUENCE; - d_DATETIME_SEQUENCE := d_DATETIME_SEQUENCE + 1; -ELSE - d_sequence := d_other_SEQUWNCE; - d_other_SEQUWNCE := d_other_SEQUWNCE + 1; -end if; -end if; - RAISE NOTICE 'EXECUTE sql move property to new table, id=%. [%/%]', d_record_property.id, d_index+1, d_count; -INSERT INTO task_property(id, task_id, name, required, description, scope, data_type, sequence, appearance, identifiable, template_id) -VALUES (d_record_property.id, d_record_property.task_id, d_record_property.name, d_record_property.required, - d_record_property.description, d_record_property.scope, d_data_type, d_sequence, - d_record_property.appearance, d_record_property.identifiable, d_template_id); -d_sql_insert_into := d_sql_insert_into || d_data_type || '_' || d_sequence || ','; - d_index := d_index+1; -end loop; -CLOSE d_cur_properties; -d_sql_insert_into := d_sql_insert_into || 'id, task_id, task_type_id, source_id)'; - d_sql_values := d_sql_values ||'id, task_id, task_type_id, source_id '; - - RAISE NOTICE 'EXECUTE sql move instance to new table : % %.', d_sql_insert_into, d_sql_values; -EXECUTE ('INSERT INTO task_instance_wide' || d_sql_insert_into || d_sql_values || 'from task_instance_wide_old where task_id = ''' || - p_task_id || ''''); -RAISE NOTICE 'EXECUTE sql move instance_delete to new table : % %.', d_sql_insert_into, d_sql_values; -EXECUTE ('INSERT INTO task_instance_deleted' || d_sql_insert_into || d_sql_values || 'from task_instance_deleted_old where task_id = ''' || - p_task_id || ''''); -end; - -$BODY$ LANGUAGE plpgsql; - - - --- 修改原有instance表和property表,并创建新表 -CREATE OR REPLACE FUNCTION back_table_and_create_new() RETURNS void as -$$ -DECLARE -alter_table_instance VARCHAR := 'ALTER TABLE task_instance_wide rename to task_instance_wide_old'; - alter_table_instance_delete VARCHAR := 'ALTER TABLE task_instance_deleted rename to task_instance_deleted_old'; - alter_table_property VARCHAR := 'ALTER TABLE task_property rename to task_property_old'; - create_table_instance VARCHAR := 'CREATE TABLE task_instance_wide (LIKE task_instance_wide_old INCLUDING INDEXES INCLUDING COMMENTS);'; - create_table_property VARCHAR := 'CREATE TABLE task_property (LIKE task_property_old INCLUDING INDEXES INCLUDING COMMENTS);'; - create_table_instance_delete VARCHAR := 'CREATE TABLE task_instance_deleted (LIKE task_instance_deleted_old INCLUDING INDEXES INCLUDING COMMENTS);'; - -BEGIN - RAISE NOTICE 'Execute alter table task_instance_wide: "%".', alter_table_instance; -EXECUTE (alter_table_instance); -RAISE NOTICE 'Execute alter table task_property: "%".', alter_table_property; -EXECUTE (alter_table_property); -RAISE NOTICE 'Execute alter table task_instance_deleted: "%".', alter_table_instance_delete; -EXECUTE (alter_table_instance_delete); -RAISE NOTICE 'Execute create table task_instance_wide: "%".', create_table_instance; -EXECUTE (create_table_instance); -RAISE NOTICE 'Execute create table task_property: "%".', create_table_property; -EXECUTE (create_table_property); -RAISE NOTICE 'Execute create table task_instance_deleted: "%".', create_table_instance_delete; -EXECUTE (create_table_instance_delete); -end; - -$$ LANGUAGE plpgsql; - --- 修改task表的template为默认模板 -CREATE OR REPLACE FUNCTION modify_task_add_template() RETURNS void as -$$ -DECLARE -d_cur_task_ids CURSOR FOR SELECT id - FROM task; -d_task_id CHAR(32); - d_task_count INTEGER; - d_task_index INTEGER := 0; - d_template_id CHAR(32); -BEGIN - PERFORM back_table_and_create_new(); -SELECT COUNT(id) into d_task_count FROM task; -SELECT id into d_template_id from task_template where name = '普通任务'; -OPEN d_cur_task_ids; -WHILE d_task_index < d_task_count LOOP - FETCH NEXT FROM d_cur_task_ids INTO d_task_id; -UPDATE task set template_id = d_template_id where CURRENT OF d_cur_task_ids; -d_task_index := d_task_index + 1; - RAISE NOTICE 'Add template to task "%". [%/%]', d_task_id, d_task_index, d_task_count; - PERFORM move_old_data_to_new_table(d_task_id); -END LOOP; -END; - -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION rollback_template_update() RETURNS void as -$$ -DECLARE -alter_table_instance VARCHAR := 'ALTER TABLE task_instance_wide rename to task_instance_wide_error'; - alter_table_instance_delete VARCHAR := 'ALTER TABLE task_instance_deleted rename to task_instance_deleted_error'; - alter_table_property VARCHAR := 'ALTER TABLE task_property rename to task_property_error'; - alter_table_instance_r VARCHAR := 'ALTER TABLE task_instance_wide_old rename to task_instance_wide'; - alter_table_instance_delete_r VARCHAR := 'ALTER TABLE task_instance_deleted_old rename to task_instance_deleted'; - alter_table_property_r VARCHAR := 'ALTER TABLE task_property_old rename to task_property'; -BEGIN - RAISE NOTICE 'Execute rollback sql.'; -DROP TABLE IF EXISTS task_instance_wide_error; -DROP TABLE IF EXISTS task_instance_deleted_error; -DROP TABLE IF EXISTS task_property_error; -EXECUTE (alter_table_instance); -EXECUTE (alter_table_instance_delete); -EXECUTE (alter_table_property); -EXECUTE (alter_table_instance_r); -EXECUTE (alter_table_instance_delete_r); -EXECUTE (alter_table_property_r); -UPDATE task set template_id = '00000000000000000000000000000000'; -end; -$$ LANGUAGE plpgsql; - -select modify_task_add_template(); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.taskrelation.create.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.taskrelation.create.sql deleted file mode 100644 index 3dc1a7a5ce..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.postgres.taskrelation.create.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE TABLE IF NOT EXISTS jane_relation ( - id CHAR(32) PRIMARY KEY COMMENT '主键id', - object_id1 CHAR(32) NOT NULL COMMENT 'object的id1', - object_type1 VARCHAR(16) NOT NULL COMMENT 'obje的type1', - object_id2 CHAR(32) NOT NULL COMMENT 'object的id2', - object_type2 VARCHAR(16) NOT NULL COMMENT 'object的type2', - relation_type VARCHAR(16) NOT NULL COMMENT '关系类型', - created_by VARCHAR(127) NOT NULL COMMENT '创建人', - created_at TIMESTAMP NOT NULL COMMENT '创建时间' - ) COMMENT '任务关系表'; -CREATE INDEX IF NOT EXISTS IDX_JANE_RELATION_OBJECT_ID1 ON jane_relation(object_id1); -CREATE INDEX IF NOT EXISTS IDX_JANE_RELATION_OBJECT_ID2 ON jane_relation(object_id2); -CREATE UNIQUE INDEX IF NOT EXISTS UK_IDX_JANE_RELATION_OBJECT_ID1_OBJECT_ID2 ON jane_relation(object_id1,object_id2); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.templateExtend.create.sql b/app-builder/jane/jober/sql/create/taskcenter/taskcenter.templateExtend.create.sql deleted file mode 100644 index b32e324427..0000000000 --- a/app-builder/jane/jober/sql/create/taskcenter/taskcenter.templateExtend.create.sql +++ /dev/null @@ -1,45 +0,0 @@ ---模板继承表 -CREATE TABLE IF NOT EXISTS extend_table -( - id VARCHAR(32) PRIMARY KEY COMMENT '主键id', - parent_id VARCHAR(32) NOT NULL COMMENT '父任务id' -) COMMENT '模板继承表'; - ---根据子任务查询所有父任务的id -CREATE OR REPLACE FUNCTION find_template_parents(input_id varchar) - RETURNS TABLE(template_parents_id varchar) AS $BODY$ -BEGIN -RETURN QUERY WITH RECURSIVE dump_parents(id, parent_id) AS ( - SELECT id, parent_id FROM extend_table WHERE id = input_id - UNION ALL - SELECT p.id, p.parent_id FROM dump_parents AS c, extend_table AS p WHERE c.parent_id = p.id - ) -SELECT input_id -UNION ALL -SELECT parent_id FROM dump_parents WHERE parent_id IS NOT NULL - AND parent_id != '00000000000000000000000000000000'; -RETURN; -END; - $BODY$ -LANGUAGE plpgsql VOLATILE - COST 100 - ROWS 1000; - ---根据父任务查询所有子任务的id -CREATE OR REPLACE FUNCTION find_template_children(input_id varchar) - RETURNS TABLE(children_template_id varchar) AS $BODY$ -BEGIN -RETURN QUERY WITH RECURSIVE dump_children(id) AS ( - SELECT id FROM extend_table WHERE parent_id = input_id - UNION ALL - SELECT c.id FROM dump_children AS p, extend_table AS c WHERE c.parent_id = p.id - ) -SELECT input_id -UNION ALL -SELECT id FROM dump_children; -RETURN; -END; - $BODY$ -LANGUAGE plpgsql VOLATILE - COST 100 - ROWS 1000; \ No newline at end of file diff --git a/app-builder/jane/jober/sql/jade/02_general_create.sql b/app-builder/jane/jober/sql/jade/02_general_create.sql deleted file mode 100644 index a23b9667b8..0000000000 --- a/app-builder/jane/jober/sql/jade/02_general_create.sql +++ /dev/null @@ -1,910 +0,0 @@ --- 流程相关 -CREATE TABLE IF NOT EXISTS flow_definition -( - definition_id VARCHAR(32) PRIMARY KEY, - meta_id VARCHAR(32) NOT NULL, - name VARCHAR(256) NOT NULL, - tenant VARCHAR(32) NOT NULL, - version VARCHAR(16) NOT NULL, - status VARCHAR(32) NOT NULL, - graph JSONB NOT NULL, - created_by VARCHAR(32) NOT NULL, - created_at timestamp without time zone NOT NULL -); - -comment on table flow_definition is '流程定义'; -comment on column flow_definition.definition_id is '表示流程定义的唯一标识。由32位16进制字符组成。'; -comment on column flow_definition.meta_id is '表示流程定义的元数据标识。由32位16进制字符组成。'; -comment on column flow_definition.name is '表示流程定义的元数据标识。'; -comment on column flow_definition.tenant is '表示流程定义的租户标识。'; -comment on column flow_definition.version is '表示流程定义的版本标识。'; -comment on column flow_definition.status is '表示流程定义的状态标识。'; -comment on column flow_definition.graph is '表示流程定义的元数据信息。'; -comment on column flow_definition.created_by is '表示创建人。'; -comment on column flow_definition.created_at is '表示创建时间。'; - -CREATE UNIQUE INDEX IF NOT EXISTS UK_FLOW_NAME_VERSION ON flow_definition (name, version); -CREATE UNIQUE INDEX IF NOT EXISTS UK_FLOW_META_ID_VERSION ON flow_definition (meta_id, version); - -CREATE TABLE IF NOT EXISTS flow_context -( - context_id VARCHAR(32) NOT NULL PRIMARY KEY, - trace_id VARCHAR(32) NOT NULL, - trans_id VARCHAR(32) NOT NULL, - root_id VARCHAR(32) NOT NULL, - stream_id VARCHAR(64) NOT NULL, - flow_data JSONB NOT NULL, - position_id VARCHAR(32) NOT NULL, - joined BOOLEAN, - status VARCHAR(10) NOT NULL, - parallel VARCHAR(32), - parallel_mode VARCHAR(10), - previous VARCHAR(32), - batch_id VARCHAR(32), - from_batch VARCHAR(32), - to_batch VARCHAR(32), - sent BOOLEAN DEFAULT FALSE, - create_at timestamp without time zone NOT NULL, - update_at timestamp without time zone, - archived_at timestamp without time zone -); - -comment on table flow_context is '流程实例上下文'; -comment on column flow_context.context_id is '表示流程实例上下文的唯一标识'; -comment on column flow_context.trace_id is '表示流程实例轨迹唯一标识。'; -comment on column flow_context.trans_id is '表示流程实例运行唯一标识。'; -comment on column flow_context.root_id is '表示流程实例根节点唯一标识。'; -comment on column flow_context.stream_id is '表示流程元数据标识和版本唯一标识。'; -comment on column flow_context.flow_data is '表示流程实例运行上下文数据。'; -comment on column flow_context.position_id is '表示流程实例上下文所处节点位置。'; -comment on column flow_context.joined is '表示流程实例上下文是否由平行节点合并。'; -comment on column flow_context.status is '表示流程实例上下文状态。'; -comment on column flow_context.parallel is '表示流程实例上下文处于哪个平行节点。'; -comment on column flow_context.parallel_mode is '表示流程实例上下文所处平行节点状态。'; -comment on column flow_context.previous is '表示流程实例上下文来源哪个上下文。'; -comment on column flow_context.batch_id is '表示流程实例上下文所处批次标识。'; -comment on column flow_context.from_batch is '表示流程实例上下文来源批次标识。'; -comment on column flow_context.to_batch is '表示流程实例上下文指向批次标识。'; -comment on column flow_context.sent is '表示流程实例上下文是否已被事件发送。'; -comment on column flow_context.create_at is '表示流程实例上下文创建时间。'; -comment on column flow_context.update_at is '表示流程实例上下文更新时间。'; -comment on column flow_context.archived_at is '表示流程实例上下文完成时间。'; - -CREATE INDEX IF NOT EXISTS INDEX_FLOW_STREAM_ID ON flow_context (stream_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_ID ON flow_context (trace_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRANS_ID_POSITION_ID ON flow_context (trans_id, position_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_BATCH_ID ON flow_context (batch_id, from_batch, to_batch); - -CREATE TABLE IF NOT EXISTS flow_trace -( - trace_id VARCHAR(32) NOT NULL PRIMARY KEY, - stream_id VARCHAR(64) NOT NULL, - operator VARCHAR(32) NOT NULL, - application_name VARCHAR(32) NOT NULL, - start_node VARCHAR(32) NOT NULL, - cur_nodes TEXT NOT NULL, - start_time timestamp without time zone NOT NULL, - end_time timestamp without time zone, - merged_to VARCHAR(32) -); - -comment on table flow_trace is '流程轨迹'; -comment on column flow_trace.trace_id is '表示流程轨迹的唯一标识'; -comment on column flow_trace.stream_id is '表示流程轨迹所处的流程标识'; -comment on column flow_trace.operator is '表示流程的发起人'; -comment on column flow_trace.application_name is '表示流程启动的应用'; -comment on column flow_trace.start_node is '表示流程启动的开始节点'; -comment on column flow_trace.cur_nodes is '表示流程实例当前所处节点'; -comment on column flow_trace.start_time is '表示流程实例启动时间'; -comment on column flow_trace.end_time is '表示流程实例结束时间'; -comment on column flow_trace.merged_to is '表示流程实例合并到哪个流程实例'; - -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_STREAM_ID ON flow_trace (stream_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_MERGE_ID ON flow_trace (trace_id, merged_to); - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'flow_trace' AND column_name = 'status' - ) THEN - -- 如果不存在,则执行 ALTER TABLE 语句 -ALTER TABLE flow_trace ADD COLUMN status VARCHAR(32) NOT NULL DEFAULT 'ARCHIVED'; -END IF; -END $$; - -CREATE TABLE IF NOT EXISTS fitable_usage -( - fitable_id VARCHAR(128) NOT NULL, - definition_id VARCHAR(32) NOT NULL, - PRIMARY KEY (fitable_id, definition_id) -); - -ALTER TABLE flow_context ALTER COLUMN trace_id TYPE TEXT; -ALTER TABLE flow_trace ALTER COLUMN cur_nodes SET DEFAULT 'default_node'; -ALTER TABLE flow_trace ADD COLUMN IF NOT EXISTS context_pool TEXT; - --- 任务中心相关 -CREATE TABLE IF NOT EXISTS task_tag -( - tag_id VARCHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - tenant VARCHAR(255) NOT NULL -); - -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_NAME_TENANT ON task_tag (name, tenant); - -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('41d00a31a9b34aa39b606d80fc426c2e', '未开始', 'public') ON CONFLICT (tag_id) DO NOTHING; -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('8d038441b7514f5b99ea18992126cfa8', '进行中', 'public') ON CONFLICT (tag_id) DO NOTHING; -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('4c4ddb220bfe4fe98374aff710afb3eb', '已完成', 'public') ON CONFLICT (tag_id) DO NOTHING; -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('ad31edc774e24bfbb15e87084744e259', '有风险', 'public') ON CONFLICT (tag_id) DO NOTHING; -INSERT INTO task_tag(tag_id, name, tenant) -VALUES ('e72a94b24e704e5abcffc920394f4a19', '无风险', 'public') ON CONFLICT (tag_id) DO NOTHING; - - -CREATE TABLE IF NOT EXISTS task_source_schedule -( - id CHAR(32) PRIMARY KEY, - fitable_id CHAR(32) NOT NULL, - "interval" INTEGER NOT NULL, - filter VARCHAR(512) NOT NULL -); - -CREATE TABLE IF NOT EXISTS task_source -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - app VARCHAR(64) NOT NULL, - type VARCHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_SOURCE ON task_source (task_id, name, app); -CREATE INDEX IF NOT EXISTS IDX_TASK_SOURCE_TASK ON task_source (task_id); - -CREATE TABLE IF NOT EXISTS task_property_trigger -( - id CHAR(32) PRIMARY KEY, - task_source_id CHAR(32) NOT NULL, - task_property_id CHAR(32) NOT NULL, - fitable_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_PROPERTY_TRIGGER ON task_property_trigger (task_source_id, task_property_id, fitable_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_SOURCE ON task_property_trigger (task_source_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_PROPERTY ON task_property_trigger (task_property_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TRIGGER_FITABLE ON task_property_trigger (fitable_id); - -CREATE TABLE IF NOT EXISTS task_property -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - required BOOLEAN NOT NULL, - identifiable BOOLEAN NOT NULL, - description VARCHAR(512) NOT NULL, - scope VARCHAR(16) NOT NULL, - data_type VARCHAR(16) NOT NULL, - sequence INTEGER NOT NULL, - appearance JSON NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_PROPERTY ON task_property (task_id, name); -CREATE INDEX IF NOT EXISTS IDX_TASK_PROPERTY_TASK ON task_property (task_id); - -CREATE TABLE IF NOT EXISTS task -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - tenant_id CHAR(32) NOT NULL, - attributes JSON NOT NULL DEFAULT '{}', - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS idx_task_id_name ON task (id, name); - -CREATE TABLE IF NOT EXISTS task_instance_wide -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - task_type_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - text_1 TEXT, - text_2 TEXT, - text_3 TEXT, - text_4 TEXT, - text_5 TEXT, - text_6 TEXT, - text_7 TEXT, - text_8 TEXT, - text_9 TEXT, - text_10 TEXT, - text_11 TEXT, - text_12 TEXT, - text_13 TEXT, - text_14 TEXT, - text_15 TEXT, - text_16 TEXT, - text_17 TEXT, - text_18 TEXT, - text_19 TEXT, - text_20 TEXT, - text_21 TEXT, - text_22 TEXT, - text_23 TEXT, - text_24 TEXT, - text_25 TEXT, - text_26 TEXT, - text_27 TEXT, - text_28 TEXT, - text_29 TEXT, - text_30 TEXT, - text_31 TEXT, - text_32 TEXT, - text_33 TEXT, - text_34 TEXT, - text_35 TEXT, - text_36 TEXT, - text_37 TEXT, - text_38 TEXT, - text_39 TEXT, - text_40 TEXT, - text_41 TEXT, - text_42 TEXT, - text_43 TEXT, - text_44 TEXT, - text_45 TEXT, - text_46 TEXT, - text_47 TEXT, - text_48 TEXT, - text_49 TEXT, - text_50 TEXT, - integer_1 INTEGER, - integer_2 INTEGER, - integer_3 INTEGER, - integer_4 INTEGER, - integer_5 INTEGER, - integer_6 INTEGER, - integer_7 INTEGER, - integer_8 INTEGER, - integer_9 INTEGER, - integer_10 INTEGER, - integer_11 INTEGER, - integer_12 INTEGER, - integer_13 INTEGER, - integer_14 INTEGER, - integer_15 INTEGER, - integer_16 INTEGER, - integer_17 INTEGER, - integer_18 INTEGER, - integer_19 INTEGER, - integer_20 INTEGER, - datetime_1 TIMESTAMP, - datetime_2 TIMESTAMP, - datetime_3 TIMESTAMP, - datetime_4 TIMESTAMP, - datetime_5 TIMESTAMP, - datetime_6 TIMESTAMP, - datetime_7 TIMESTAMP, - datetime_8 TIMESTAMP, - datetime_9 TIMESTAMP, - datetime_10 TIMESTAMP, - datetime_11 TIMESTAMP, - datetime_12 TIMESTAMP, - datetime_13 TIMESTAMP, - datetime_14 TIMESTAMP, - datetime_15 TIMESTAMP, - datetime_16 TIMESTAMP, - datetime_17 TIMESTAMP, - datetime_18 TIMESTAMP, - datetime_19 TIMESTAMP, - datetime_20 TIMESTAMP, - boolean_1 BOOLEAN, - boolean_2 BOOLEAN, - boolean_3 BOOLEAN, - boolean_4 BOOLEAN, - boolean_5 BOOLEAN, - boolean_6 BOOLEAN, - boolean_7 BOOLEAN, - boolean_8 BOOLEAN, - boolean_9 BOOLEAN, - boolean_10 BOOLEAN, - boolean_11 BOOLEAN, - boolean_12 BOOLEAN, - boolean_13 BOOLEAN, - boolean_14 BOOLEAN, - boolean_15 BOOLEAN, - boolean_16 BOOLEAN, - boolean_17 BOOLEAN, - boolean_18 BOOLEAN, - boolean_19 BOOLEAN, - boolean_20 BOOLEAN -); - -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_TASK ON task_instance_wide (task_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_SOURCE ON task_instance_wide (source_id); - -CREATE TABLE IF NOT EXISTS task_type -( - id CHAR(32) PRIMARY KEY, - tree_id CHAR(32) NOT NULL, - parent_id CHAR(32) NOT NULL, - name VARCHAR(255) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL, - task_id CHAR(32) -); -CREATE INDEX IF NOT EXISTS IDX_TASK_TYPE_TREE ON task_type (tree_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TYPE_PARENT ON task_type (parent_id); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TYPE_NAME ON task_type (task_id, name); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TYPE_TREE_NAME ON task_type (tree_id, name); - -CREATE TABLE IF NOT EXISTS tag -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG ON tag (name); - -CREATE TABLE IF NOT EXISTS tag_usage -( - id CHAR(32) PRIMARY KEY, - tag_id CHAR(32) NOT NULL, - object_type VARCHAR(16) NOT NULL, - object_id CHAR(32) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG_USAGE ON tag_usage (tag_id, object_id, object_type); -CREATE INDEX IF NOT EXISTS IDX_TAG_OBJECT_ID ON tag_usage (object_id); - -CREATE TABLE IF NOT EXISTS task_category_trigger -( - "id" CHAR(32) PRIMARY KEY, - "task_id" CHAR(32) NOT NULL, - "category_id" CHAR(32) NOT NULL, - "fitable_id" CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS "UK_TASK_CATEGORY_TRIGGER" ON task_category_trigger ("task_id", "category_id", "fitable_id"); -CREATE INDEX IF NOT EXISTS "IDX_TASK_CATEGORY_TRIGGER_TASK" ON task_category_trigger ("task_id"); - -CREATE TABLE IF NOT EXISTS category_group -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_GROUP_NAME ON category_group (name); - -CREATE TABLE IF NOT EXISTS category -( - id CHAR(32) PRIMARY KEY, - category_group_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - description VARCHAR(512) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at TIMESTAMP NOT NULL -); -comment on table category is '类目'; -comment on column category.id is '表示类目的唯一标识。由32位16进制字符组成。'; -comment on column category.category_group_id is '表示类目组的唯一标识。'; -comment on column category.name is '表示类目的名称类型。'; -comment on column category.description is '表示类目的描述信息。'; -comment on column category.created_by is '表示创建人。'; -comment on column category.created_at is '表示创建时间。'; -comment on column category.updated_by is '表示最近的修改人。未被修改时与创建人相同。'; -comment on column category.updated_at is '表示最近的修改时间。未被修改时与创建时间相同。'; - -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_GROUP ON category (category_group_id); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_NAME ON category (name); - -CREATE TABLE IF NOT EXISTS category_matcher -( - id CHAR(32) PRIMARY KEY, - category_id CHAR(32) NOT NULL, - property_id CHAR(32) NOT NULL, - "value" TEXT NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_CATEGORY_MATCHER ON category_matcher (category_id, property_id, "value"); -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_MATCHER_CATEGORY ON category_matcher (category_id); -CREATE INDEX IF NOT EXISTS IDX_CATEGORY_MATCHER_PROPERTY ON category_matcher (property_id); - -CREATE TABLE IF NOT EXISTS task_instance_event ( - id CHAR(32) PRIMARY KEY, - source_id CHAR(32) NOT NULL, - event_type VARCHAR(16) NOT NULL, - fitable_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_INSTANCE_EVENT ON task_instance_event(source_id, event_type, fitable_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_INSTANCE_EVENT_SOURCE ON task_instance_event(source_id); - -CREATE TABLE IF NOT EXISTS tenant -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - description TEXT, - avatar_id CHAR(32), - created_by VARCHAR(127) NOT NULL, - updated_by VARCHAR(127), - created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TENANT ON tenant (name); - -CREATE TABLE IF NOT EXISTS tenant_member -( - id CHAR(32) PRIMARY KEY, - tenant_id CHAR(32) NOT NULL, - user_id VARCHAR(127) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at timestamp without time zone NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TENANT_ID_USER_ID ON tenant_member (tenant_id, user_id); -CREATE INDEX IF NOT EXISTS IDX_TENANT_ID_TENANT_MEMBER ON tenant_member (tenant_id); - -CREATE TABLE IF NOT EXISTS file -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(127) NOT NULL, - content BYTEA NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at timestamp without time zone NOT NULL -); - -CREATE -OR REPLACE FUNCTION generate_uuid_text() -RETURNS CHAR(32) AS $$ -BEGIN -RETURN md5(random()::text || clock_timestamp()::text); -END; -$$ -LANGUAGE plpgsql; - -CREATE -OR REPLACE FUNCTION now_utc() -RETURNS TIMESTAMP AS $$ -BEGIN -RETURN CURRENT_TIMESTAMP AT TIME ZONE 'UTC'; -END; -$$ -LANGUAGE plpgsql; - -INSERT INTO category_group(id, name, description, created_by, created_at, updated_by, updated_at) -VALUES (generate_uuid_text(), 'status', '表示系统级别的状态定义。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), 'health', '表示系统级别的健康度定义。', 'admin', now_utc(), 'admin', now_utc()) ON CONFLICT (name) DO NOTHING; - -INSERT INTO category(id, category_group_id, name, description, created_by, created_at, updated_by, - updated_at) -VALUES (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '未开始', - '表示任务尚未开始处理。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '处理中', - '表示任务正在处理中。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'status'), '已完成', - '表示任务已闭环完成。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'health'), '无风险', - '表示当前任务的进展不存在风险。', 'admin', now_utc(), 'admin', now_utc()), - (generate_uuid_text(), (SELECT id FROM category_group WHERE name = 'health'), '风险', - '表示当前任务存在风险。', 'admin', now_utc(), 'admin', now_utc()) ON CONFLICT (name) DO NOTHING; - -CREATE TABLE IF NOT EXISTS task_tree_task -( - id CHAR(32) PRIMARY KEY, - tree_id CHAR(32) NOT NULL, - task_id CHAR(32) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TREE_TASK_TREE_TASK ON task_tree_task (tree_id, task_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TREE_TASK_TREE ON task_tree_task (tree_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_TREE_TASK_TASK ON task_tree_task (task_id); - -CREATE TABLE IF NOT EXISTS task_tree_v2 -( - id char(32) PRIMARY KEY, - name varchar(64) NOT NULL, - created_by varchar(127) NOT NULL, - created_at timestamp(6) NOT NULL, - updated_by varchar(127) NOT NULL, - updated_at timestamp(6) NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TREE_NAME ON task_tree_v2 (name); - -CREATE TABLE IF NOT EXISTS task_node_source -( - id CHAR(32) PRIMARY KEY, - node_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_NODE_SOURCE ON task_node_source (node_id, source_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_NODE_SOURCE_NODE ON task_node_source (node_id); -CREATE INDEX IF NOT EXISTS IDX_TASK_NODE_SOURCE_SOURCE ON task_node_source (source_id); - -CREATE TABLE IF NOT EXISTS "index" -( - "id" char(32) COLLATE "pg_catalog"."default" NOT NULL, - "task_id" char(32) COLLATE "pg_catalog"."default" NOT NULL, - "name" varchar(128) COLLATE "pg_catalog"."default" NOT NULL, - "created_by" varchar(127) COLLATE "pg_catalog"."default" NOT NULL, - "created_at" timestamp(6) NOT NULL, - "updated_by" varchar(127) COLLATE "pg_catalog"."default" NOT NULL, - "updated_at" timestamp(6) NOT NULL, - PRIMARY KEY ("id") -) -; - -CREATE TABLE IF NOT EXISTS task_instance_deleted -( - id CHAR(32) PRIMARY KEY, - task_id CHAR(32) NOT NULL, - task_type_id CHAR(32) NOT NULL, - source_id CHAR(32) NOT NULL, - text_1 TEXT, - text_2 TEXT, - text_3 TEXT, - text_4 TEXT, - text_5 TEXT, - text_6 TEXT, - text_7 TEXT, - text_8 TEXT, - text_9 TEXT, - text_10 TEXT, - text_11 TEXT, - text_12 TEXT, - text_13 TEXT, - text_14 TEXT, - text_15 TEXT, - text_16 TEXT, - text_17 TEXT, - text_18 TEXT, - text_19 TEXT, - text_20 TEXT, - text_21 TEXT, - text_22 TEXT, - text_23 TEXT, - text_24 TEXT, - text_25 TEXT, - text_26 TEXT, - text_27 TEXT, - text_28 TEXT, - text_29 TEXT, - text_30 TEXT, - text_31 TEXT, - text_32 TEXT, - text_33 TEXT, - text_34 TEXT, - text_35 TEXT, - text_36 TEXT, - text_37 TEXT, - text_38 TEXT, - text_39 TEXT, - text_40 TEXT, - text_41 TEXT, - text_42 TEXT, - text_43 TEXT, - text_44 TEXT, - text_45 TEXT, - text_46 TEXT, - text_47 TEXT, - text_48 TEXT, - text_49 TEXT, - text_50 TEXT, - integer_1 INTEGER, - integer_2 INTEGER, - integer_3 INTEGER, - integer_4 INTEGER, - integer_5 INTEGER, - integer_6 INTEGER, - integer_7 INTEGER, - integer_8 INTEGER, - integer_9 INTEGER, - integer_10 INTEGER, - integer_11 INTEGER, - integer_12 INTEGER, - integer_13 INTEGER, - integer_14 INTEGER, - integer_15 INTEGER, - integer_16 INTEGER, - integer_17 INTEGER, - integer_18 INTEGER, - integer_19 INTEGER, - integer_20 INTEGER, - datetime_1 TIMESTAMP, - datetime_2 TIMESTAMP, - datetime_3 TIMESTAMP, - datetime_4 TIMESTAMP, - datetime_5 TIMESTAMP, - datetime_6 TIMESTAMP, - datetime_7 TIMESTAMP, - datetime_8 TIMESTAMP, - datetime_9 TIMESTAMP, - datetime_10 TIMESTAMP, - datetime_11 TIMESTAMP, - datetime_12 TIMESTAMP, - datetime_13 TIMESTAMP, - datetime_14 TIMESTAMP, - datetime_15 TIMESTAMP, - datetime_16 TIMESTAMP, - datetime_17 TIMESTAMP, - datetime_18 TIMESTAMP, - datetime_19 TIMESTAMP, - datetime_20 TIMESTAMP, - boolean_1 BOOLEAN, - boolean_2 BOOLEAN, - boolean_3 BOOLEAN, - boolean_4 BOOLEAN, - boolean_5 BOOLEAN, - boolean_6 BOOLEAN, - boolean_7 BOOLEAN, - boolean_8 BOOLEAN, - boolean_9 BOOLEAN, - boolean_10 BOOLEAN, - boolean_11 BOOLEAN, - boolean_12 BOOLEAN, - boolean_13 BOOLEAN, - boolean_14 BOOLEAN, - boolean_15 BOOLEAN, - boolean_16 BOOLEAN, - boolean_17 BOOLEAN, - boolean_18 BOOLEAN, - boolean_19 BOOLEAN, - boolean_20 BOOLEAN - ); - -CREATE TABLE IF NOT EXISTS operation_record -( - id char(32) PRIMARY KEY, - object_type varchar(25) NOT NULL, - object_id char(32) NOT NULL, - operator varchar(127) NOT NULL, - operated_time timestamp without time zone, - message TEXT NOT NULL, - operate char(7) NOT NULL - ); - -CREATE TABLE IF NOT EXISTS flow_retry -( - entity_id VARCHAR(32) NOT NULL, - entity_type VARCHAR(32) NOT NULL, - next_retry_time timestamp without time zone NOT NULL, - last_retry_time timestamp without time zone, - retry_count INTEGER NOT NULL, - version INTEGER NOT NULL, - PRIMARY KEY (entity_id) - ); - -comment on table flow_retry is '流程节点执行异常重试记录'; -comment on column flow_retry.entity_id is '表示任务重试的上下文实体ID'; -comment on column flow_retry.entity_type is '表示任务重试的上下文实体类型'; -comment on column flow_retry.next_retry_time is '表示任务重试的下次时间'; -comment on column flow_retry.last_retry_time is '表示上次执行的任务重试时间'; -comment on column flow_retry.retry_count is '表示到目前为止的任务重试次数'; -comment on column flow_retry.version is '表示当前重试的版本号'; - -CREATE TABLE IF NOT EXISTS flow_lock -( - lock_key VARCHAR(100) NOT NULL PRIMARY KEY, - expired_at timestamp without time zone, - locked_client VARCHAR(50) - ); - -comment on table flow_lock is '流程锁'; -comment on column flow_lock.lock_key is '锁名称'; -comment on column flow_lock.expired_at is '锁过期时间'; -comment on column flow_lock.locked_client is '上锁的客户端IP'; - -ALTER TABLE task ADD COLUMN IF NOT EXISTS template_id CHAR(32) NOT NULL DEFAULT '00000000000000000000000000000000'; -ALTER TABLE task ADD COLUMN IF NOT EXISTS category VARCHAR(32) NOT NULL DEFAULT 'TASK'; -ALTER TABLE file ADD COLUMN IF NOT EXISTS type VARCHAR(32); -ALTER TABLE task_property ADD COLUMN IF NOT EXISTS template_id CHAR(32) NOT NULL DEFAULT '00000000000000000000000000000000'; -ALTER TABLE tenant ADD COLUMN IF NOT EXISTS access_level VARCHAR(32); - - -CREATE TABLE IF NOT EXISTS task_source_refresh_in_time ( - id CHAR(32) PRIMARY KEY, - metadata JSONB NOT NULL, - create_fitable_id CHAR(32) NOT NULL, - patch_fitable_id CHAR(32) NOT NULL, - delete_fitable_id CHAR(32) NOT NULL, - retrieve_fitable_id CHAR(32) NOT NULL, - list_fitable_id CHAR(32) NOT NULL - ); - -CREATE TABLE IF NOT EXISTS "category_usage" ( - "id" CHAR(32) PRIMARY KEY, - "object_type" VARCHAR(16) NOT NULL, - "object_id" CHAR(32) NOT NULL, - "category_group_id" CHAR(32) NOT NULL, - "category_id" CHAR(32) NOT NULL, - "created_by" VARCHAR(127) NOT NULL, - "created_at" TIMESTAMP NOT NULL, - "updated_by" VARCHAR(127) NOT NULL, - "updated_at" TIMESTAMP NOT NULL - ); -CREATE UNIQUE INDEX IF NOT EXISTS "UK_CATEGORY_USAGE" ON "category_usage"("object_id", "category_group_id", "object_type"); -CREATE INDEX IF NOT EXISTS "IDX_CATEGORY_USAGE_OBJECT_ID" ON "category_usage"("object_id"); - -/* 创建任务模板表 */ -CREATE TABLE IF NOT EXISTS task_template -( - id CHAR(32) PRIMARY KEY, - name VARCHAR(255) NOT NULL, - description VARCHAR(512) NOT NULL, - "tenant_id" char(32) NOT NULL DEFAULT '00000000000000000000000000000000' - ); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TEMPLATE_NAME ON task_template (name); -COMMENT on column task_template.id is '主键Id'; -COMMENT on column task_template.name is '任务模板名称'; -COMMENT on column task_template.description is '任务模板描述'; - -/* 创建任务模板属性表 */ -CREATE TABLE IF NOT EXISTS task_template_property -( - id CHAR(32) PRIMARY KEY, - task_template_id CHAR(32) NOT NULL, - name VARCHAR(64) NOT NULL, - data_type VARCHAR(16) NOT NULL, - sequence INTEGER NOT NULL - ); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TEMPLATE_PROPERTY_NAME ON task_template_property (task_template_id, name); -CREATE UNIQUE INDEX IF NOT EXISTS UK_TASK_TEMPLATE_PROPERTY_COLUMN ON task_template_property (task_template_id, data_type, sequence); - -COMMENT on column task_template_property.id is '主键Id'; -COMMENT on column task_template_property.task_template_id is '任务模板Id'; -COMMENT on column task_template_property.name is '任务模板属性名称'; -COMMENT on column task_template_property.data_type is '任务模板属性数据类型'; -COMMENT on column task_template_property.sequence is '任务模板属性在当前数据类型中的序号'; - -CREATE TABLE IF NOT EXISTS "index" ( - "id" CHAR(32) PRIMARY KEY, - "task_id" CHAR(32) NOT NULL, - "name" VARCHAR(128) NOT NULL, - "created_by" VARCHAR(127) NOT NULL, - "created_at" TIMESTAMP NOT NULL, - "updated_by" VARCHAR(127) NOT NULL, - "updated_at" TIMESTAMP NOT NULL -); - -CREATE TABLE IF NOT EXISTS "index_property" ( - "id" CHAR(32) PRIMARY KEY, - "index_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL -); -CREATE INDEX IF NOT EXISTS "idx_index_property_index" ON "index_property"("index_id"); - -CREATE TABLE IF NOT EXISTS "index_text" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" TEXT -); -CREATE INDEX IF NOT EXISTS "idx_index_text_instance" ON "index_text"("instance_id"); -CREATE INDEX IF NOT EXISTS "idx_index_text_property" ON "index_text"("property_id"); -CREATE INDEX IF NOT EXISTS "idx_index_text_value" ON "index_text"("value"); - -CREATE TABLE IF NOT EXISTS "index_integer" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" BIGINT -); -CREATE INDEX IF NOT EXISTS "idx_index_integer_instance" ON "index_integer"("instance_id"); -CREATE INDEX IF NOT EXISTS "idx_index_integer_property" ON "index_integer"("property_id"); -CREATE INDEX IF NOT EXISTS "idx_index_integer_value" ON "index_integer"("value"); - -CREATE TABLE IF NOT EXISTS "index_datetime" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "value" TIMESTAMP -); -CREATE INDEX IF NOT EXISTS "idx_index_datetime_instance" ON "index_datetime"("instance_id"); -CREATE INDEX IF NOT EXISTS "idx_index_datetime_property" ON "index_datetime"("property_id"); -CREATE INDEX IF NOT EXISTS "idx_index_datetime_value" ON "index_datetime"("value"); - -CREATE TABLE IF NOT EXISTS "list_text" ( - "id" CHAR(32) PRIMARY KEY, - "instance_id" CHAR(32) NOT NULL, - "property_id" CHAR(32) NOT NULL, - "index" BIGINT NOT NULL, - "value" TEXT -); -CREATE INDEX IF NOT EXISTS "IDX_LIST_TEXT_INSTANCE" ON "list_text"("instance_id"); -CREATE INDEX IF NOT EXISTS "IDX_LIST_TEXT_PROPERTY" ON "list_text"("property_id"); - --- 任务关联表 -CREATE TABLE IF NOT EXISTS jane_relation ( - id CHAR(32) PRIMARY KEY, - object_id1 CHAR(32) NOT NULL, - object_type1 VARCHAR(16) NOT NULL, - object_id2 CHAR(32) NOT NULL, - object_type2 VARCHAR(16) NOT NULL, - relation_type VARCHAR(16) NOT NULL, - created_by VARCHAR(127) NOT NULL, - created_at TIMESTAMP NOT NULL - ); -CREATE INDEX IF NOT EXISTS IDX_JANE_RELATION_OBJECT_ID1 ON jane_relation(object_id1); -CREATE INDEX IF NOT EXISTS IDX_JANE_RELATION_OBJECT_ID2 ON jane_relation(object_id2); -CREATE UNIQUE INDEX IF NOT EXISTS UK_IDX_JANE_RELATION_OBJECT_ID1_OBJECT_ID2 ON jane_relation(object_id1,object_id2); - ---模板继承表 -CREATE TABLE IF NOT EXISTS extend_table -( - id VARCHAR(32) PRIMARY KEY, - parent_id VARCHAR(32) NOT NULL - ); - ---根据子任务查询所有父任务的id -CREATE OR REPLACE FUNCTION find_template_parents(input_id varchar) - RETURNS TABLE(template_parents_id varchar) AS $BODY$ -BEGIN -RETURN QUERY WITH RECURSIVE dump_parents(id, parent_id) AS ( - SELECT id, parent_id FROM extend_table WHERE id = input_id - UNION ALL - SELECT p.id, p.parent_id FROM dump_parents AS c, extend_table AS p WHERE c.parent_id = p.id - ) -SELECT input_id -UNION ALL -SELECT parent_id FROM dump_parents WHERE parent_id IS NOT NULL - AND parent_id != '00000000000000000000000000000000'; -RETURN; -END; - $BODY$ -LANGUAGE plpgsql VOLATILE - COST 100 - ROWS 1000; - ---根据父任务查询所有子任务的id -CREATE OR REPLACE FUNCTION find_template_children(input_id varchar) - RETURNS TABLE(children_template_id varchar) AS $BODY$ -BEGIN -RETURN QUERY WITH RECURSIVE dump_children(id) AS ( - SELECT id FROM extend_table WHERE parent_id = input_id - UNION ALL - SELECT c.id FROM dump_children AS p, extend_table AS c WHERE c.parent_id = p.id - ) -SELECT input_id -UNION ALL -SELECT id FROM dump_children; -RETURN; -END; - $BODY$ -LANGUAGE plpgsql VOLATILE - COST 100 - ROWS 1000; - -CREATE TABLE IF NOT EXISTS flow_graph -( - id VARCHAR(32) NOT NULL, - version VARCHAR(16) NOT NULL, - tenant VARCHAR(32) NOT NULL, - status VARCHAR(32) NOT NULL, - name VARCHAR(256), - data TEXT, - created_by VARCHAR(32) NOT NULL, - created_at timestamp without time zone NOT NULL, - updated_by VARCHAR(32) NOT NULL, - updated_at timestamp without time zone NOT NULL, - previous VARCHAR(50), - is_deleted BOOLEAN DEFAULT FALSE, - PRIMARY KEY (id, version) - ); - -DROP INDEX IF EXISTS task_template_name_idx; -CREATE UNIQUE INDEX IF NOT EXISTS task_template_name_tenant_idx ON task_template ("name","tenant_id"); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.add_wide_text_column.sql b/app-builder/jane/jober/sql/update/taskcenter/taskcenter.add_wide_text_column.sql deleted file mode 100644 index dc4bb9623c..0000000000 --- a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.add_wide_text_column.sql +++ /dev/null @@ -1,18 +0,0 @@ ---创建函数增加宽表列51-100 -CREATE OR REPLACE FUNCTION "public"."wide_add_text_column"() - RETURNS "pg_catalog"."void" AS $BODY$ -DECLARE -i INT := 51; -BEGIN - WHILE i <= 100 LOOP - EXECUTE format ( 'ALTER TABLE task_instance_wide ADD COLUMN text_%s TEXT', i ); - EXECUTE format ( 'ALTER TABLE task_instance_deleted ADD COLUMN text_%s TEXT', i ); - i := i+1; -END LOOP; -END; -$BODY$ -LANGUAGE plpgsql VOLATILE - COST 100 - ---调用函数 -SELECT wide_add_text_column(); \ No newline at end of file diff --git a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202402171055.sql b/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202402171055.sql deleted file mode 100644 index 8af2ebaabf..0000000000 --- a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202402171055.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE client_login -( - "id" char(32) NOT NULL, - client_id char(32) NOT NULL, - "cookie" text, - PRIMARY KEY ("id") -) -; - -CREATE INDEX "idx_client_id_cookie" ON client_login (client_id, cookie); - -CREATE UNIQUE INDEX "idx_client_id" ON client_login (client_id); - -COMMENT ON COLUMN "client_login"."id" IS '主键'; - -COMMENT ON COLUMN "client_login".client_id IS 'idea插件客户端id'; - -COMMENT ON COLUMN "client_login"."cookie" IS '登录cookie'; diff --git a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202403011554.sql b/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202403011554.sql deleted file mode 100644 index 1072958f1f..0000000000 --- a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202403011554.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE task_template - ADD COLUMN "tenant_id" char(32) NOT NULL DEFAULT '00000000000000000000000000000000'; - -COMMENT ON COLUMN "task_template"."tenant_id" IS '租户Id'; - -UPDATE task_template set tenant_id = (SELECT id from "tenant" where name = 'public') where name = '普通任务' \ No newline at end of file diff --git a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202405091900.sql b/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202405091900.sql deleted file mode 100644 index e68f053586..0000000000 --- a/app-builder/jane/jober/sql/update/taskcenter/taskcenter.postgres.update_202405091900.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP INDEX task_template_name_idx; - -CREATE UNIQUE INDEX task_template_name_tenant_idx ON task_template ("name","tenant_id"); \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/pom.xml b/app-builder/jane/plugins/aipp-plugin/pom.xml index 9251a0ea19..ba2fcde831 100644 --- a/app-builder/jane/plugins/aipp-plugin/pom.xml +++ b/app-builder/jane/plugins/aipp-plugin/pom.xml @@ -122,6 +122,10 @@ io.opentelemetry opentelemetry-api + + com.opencsv + opencsv + modelengine.jade.service aipp-prompt-builder-service @@ -180,11 +184,6 @@ org.fitframework.extension fit-schedule - - org.fitframework.fel - tool-service - 3.5.0-M2.1 - modelengine.fit.jade.service store-service diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/AippPluginStarter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/AippPluginStarter.java new file mode 100644 index 0000000000..61d3cd5299 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/AippPluginStarter.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +package modelengine.fit.jober.aipp; + +import modelengine.fit.jober.aipp.mapper.AppChatNumMapper; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.plugin.Plugin; +import modelengine.fitframework.plugin.PluginStartedObserver; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +/** + * 插件启动器 + * + * @author 邬涨财 + * @since 2025-06-04 + */ +@Component +public class AippPluginStarter implements PluginStartedObserver { + @Override + public void onPluginStarted(Plugin plugin) { + if (!StringUtils.equals(plugin.metadata().name(), "aipp-plugin")) { + return; + } + plugin.container().all(AppChatNumMapper.class).stream().findAny().ifPresent(beanFactory -> { + AppChatNumMapper appChatNumMapper = ObjectUtils.cast(beanFactory.get()); + appChatNumMapper.clearNum(); + }); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/aop/LocaleAspect.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/aop/LocaleAspect.java index 69abba4095..e33914c69f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/aop/LocaleAspect.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/aop/LocaleAspect.java @@ -6,8 +6,11 @@ package modelengine.fit.jober.aipp.aop; -import lombok.RequiredArgsConstructor; import modelengine.fit.jober.aipp.service.DatabaseFieldLocaleService; +import modelengine.jade.authentication.context.UserContext; +import modelengine.jade.authentication.context.UserContextHolder; + +import lombok.RequiredArgsConstructor; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.aop.ProceedingJoinPoint; import modelengine.fitframework.aop.annotation.Around; @@ -36,11 +39,8 @@ @RequiredArgsConstructor public class LocaleAspect { private static final Logger log = Logger.get(LocaleAspect.class); - private static final String I18N_PATTERN = "i18n_appBuilder_\\{(.*?)\\}"; - private static final Pattern PATTERN = Pattern.compile(I18N_PATTERN); - private static final List LOCALES = Collections.unmodifiableList( Arrays.asList(new Locale("en"), new Locale("zh"), new Locale("en", "US"), new Locale("zh", "CN"))); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/AppTaskRunnable.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/AppTaskRunnable.java new file mode 100644 index 0000000000..6813404572 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/AppTaskRunnable.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.common; + +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.entity.ChatSession; + +/** + * 应用执行接口。 + * + * @author 张越 + * @since 2025-01-10 + */ +public interface AppTaskRunnable { + /** + * 运行任务。 + * + * @param context 上下文信息。 + */ + void run(RunContext context); + + /** + * 运行任务。 + * + * @param context 上下文信息。 + * @param chatSession 会话对象。 + */ + void run(RunContext context, ChatSession chatSession); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippCheckedException.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippCheckedException.java index 1b8262d4bb..fb6e68a554 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippCheckedException.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippCheckedException.java @@ -6,9 +6,10 @@ package modelengine.fit.jober.aipp.common.exception; -import lombok.Getter; import modelengine.fit.jane.common.entity.OperationContext; +import lombok.Getter; + /** * aipp通用受检异常 * diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippTaskNotFoundException.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippTaskNotFoundException.java index f2c53312de..d3e3e096d0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippTaskNotFoundException.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/common/exception/AippTaskNotFoundException.java @@ -6,9 +6,10 @@ package modelengine.fit.jober.aipp.common.exception; -import lombok.Getter; import modelengine.fit.jane.common.entity.OperationContext; +import lombok.Getter; + /** * aipp通用受检异常 * @@ -19,10 +20,9 @@ public class AippTaskNotFoundException extends AippCheckedException { private OperationContext context; - private modelengine.fit.jober.aipp.common.exception.AippErrCode error; + private AippErrCode error; - public AippTaskNotFoundException(OperationContext context, - modelengine.fit.jober.aipp.common.exception.AippErrCode error) { + public AippTaskNotFoundException(OperationContext context, AippErrCode error) { super(context, error); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AippQueryCondition.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AippQueryCondition.java index 5acb27c672..4e7ab24573 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AippQueryCondition.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AippQueryCondition.java @@ -26,21 +26,18 @@ public class AippQueryCondition { @RequestQuery(name = "name", required = false) private String name; - @RequestQuery(name = "status", required = false) private String status; - @RequestQuery(name = "version", required = false) private String version; - @RequestQuery(name = "creator", required = false) private String creator; - - @Property(description = "排序条件,支持字段:create_at/update_at", example = "create_at") + @Property(description = "排序条件,支持字段:create_at/update_at", + example = "create_at") @RequestQuery(name = "sort", required = false, defaultValue = "update_at") private String sort; - - @Property(description = "排序方向,descend表示降序,ascend表示升序", example = "descend") + @Property(description = "排序方向,descend表示降序,ascend表示升序", + example = "descend") @RequestQuery(name = "order", required = false, defaultValue = "descend") private String order; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AppQueryCondition.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AppQueryCondition.java index 90b16e0614..b548ff772f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AppQueryCondition.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/AppQueryCondition.java @@ -29,7 +29,6 @@ public class AppQueryCondition { @RequestParam(name = "type", required = false, defaultValue = "app") private String type; - private List ids; @RequestParam(name = "name", required = false) @@ -40,11 +39,14 @@ public class AppQueryCondition { @RequestParam(name = "app_category", required = false) private String appCategory; - private List excludeNames; + private String appSuiteId; + private String orderBy; + private String sort; + private Long offset; + private Integer limit; @RequestParam(name = "app_type", required = false) private String appType; - private String createBy; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/FormQueryCondition.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/FormQueryCondition.java index ba33e7df48..960e19b4d2 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/FormQueryCondition.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/FormQueryCondition.java @@ -25,18 +25,11 @@ @AllArgsConstructor public class FormQueryCondition { private String tenantId; - private String type; - private Long offset; - private int limit; - private String name; - private String id; - private String createBy; - private List excludeNames; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/InspirationQueryCondition.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/InspirationQueryCondition.java index b62d4e3e58..d27818c166 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/InspirationQueryCondition.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/condition/InspirationQueryCondition.java @@ -24,11 +24,8 @@ @AllArgsConstructor public class InspirationQueryCondition { private String aippId; - @RequestParam(name = "parent_id", required = false) private String parentId; - private String categoryId; - private String createUser; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AgentController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AgentController.java index 774ce05a52..81b1430c4b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AgentController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AgentController.java @@ -26,6 +26,7 @@ import modelengine.fitframework.validation.Validated; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + /** * 表示智能体信息获取接口集。 * diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippChatController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippChatController.java index 8a457af7cb..9639f5b4ae 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippChatController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippChatController.java @@ -6,19 +6,6 @@ package modelengine.fit.jober.aipp.controller; -import modelengine.fit.jane.task.gateway.Authenticator; -import modelengine.fit.jober.aipp.dto.chat.ChatInfoRspDto; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; -import modelengine.fit.jober.aipp.dto.chat.CreateChatRequest; -import modelengine.fit.jober.aipp.dto.chat.QueryChatInfoRequest; -import modelengine.fit.jober.aipp.dto.chat.QueryChatRequest; -import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; -import modelengine.fit.jober.aipp.dto.chat.QueryChatRspDto; -import modelengine.fit.jober.aipp.service.AippChatService; -import modelengine.fit.jober.common.RangedResultSet; -import modelengine.jade.service.annotations.CarverSpan; -import modelengine.jade.service.annotations.SpanAttr; - import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.PathVariable; import modelengine.fit.http.annotation.PostMapping; @@ -28,6 +15,7 @@ import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fit.jane.common.controller.AbstractController; import modelengine.fit.jane.common.response.Rsp; +import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; import modelengine.fit.jober.aipp.dto.chat.ChatInfoRspDto; import modelengine.fit.jober.aipp.dto.chat.CreateChatRequest; @@ -39,6 +27,8 @@ import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.StringUtils; +import modelengine.jade.service.annotations.CarverSpan; +import modelengine.jade.service.annotations.SpanAttr; import java.util.List; import java.util.Map; @@ -170,45 +160,4 @@ public Rsp deleteChatV2(HttpClassicServerRequest httpRequest, @PathVariabl @RequestBody("chat_id") String chatIds) { return this.deleteChat(httpRequest, tenantId, appId, chatIds); } - - /** - * deleteChat(用于mag网关) - * - * @param httpRequest httpRequest - * @param tenantId tenantId - * @param chatId 会话ID, 当传入多个id时,以“,”进行分割 - * @param body 请求体 - * @return Rsp - * @throws AippTaskNotFoundException 未查询到task异常 - * @deprecated 废弃,下个版本删除 - */ - @Deprecated - @CarverSpan(value = "operation.aippChat.update") - @PostMapping(path = "/{chat_id}", description = "更新会话接口") - public Rsp updateChat(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, @PathVariable("chat_id") @SpanAttr("chat_id") String chatId, - @RequestBody CreateChatRequest body) throws AippTaskNotFoundException { - return Rsp.ok(this.aippChatService.updateChat(chatId, body, this.contextOf(httpRequest, tenantId))); - } - - /** - * 重新发起会话。 - * - * @param httpRequest Http 请求体。 - * @param currentInstanceId 需要重新发起会话的实例 ID。 - * @param tenantId 租户 ID。 - * @param additionalContext 重新会话需要的信息,如是否使用多轮对话等等。 - * @return 表示会话相应体的 {@link Rsp}{@code <}{@link QueryChatRsp}{@code >}。 - * @deprecated 废弃,下个版本删除 - */ - @Deprecated - @CarverSpan(value = "operation.aippChat.rechat") - @PostMapping(path = "/instances/{current_instance_id}", description = "重新发起会话接口") - public Rsp restartChat(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, - @PathVariable("current_instance_id") @SpanAttr("current_instance_id") String currentInstanceId, - @RequestBody Map additionalContext) { - return Rsp.ok(this.aippChatService.restartChat(currentInstanceId, additionalContext, - this.contextOf(httpRequest, tenantId))); - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippFlowController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippFlowController.java index ff22c69dd6..974fcf0f39 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippFlowController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippFlowController.java @@ -6,10 +6,6 @@ package modelengine.fit.jober.aipp.controller; -import modelengine.fit.jane.task.gateway.Authenticator; -import modelengine.jade.service.annotations.CarverSpan; -import modelengine.jade.service.annotations.SpanAttr; - import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; @@ -22,9 +18,11 @@ import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fit.jane.common.controller.AbstractController; import modelengine.fit.jane.common.response.Rsp; +import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.condition.AippQueryCondition; import modelengine.fit.jober.aipp.condition.PaginationCondition; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDetailDto; import modelengine.fit.jober.aipp.dto.AippDto; @@ -34,6 +32,8 @@ import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.validation.Validated; +import modelengine.jade.service.annotations.CarverSpan; +import modelengine.jade.service.annotations.SpanAttr; import java.util.List; @@ -47,16 +47,19 @@ @RequestMapping(path = "/v1/api/{tenant_id}/aipp-info", group = "aipp编排管理接口") public class AippFlowController extends AbstractController { private final AippFlowService aippFlowService; + private final AppVersionService appVersionService; /** - * AippFlowController + * AippFlowController 的构造方法 * * @param authenticator authenticator * @param aippFlowService aippFlowService */ - public AippFlowController(Authenticator authenticator, @Fit AippFlowService aippFlowService) { + public AippFlowController(Authenticator authenticator, @Fit AippFlowService aippFlowService, + AppVersionService appVersionService) { super(authenticator); this.aippFlowService = aippFlowService; + this.appVersionService = appVersionService; } /** @@ -155,11 +158,12 @@ public Rsp updateAipp(HttpClassicServerRequest httpRequest, @CarverSpan(value = "operation.flow.preview") @PostMapping(path = "/{aipp_id}/preview", description = "预览aipp") public Rsp previewAipp(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, + @PathVariable("tenant_id") String tenantId, + @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, @RequestParam("version") @SpanAttr("version") String baselineVersion, @RequestBody AippDto aippDto) { aippDto.setId(aippId); - return Rsp.ok( - this.aippFlowService.previewAipp(baselineVersion, aippDto, this.contextOf(httpRequest, tenantId))); + return Rsp.ok(this.appVersionService.retrieval(aippDto.getAppId()) + .preview(baselineVersion, aippDto, this.contextOf(httpRequest, tenantId))); } /** @@ -193,7 +197,8 @@ public Rsp cleanPreviewAipp(HttpClassicServerRequest httpRequest, @PathVar @CarverSpan(value = "operation.flow.upgrade") @PostMapping(path = "/{aipp_id}/upgrade", description = "升级aipp") public Rsp upgradeAipp(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, + @PathVariable("tenant_id") String tenantId, + @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, @RequestParam("version") @SpanAttr("version") String baselineVersion, @RequestBody AippDto aippDto) { aippDto.setId(aippId); return Rsp.ok(this.aippFlowService.upgrade(baselineVersion, aippDto, contextOf(httpRequest, tenantId))); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippLogController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippLogController.java index 07742e48fb..b893dc01e6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippLogController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippLogController.java @@ -6,7 +6,12 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; +import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; @@ -19,11 +24,6 @@ import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.annotation.RequestParam; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; -import modelengine.fit.jober.aipp.entity.AippInstLog; -import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fitframework.annotation.Component; import java.util.List; @@ -145,7 +145,7 @@ public Rsp deleteLogs(@RequestBody List logIds) { } /** - * 删除指定的应用对话记录(针对mag网关接口) + * 删除指定的应用对话记录(针对不能使用delete方式的接口) * * @param logIds 需要删除的对话记录列表 * @return 返回空回复的 {@link Rsp}{@code <}{@link Void}{@code >}。 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippPromptController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippPromptController.java index 30e1fd7f27..83b1633303 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippPromptController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AippPromptController.java @@ -6,17 +6,17 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; +import modelengine.fit.jober.aipp.dto.model.PromptGenerateDto; +import modelengine.fit.jober.aipp.service.AippModelService; import modelengine.jade.service.annotations.CarverSpan; import modelengine.fit.http.annotation.PostMapping; import modelengine.fit.http.annotation.RequestBody; import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.model.PromptGenerateDto; -import modelengine.fit.jober.aipp.service.AippModelService; import modelengine.fitframework.annotation.Component; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderAppController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderAppController.java index 3ebb73588b..fb11f09c95 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderAppController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderAppController.java @@ -29,6 +29,10 @@ import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.app.service.AppDomainService; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; @@ -58,6 +62,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -70,23 +75,19 @@ @RequestMapping(path = "/v1/api/{tenant_id}/app") public class AppBuilderAppController extends AbstractController { private static final String DEFAULT_TYPE = "app"; - private static final int ERR_CODE = -1; - private static final String ERR_LOCALE_CODE = "90002920"; - private static final Logger log = Logger.get(AppBuilderAppController.class); private final List excludeNames; - private final AppBuilderAppService appService; - private final modelengine.fit.jober.aipp.genericable.AppBuilderAppService appGenericable; - private final LocaleService localeService; - + private final AppVersionService appVersionService; + private final AppDomainService appDomainService; + private final ConverterFactory converterFactory; + private final Map exportMeta; private final FitRuntime fitRuntime; - private final int maxAppNum; /** @@ -101,20 +102,29 @@ public class AppBuilderAppController extends AbstractController { */ public AppBuilderAppController(Authenticator authenticator, AppBuilderAppService appService, modelengine.fit.jober.aipp.genericable.AppBuilderAppService appGenericable, - @Value("${app-engine.exclude-names}") List excludeNames, - @Value("${app.max-number}") Integer maxAppNum, LocaleService localeService, FitRuntime fitRuntime) { + @Value("${app-engine.exclude-names}") List excludeNames, LocaleService localeService, + AppVersionService appVersionService, AppDomainService appDomainService, + @Value("${export-meta}") Map exportMeta, ConverterFactory converterFactory, + @Value("${app-engine.max-number}") Integer maxAppNum, FitRuntime fitRuntime) { super(authenticator); // 需要FIT框架支持exclude-names配置大括号 this.excludeNames = replaceAsterisks(excludeNames); this.appService = appService; this.appGenericable = appGenericable; this.localeService = localeService; + this.appVersionService = appVersionService; + this.appDomainService = appDomainService; + this.exportMeta = exportMeta; + this.converterFactory = converterFactory; this.fitRuntime = fitRuntime; this.maxAppNum = maxAppNum != null ? maxAppNum : 200; } private static List replaceAsterisks(List excludeNames) { - return excludeNames.stream().map(s -> s.replaceAll("\\*(\\w+)\\*", "{$1}")).collect(Collectors.toList()); + return excludeNames + .stream() + .map(s -> s.replaceAll("\\*(\\w+)\\*", "{$1}")) + .collect(Collectors.toList()); } /** @@ -145,8 +155,8 @@ public Rsp> list(HttpClassicServerRequ * @return 表示查询app的最新可编排版本的DTO {@link Rsp}{@code <}{@link AppBuilderAppDto}{@code >}。 */ @GetMapping(value = "/{app_id}", description = "查询 app ") - public Rsp query(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, - @PathVariable("app_id") String appId) { + public Rsp query(HttpClassicServerRequest httpRequest, + @PathVariable("tenant_id") String tenantId, @PathVariable("app_id") String appId) { return Rsp.ok(this.appGenericable.query(appId, this.contextOf(httpRequest, tenantId))); } @@ -176,7 +186,7 @@ public Rsp queryLatestOrchestration(HttpClassicServerRequest h * @return 查询结果列表。 */ @GetMapping(value = "/{app_id}/recentPublished", description = "查询 app 的历史发布版本") - public Rsp> recentPublished(HttpClassicServerRequest httpRequest, + public Rsp> recentPublished(HttpClassicServerRequest httpRequest, @PathVariable("app_id") String appId, @PathVariable("tenant_id") String tenantId, @RequestParam(value = "offset", defaultValue = "0") long offset, @RequestParam(value = "limit", defaultValue = "10") int limit, @RequestBean AppQueryCondition cond) { @@ -223,7 +233,8 @@ public Rsp create(HttpClassicServerRequest request, @PathVaria DEFAULT_TYPE)) >= this.maxAppNum) { return Rsp.err(ERR_CODE, this.localeService.localize(AppBuilderAppController.ERR_LOCALE_CODE)); } - return Rsp.ok(this.appService.create(appId, dto, context, false)); + AppVersion appVersion = this.appVersionService.create(appId, dto, context); + return Rsp.ok(this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); } /** @@ -367,9 +378,10 @@ public Rsp delete(HttpClassicServerRequest httpRequest, @PathVariable("ten @GetMapping(path = "/export/{app_id}", description = "导出应用配置") public FileEntity export(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, @PathVariable("app_id") String appId, HttpClassicServerResponse response) { - AppExportDto configDto = this.appService.export(appId, this.contextOf(httpRequest, tenantId)); - ByteArrayInputStream fileStream = - new ByteArrayInputStream(JsonUtils.toJsonString(configDto).getBytes(StandardCharsets.UTF_8)); + AppExportDto configDto = this.appDomainService.exportApp(appId, this.exportMeta, + this.contextOf(httpRequest, tenantId)); + ByteArrayInputStream fileStream = new ByteArrayInputStream(JsonUtils.toJsonString(configDto).getBytes( + StandardCharsets.UTF_8)); return FileEntity.createAttachment(response, configDto.getApp().getName() + ".json", fileStream, @@ -418,13 +430,31 @@ public Rsp importApp(HttpClassicServerRequest httpRequest, } String configString = new String(AppImExportUtil.readAllBytes(appConfigFileEntity.getInputStream()), StandardCharsets.UTF_8); - return Rsp.ok(this.appService.importApp(configString, this.contextOf(httpRequest, tenantId))); + return Rsp.ok(this.appDomainService.importApp(configString, + this.contextOf(httpRequest, tenantId))); } catch (IOException e) { log.error("Failed to read uploaded application config file", e); throw new AippException(AippErrCode.UPLOAD_FAILED); } } + /** + * 恢复应用到指定历史版本。 + * + * @param httpRequest 表示 http 请求的 {@link HttpClassicServerRequest}。 + * @param tenantId 表示租户唯一标识的 {@link String}。 + * @param appId 表示应用唯一标识的 {@link String}。 + * @param recoverAppId 表示指定历史版本唯一标识的 {@link String}。 + * @return 表示恢复后应用信息的 {@link AppBuilderAppDto}。 + */ + @CarverSpan(value = "operation.appBuilderApp.recoverApp") + @PostMapping(path = "/{app_id}/recover") + public Rsp recoverApp(HttpClassicServerRequest httpRequest, + @PathVariable("tenant_id") String tenantId, @PathVariable("app_id") String appId, + @RequestBody String recoverAppId) { + return Rsp.ok(this.appService.recoverApp(appId, recoverAppId, contextOf(httpRequest, tenantId))); + } + private AppQueryCondition buildAppQueryCondition(AppQueryCondition cond, String type) { cond.setType(type); if (cond.getExcludeNames() == null) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderFormController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderFormController.java index b1df18e367..afd613aee9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderFormController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderFormController.java @@ -6,7 +6,12 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; +import modelengine.fit.jober.aipp.dto.AppBuilderFormDto; +import modelengine.fit.jober.aipp.service.AppBuilderFormService; +import modelengine.fit.jober.common.RangedResultSet; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; @@ -19,11 +24,6 @@ import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.annotation.RequestParam; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.AppBuilderFormDto; -import modelengine.fit.jober.aipp.service.AppBuilderFormService; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.annotation.Component; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderPromptController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderPromptController.java index c3a1b5dfd7..06054dc55d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderPromptController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderPromptController.java @@ -6,10 +6,15 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; - +import modelengine.fit.jober.aipp.dto.AppBuilderPromptCategoryDto; +import modelengine.fit.jober.aipp.dto.AppBuilderPromptDto; +import modelengine.fit.jober.aipp.service.AppBuilderPromptService; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; @@ -19,11 +24,6 @@ import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.annotation.RequestParam; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.AppBuilderPromptCategoryDto; -import modelengine.fit.jober.aipp.dto.AppBuilderPromptDto; -import modelengine.fit.jober.aipp.service.AppBuilderPromptService; import modelengine.fitframework.annotation.Component; import java.util.List; @@ -121,7 +121,10 @@ public Rsp updateMyInspiration(HttpClassicServerRequest httpRequest, @SpanAttr("app_id") @PathVariable("app_id") String appId, @SpanAttr("inspiration_id") @PathVariable("inspiration_id") String inspirationId, @RequestBody AppBuilderPromptDto.AppBuilderInspirationDto inspirationDto) { - this.service.updateCustomInspiration(appId, categoryId, inspirationId, inspirationDto, + this.service.updateCustomInspiration(appId, + categoryId, + inspirationId, + inspirationDto, this.contextOf(httpRequest, tenantId)); return Rsp.ok(); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderUrlController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderUrlController.java index ee3e00ec8e..05d7c67443 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderUrlController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppBuilderUrlController.java @@ -6,15 +6,16 @@ package modelengine.fit.jober.aipp.controller; -import modelengine.fit.http.annotation.GetMapping; -import modelengine.fit.http.annotation.PathVariable; -import modelengine.fit.http.annotation.RequestMapping; -import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fit.jane.common.controller.AbstractController; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.genericable.AppBuilderAppService; + +import modelengine.fit.http.annotation.GetMapping; +import modelengine.fit.http.annotation.PathVariable; +import modelengine.fit.http.annotation.RequestMapping; +import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fitframework.annotation.Component; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppChatController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppChatController.java index 1208e40d8b..ed6512c939 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppChatController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppChatController.java @@ -6,22 +6,22 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; import modelengine.fit.jane.task.gateway.Authenticator; - +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; +import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; +import modelengine.fit.jober.aipp.service.AppChatService; +import modelengine.fit.jober.aipp.service.impl.AppChatServiceImpl; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + import modelengine.fit.http.annotation.PathVariable; import modelengine.fit.http.annotation.PostMapping; import modelengine.fit.http.annotation.RequestBody; import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; -import modelengine.fit.jober.aipp.common.exception.AippParamException; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; -import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; -import modelengine.fit.jober.aipp.service.AppChatService; -import modelengine.fit.jober.aipp.service.impl.AppChatServiceImpl; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.flowable.Choir; import modelengine.fitframework.log.Logger; @@ -40,7 +40,6 @@ @RequestMapping(path = "/v1/api/{tenant_id}", group = "app对话管理接口") public class AppChatController extends AbstractController { private static final Logger LOGGER = Logger.get(AppChatServiceImpl.class); - private final AppChatService appChatService; /** @@ -99,8 +98,8 @@ public Choir chatDebug(HttpClassicServerRequest httpRequest, @PathVariab */ @CarverSpan(value = "operation.appChat.waterflow.chat") @PostMapping(value = "/water_flow_chat", description = "会话接口,传递会话信息") - public Choir waterFlowChat(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, - @RequestBody CreateAppChatRequest body) { + public Choir waterFlowChat(HttpClassicServerRequest httpRequest, + @PathVariable("tenant_id") String tenantId, @RequestBody CreateAppChatRequest body) { this.validateChatBody(body); return this.appChatService.chat(body, this.contextOf(httpRequest, tenantId), false); } @@ -132,7 +131,8 @@ public Choir waterFlowChatDebug(HttpClassicServerRequest httpRequest, */ @CarverSpan(value = "operation.appChat.restartChat") @PostMapping(path = "/instances/{current_instance_id}", description = "重新发起会话接口") - public Choir restartChat(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, + public Choir restartChat(HttpClassicServerRequest httpRequest, + @PathVariable("tenant_id") String tenantId, @PathVariable("current_instance_id") @SpanAttr("current_instance_id") String currentInstanceId, @RequestBody Map additionalContext) { return this.appChatService.restartChat(currentInstanceId, additionalContext, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppRunTimeController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppRunTimeController.java index 5fb68027b5..1df25c5032 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppRunTimeController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppRunTimeController.java @@ -6,7 +6,6 @@ package modelengine.fit.jober.aipp.controller; -import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; import modelengine.fit.http.annotation.PostMapping; @@ -20,8 +19,6 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; -import modelengine.fit.jober.aipp.dto.AppBuilderAippCreateDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppStartDto; import modelengine.fit.jober.aipp.dto.ResumeAippDto; import modelengine.fit.jober.aipp.service.AippFlowRuntimeInfoService; import modelengine.fit.jober.aipp.service.AippRunTimeService; @@ -30,7 +27,6 @@ import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Property; import modelengine.fitframework.flowable.Choir; -import modelengine.fitframework.validation.Validated; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; @@ -46,9 +42,6 @@ @RequestMapping(path = "/v1/api/{tenant_id}", group = "aipp运行时管理接口") public class AppRunTimeController extends AbstractController { private final AippRunTimeService aippRunTimeService; - - private final modelengine.fit.jober.aipp.genericable.AippRunTimeService aippRunTimeGenericable; - private final AippFlowRuntimeInfoService aippFlowRuntimeInfoService; /** @@ -56,39 +49,15 @@ public class AppRunTimeController extends AbstractController { * * @param authenticator 认证器。 * @param aippRunTimeService AIPP运行时服务。 - * @param aippRunTimeGenericable AIPP通用运行时服务。 * @param aippFlowRuntimeInfoService AIPP流程运行时信息服务。 */ public AppRunTimeController(Authenticator authenticator, AippRunTimeService aippRunTimeService, - modelengine.fit.jober.aipp.genericable.AippRunTimeService aippRunTimeGenericable, AippFlowRuntimeInfoService aippFlowRuntimeInfoService) { super(authenticator); this.aippRunTimeService = aippRunTimeService; - this.aippRunTimeGenericable = aippRunTimeGenericable; this.aippFlowRuntimeInfoService = aippFlowRuntimeInfoService; } - /** - * 启动一个Aipp - * - * @param httpRequest 操作上下文 - * @param tenantId 租户id - * @param aippId aippId - * @param version aipp 版本 - * @param initContext 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @return 实例id - */ - @CarverSpan(value = "operation.appRuntime.run") - @PostMapping(path = "/aipp/{aipp_id}", description = "启动一个Aipp") - public Rsp createAippInstance(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, - @Property(description = "initContext表示start表单填充的内容,作为流程初始化的businessData", - example = "图片url, 文本输入, prompt") @RequestBody Map initContext, - @RequestParam(value = "version") @SpanAttr("version") String version) { - return Rsp.ok(this.aippRunTimeGenericable.createAippInstance(aippId, version, initContext, - this.contextOf(httpRequest, tenantId))); - } - /** * 用户选择历史后启动流程 * @@ -111,26 +80,6 @@ public Choir startFlowByUserSelectMemory(HttpClassicServerRequest httpRe return this.aippRunTimeService.startFlowWithUserSelectMemory(metaInstId, initContext, context, isDebug); } - /** - * 删除应用实例 - * - * @param httpRequest 操作上下文 - * @param tenantId 租户id - * @param aippId aippId - * @param instanceId 实例id - * @param version aipp版本 - * @return 返回空回复的 {@link Rsp}{@code <}{@link Void}{@code >} - */ - @CarverSpan(value = "operation.appRuntime.delete") - @DeleteMapping(path = "/aipp/{aipp_id}/instances/{instance_id}", description = "删除应用实例") - public Rsp deleteInstance(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, - @PathVariable("aipp_id") @SpanAttr("aipp_id") String aippId, - @PathVariable("instance_id") @SpanAttr("instance_id") String instanceId, - @RequestParam(value = "version") String version) { - aippRunTimeService.deleteAippInstance(aippId, version, instanceId, this.contextOf(httpRequest, tenantId)); - return Rsp.ok(); - } - /** * 更新表单数据,并恢复实例任务执行 * @@ -145,8 +94,10 @@ public Choir resumeAndUpdateAippInstance(HttpClassicServerRequest httpRe @RequestBean ResumeAippDto resumeAippDto, @Property(description = "用户填写的表单信息", example = "用户选择的大模型信息") @RequestBody Map formArgs) { - return this.aippRunTimeService.resumeAndUpdateAippInstance(resumeAippDto.getInstanceId(), formArgs, - resumeAippDto.getLogId(), this.contextOf(httpRequest, resumeAippDto.getTenantId()), + return this.aippRunTimeService.resumeAndUpdateAippInstance(resumeAippDto.getInstanceId(), + formArgs, + resumeAippDto.getLogId(), + this.contextOf(httpRequest, resumeAippDto.getTenantId()), resumeAippDto.isDebug()); } @@ -165,8 +116,9 @@ public Rsp terminateAippInstance(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, @PathVariable("instance_id") @SpanAttr("instance_id") String instanceId, @RequestBody Map msgArgs) { - return Rsp.ok( - this.aippRunTimeService.terminateInstance(instanceId, msgArgs, this.contextOf(httpRequest, tenantId))); + return Rsp.ok(this.aippRunTimeService.terminateInstance(instanceId, + msgArgs, + this.contextOf(httpRequest, tenantId))); } /** @@ -189,25 +141,6 @@ public Rsp terminateAippInstance(HttpClassicServerRequest httpRequest, this.contextOf(httpRequest, tenantId))); } - /** - * 启动对话实例 - * - * @param httpRequest 操作上下文 - * @param tenantId 租户id - * @param appBuilderAippCreateDto 启动对话结构体 - * @return 实例id - */ - @CarverSpan(value = "operation.appRuntime.createInstance") - @PostMapping(path = "/aipp/{app_id}/start", description = "启动一个对话实例") - public Rsp startInstance(HttpClassicServerRequest httpRequest, - @PathVariable("tenant_id") String tenantId, - @Property(description = "initContext表示start表单填充的内容,作为流程初始化的businessData", - example = "图片url, 文本输入, prompt") @RequestBody @Validated - AppBuilderAippCreateDto appBuilderAippCreateDto) { - return Rsp.ok(aippRunTimeService.startInstance(appBuilderAippCreateDto.getAppDto(), - appBuilderAippCreateDto.getContext(), this.contextOf(httpRequest, tenantId))); - } - /** * 查询流程运行时数据. * @@ -223,8 +156,8 @@ public Rsp getRuntimeInfo(HttpClassicServerRequest httpRequest, @PathVariable("tenant_id") String tenantId, @PathVariable("aipp_id") String aippId, @PathVariable("instance_id") String instanceId, @RequestParam(value = "version") String version) { OperationContext ctx = this.contextOf(httpRequest, tenantId); - RuntimeData runtimeData = this.aippFlowRuntimeInfoService.getRuntimeData(aippId, version, instanceId, ctx) - .orElse(null); + RuntimeData runtimeData = + this.aippFlowRuntimeInfoService.getRuntimeData(aippId, version, instanceId, ctx).orElse(null); return Rsp.ok(runtimeData); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTemplateController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTemplateController.java index b8004a1d25..529ddf6cdb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTemplateController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTemplateController.java @@ -6,10 +6,18 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; - +import modelengine.fit.jober.aipp.condition.TemplateQueryCondition; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; +import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; +import modelengine.fit.jober.aipp.service.AppTemplateService; +import modelengine.fit.jober.common.RangedResultSet; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; @@ -18,14 +26,6 @@ import modelengine.fit.http.annotation.RequestBody; import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.condition.TemplateQueryCondition; -import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; -import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; -import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; -import modelengine.fit.jober.aipp.service.AppTemplateService; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.annotation.Component; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTypeController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTypeController.java index f40d474cba..af7bad9113 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTypeController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/AppTypeController.java @@ -6,10 +6,14 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; - +import modelengine.fit.jober.aipp.dto.AppTypeDto; +import modelengine.fit.jober.aipp.service.AppTypeService; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + import modelengine.fit.http.annotation.DeleteMapping; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; @@ -18,10 +22,6 @@ import modelengine.fit.http.annotation.RequestBody; import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.AppTypeDto; -import modelengine.fit.jober.aipp.service.AppTypeService; import modelengine.fitframework.annotation.Component; import java.util.List; @@ -57,7 +57,7 @@ public AppTypeController(Authenticator authenticator, AppTypeService appTypeServ */ @GetMapping(description = "查询所有应用业务分类") public Rsp> queryAll(HttpClassicServerRequest request, - @PathVariable("tenant_id") String tenantId) { + @PathVariable("tenant_id") String tenantId) { return Rsp.ok(this.appTypeService.queryAll(tenantId)); } @@ -71,7 +71,7 @@ public Rsp> queryAll(HttpClassicServerRequest request, */ @GetMapping(value = "/{id}", description = "查询一条应用业务分类") public Rsp query(HttpClassicServerRequest request, @PathVariable("tenant_id") String tenantId, - @PathVariable("id") String id) { + @PathVariable("id") String id) { return Rsp.ok(this.appTypeService.query(id, tenantId)); } @@ -86,7 +86,7 @@ public Rsp query(HttpClassicServerRequest request, @PathVariable("te @PostMapping(description = "创建一条应用业务分类") @CarverSpan(value = "operation.appType.create") public Rsp create(HttpClassicServerRequest request, @PathVariable("tenant_id") String tenantId, - @RequestBody @SpanAttr("name:$.name") AppTypeDto createDto) { + @RequestBody @SpanAttr("name:$.name") AppTypeDto createDto) { return Rsp.ok(this.appTypeService.add(createDto, tenantId)); } @@ -102,7 +102,7 @@ public Rsp create(HttpClassicServerRequest request, @PathVariable("t @PutMapping(value = "/{id}", description = "更新一条应用业务分类") @CarverSpan(value = "operation.appType.update") public Rsp update(HttpClassicServerRequest request, @PathVariable("tenant_id") String tenantId, - @PathVariable("id") @SpanAttr("id") String id, @RequestBody @SpanAttr("name:$.name") AppTypeDto dto) { + @PathVariable("id") @SpanAttr("id") String id, @RequestBody @SpanAttr("name:$.name") AppTypeDto dto) { dto.setId(id); this.appTypeService.update(dto, tenantId); return Rsp.ok(); @@ -119,7 +119,7 @@ public Rsp update(HttpClassicServerRequest request, @PathVariable("tenant_ @DeleteMapping(value = "/{id}", description = "根据id删除") @CarverSpan(value = "operation.appType.delete") public Rsp delete(HttpClassicServerRequest request, @PathVariable("tenant_id") String tenantId, - @PathVariable("id") @SpanAttr("id") String id) { + @PathVariable("id") @SpanAttr("id") String id) { this.appTypeService.delete(id, tenantId); return Rsp.ok(); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/FileController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/FileController.java index 9179d0cbfc..58b54887c0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/FileController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/FileController.java @@ -102,7 +102,6 @@ public Rsp uploadFile(HttpClassicServerRequest httpRequest, @PathVar @RequestHeader(value = "attachment-filename", defaultValue = "blank") @SpanAttr("fileName") String fileName, @RequestParam(value = "aipp_id", required = false) String aippId, PartitionedEntity receivedFile) throws IOException { OperationContext context = this.contextOf(httpRequest, tenantId); - List files = AippFileUtils.getFileEntity(receivedFile); if (files.isEmpty()) { throw new AippException(AippErrCode.UPLOAD_FAILED); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/GenericableController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/GenericableController.java index d639fd1a94..2488eb8d7c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/GenericableController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/GenericableController.java @@ -6,10 +6,14 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; - +import modelengine.fit.jober.aipp.dto.FitableInfoDto; +import modelengine.fit.jober.aipp.service.GenericableManageService; import modelengine.jade.service.annotations.CarverSpan; import modelengine.jade.service.annotations.SpanAttr; + import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; import modelengine.fit.http.annotation.PostMapping; @@ -17,10 +21,6 @@ import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.annotation.RequestParam; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.FitableInfoDto; -import modelengine.fit.jober.aipp.service.GenericableManageService; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.ObjectUtils; @@ -84,7 +84,9 @@ public Rsp>> executeInspirationFitable(HttpClassicServe @RequestBody Map body) { String appId = ObjectUtils.cast(body.get("appId")); String appType = ObjectUtils.cast(body.get("appType")); - return Rsp.ok(this.genericableManageService.executeInspirationFitable(fitableId, appId, appType, + return Rsp.ok(this.genericableManageService.executeInspirationFitable(fitableId, + appId, + appType, this.contextOf(httpRequest, tenantId))); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/KnowledgeController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/KnowledgeController.java index 6ea56b4042..4aadf2ee9a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/KnowledgeController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/KnowledgeController.java @@ -6,7 +6,12 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; +import modelengine.fit.jober.aipp.common.PageResponse; +import modelengine.fit.jober.aipp.condition.KnowledgeQueryCondition; +import modelengine.fit.jober.aipp.service.KnowledgeService; import modelengine.jade.app.engine.knowledge.dto.KRepoDto; import modelengine.jade.app.engine.knowledge.dto.KTableDto; @@ -16,11 +21,6 @@ import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.annotation.RequestQuery; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.common.PageResponse; -import modelengine.fit.jober.aipp.condition.KnowledgeQueryCondition; -import modelengine.fit.jober.aipp.service.KnowledgeService; import modelengine.fitframework.annotation.Component; /** @@ -73,7 +73,8 @@ public Rsp> listKnowledgeRepo(HttpClassicServerRequest ht */ @GetMapping(path = "/repos/{repo_id}/tables", description = "根据知识库 id 获取知识表列表") public Rsp> listKnowledgeTables(HttpClassicServerRequest httpRequest, - @PathVariable("repo_id") Long repoId, @RequestQuery(value = "pageNum", defaultValue = "0") Integer pageNum, + @PathVariable("repo_id") Long repoId, + @RequestQuery(value = "pageNum", defaultValue = "0") Integer pageNum, @RequestQuery(value = "pageSize", defaultValue = "10") Integer pageSize) { return Rsp.ok(this.knowledgeService.listKnowledgeTables(repoId, pageNum, pageSize)); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/StatisticsController.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/StatisticsController.java index 45a34acb4e..96d8c90815 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/StatisticsController.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/controller/StatisticsController.java @@ -6,16 +6,16 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.common.controller.AbstractController; +import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.gateway.Authenticator; +import modelengine.fit.jober.aipp.dto.StatisticsDTO; +import modelengine.fit.jober.aipp.service.StatisticsService; import modelengine.fit.http.annotation.GetMapping; import modelengine.fit.http.annotation.PathVariable; import modelengine.fit.http.annotation.RequestMapping; import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.controller.AbstractController; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.dto.StatisticsDTO; -import modelengine.fit.jober.aipp.service.StatisticsService; import modelengine.fitframework.annotation.Component; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/ConverterFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/ConverterFactory.java new file mode 100644 index 0000000000..ea35589f5c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/ConverterFactory.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.List; + +/** + * 转换器工厂. + * + * @author 张越 + * @since 2025-02-14 + */ +@Component +public class ConverterFactory { + private final List entityConverters; + + public ConverterFactory(List converters) { + this.entityConverters = Validation.notNull(converters, "Converters not exists."); + } + + /** + * 将 T 对象转换为 R. + * + * @param t 待转换对象. + * @param clz 目标类型. + * @return {@code R} 类型. + */ + public R convert(T t, Class clz) { + return ObjectUtils.cast(this.entityConverters.stream() + .filter(ec -> ec.source().equals(t.getClass()) && ec.target().equals(clz)) + .findFirst() + .map(ec -> ec.convert(ObjectUtils.cast(t))) + .orElse(null)); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/EntityConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/EntityConverter.java new file mode 100644 index 0000000000..2176ee9e52 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/EntityConverter.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters; + +/** + * 实体转换器接口. + * + * @author 张越 + * @since 2025-02-14 + */ +public interface EntityConverter { + /** + * 源类型. + * + * @return {@link Class}{@code <}{@code T}{@code >} 对象. + */ + Class source(); + + /** + * 目标类型. + * + * @return {@link Class}{@code <}{@code T}{@code >} 对象. + */ + Class target(); + + /** + * 将源对象转换为目标独享. + * + * @param source 源对象. + * @return 目标对象. + */ + Object convert(Object source); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppConfigToExportConfigConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppConfigToExportConfigConverter.java new file mode 100644 index 0000000000..b897e85c94 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppConfigToExportConfigConverter.java @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderConfigProperty; +import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.export.AppExportConfig; +import modelengine.fit.jober.aipp.dto.export.AppExportConfigProperty; +import modelengine.fit.jober.aipp.dto.export.AppExportForm; +import modelengine.fit.jober.aipp.dto.export.AppExportFormProperty; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * {@link AppBuilderConfig} -> {@link AppExportConfig}. + * + * @author 张越 + * @since 2025-02-14 + */ +@Component +public class AppConfigToExportConfigConverter implements EntityConverter { + @Override + public Class source() { + return AppBuilderConfig.class; + } + + @Override + public Class target() { + return AppExportConfig.class; + } + + @Override + public AppExportConfig convert(Object config) { + return Optional.ofNullable(config).map(ObjectUtils::cast).map(this::convert0).orElse(null); + } + + private AppExportConfig convert0(AppBuilderConfig s) { + List configProperties = s.getConfigProperties(); + List exportProperties = configProperties.stream() + .map(cp -> this.buildExportConfigProperty(cp, s.getAppVersion())) + .collect(Collectors.toList()); + return AppExportConfig.builder() + .form(this.buildExportForm(s.getForm())) + .configProperties(exportProperties) + .build(); + } + + private AppExportForm buildExportForm(AppBuilderForm appBuilderForm) { + return AppExportForm.builder() + .id(appBuilderForm.getId()) + .name(appBuilderForm.getName()) + .appearance(appBuilderForm.getAppearance()) + .type(appBuilderForm.getType()) + .formSuiteId(appBuilderForm.getFormSuiteId()) + .version(appBuilderForm.getVersion()) + .build(); + } + + private AppExportConfigProperty buildExportConfigProperty(AppBuilderConfigProperty configProperty, + AppVersion appVersion) { + AppBuilderFormProperty appBuilderFormProperty = appVersion.getFormProperty(configProperty.getFormPropertyId()); + return AppExportConfigProperty.builder() + .nodeId(configProperty.getNodeId()) + .formProperty(this.buildExportFormProperty(appBuilderFormProperty)) + .build(); + } + + private AppExportFormProperty buildExportFormProperty(AppBuilderFormProperty appBuilderFormProperty) { + return AppExportFormProperty.builder() + .name(appBuilderFormProperty.getName()) + .dataType(appBuilderFormProperty.getDataType()) + .defaultValue(JsonUtils.toJsonString(appBuilderFormProperty.getDefaultValue())) + .from(appBuilderFormProperty.getFrom()) + .group(appBuilderFormProperty.getGroup()) + .description(appBuilderFormProperty.getDescription()) + .index(appBuilderFormProperty.getIndex()) + .build(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppExportToAppPoConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppExportToAppPoConverter.java new file mode 100644 index 0000000000..2418647068 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppExportToAppPoConverter.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.dto.export.AppExportApp; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Optional; + +/** + * {@link AppExportApp} -> {@link AppBuilderAppPo}. + * + * @author 张越 + * @since 2025-02-14 + */ +@Component +public class AppExportToAppPoConverter implements EntityConverter { + @Override + public Class source() { + return AppExportApp.class; + } + + @Override + public Class target() { + return AppBuilderAppPo.class; + } + + @Override + public AppBuilderAppPo convert(Object appExportApp) { + return Optional.ofNullable(appExportApp) + .map(ObjectUtils::cast) + .map(s -> AppBuilderAppPo.builder() + .name(s.getName()) + .type(s.getType()) + .appBuiltType(s.getAppBuiltType()) + .version(s.getVersion()) + .attributes(JsonUtils.toJsonString(s.getAttributes())) + .appCategory(s.getAppCategory()) + .appType(s.getAppType()) + .build()) + .orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppGraphToExportGraphConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppGraphToExportGraphConverter.java new file mode 100644 index 0000000000..9ce86cd7b2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppGraphToExportGraphConverter.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.dto.export.AppExportFlowGraph; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Optional; + +/** + * {@link AppBuilderFlowGraph} -> {@link AppExportFlowGraph}. + * + * @author 张越 + * @since 2025-02-14 + */ +@Component +public class AppGraphToExportGraphConverter implements EntityConverter { + @Override + public Class source() { + return AppBuilderFlowGraph.class; + } + + @Override + public Class target() { + return AppExportFlowGraph.class; + } + + @Override + public AppExportFlowGraph convert(Object graph) { + return Optional.ofNullable(graph) + .map(ObjectUtils::cast) + .map(s -> AppExportFlowGraph.builder().name(s.getName()).appearance(s.getAppearance()).build()) + .orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAippDtoConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAippDtoConverter.java new file mode 100644 index 0000000000..7bb2a9c0bb --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAippDtoConverter.java @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.Optional; + +/** + * {@link AppVersion} -> {@link AippDto}. + * + * @author 张越 + * @since 2025-02-18 + */ +@Component +public class AppVersionToAippDtoConverter implements EntityConverter { + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AippDto.class; + } + + @Override + public AippDto convert(Object appVersion) { + return Optional.ofNullable(appVersion).map(ObjectUtils::cast).map(s -> { + String description = ObjectUtils.nullIf(s.getDescription(), StringUtils.EMPTY); + String icon = ObjectUtils.nullIf(s.getIcon(), StringUtils.EMPTY); + return AippDto.builder() + .name(s.getData().getName()) + .description(description) + .flowViewData(JsonUtils.parseObject(s.getFlowGraph().getAppearance())) + .icon(icon) + .appId(s.getData().getId()) + .version(s.getData().getVersion()) + .type(s.getData().getType()) + .appCategory(s.getData().getAppCategory()) + .build(); + }).orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAppDtoConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAppDtoConverter.java new file mode 100644 index 0000000000..75272b6c79 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToAppDtoConverter.java @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * {@link AppVersion} -> {@link AppBuilderAppDto}. + * + * @author 张越 + * @since 2025-02-18 + */ +@Component +public class AppVersionToAppDtoConverter implements EntityConverter { + private static final String FORM_PROPERTY_GROUP_NULL = "null"; + + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AppBuilderAppDto.class; + } + + @Override + public AppBuilderAppDto convert(Object appVersion) { + return Optional.ofNullable(appVersion).map(ObjectUtils::cast).map(s -> { + AppBuilderAppDto.AppBuilderAppDtoBuilder appDtoBuilder = AppBuilderAppDto.builder() + .id(s.getData().getId()) + .name(s.getData().getName()) + .type(s.getData().getType()) + .state(s.getData().getState()) + .attributes(s.getAttributes()) + .appType(s.getData().getAppType()) + .version(s.getData().getVersion()) + .appCategory(s.getData().getAppCategory()) + .createBy(s.getData().getCreateBy()) + .updateBy(s.getData().getUpdateBy()) + .createAt(s.getData().getCreateAt()) + .updateAt(s.getData().getUpdateAt()) + .appBuiltType(s.getData().getAppBuiltType()) + .config(this.buildAppBuilderConfig(s.getConfig())) + .flowGraph(this.buildFlowGraph(s.getFlowGraph())) + .aippId(s.getData().getAppSuiteId()) + .configFormProperties(this.buildConfigFormProperties(s.getFormProperties())); + Optional.ofNullable(s.getData().getPath()) + .filter(path -> !path.isEmpty()) + .ifPresent(path -> appDtoBuilder.chatUrl(String.format("/chat/%s", path))); + return appDtoBuilder.build(); + }).orElse(null); + } + + private AppBuilderFlowGraphDto buildFlowGraph(AppBuilderFlowGraph flowGraph) { + return AppBuilderFlowGraphDto.builder() + .id(flowGraph.getId()) + .name(flowGraph.getName()) + .appearance(JsonUtils.parseObject(flowGraph.getAppearance())) + .createBy(flowGraph.getCreateBy()) + .updateBy(flowGraph.getUpdateBy()) + .createAt(flowGraph.getCreateAt()) + .updateAt(flowGraph.getUpdateAt()) + .build(); + } + + private AppBuilderConfigDto buildAppBuilderConfig(AppBuilderConfig config) { + return AppBuilderConfigDto.builder() + .id(config.getId()) + .tenantId(config.getTenantId()) + .createBy(config.getCreateBy()) + .updateBy(config.getUpdateBy()) + .createAt(config.getCreateAt()) + .updateAt(config.getUpdateAt()) + .form(buildAppBuilderConfigFormDto(config)) + .build(); + } + + private AppBuilderConfigFormDto buildAppBuilderConfigFormDto(AppBuilderConfig config) { + Validation.notNull(config.getForm(), "Form can not be null."); + return AppBuilderConfigFormDto.builder() + .id(config.getFormId()) + .name(config.getForm().getName()) + .appearance(config.getForm().getAppearance()) + .build(); + } + + private List buildConfigFormProperties( + List formProperties) { + LinkedHashMap formPropertyMapping = formProperties.stream() + .map(AppBuilderFormProperty::toAppBuilderConfigFormPropertyDto) + .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getName, Function.identity(), (k1, k2) -> k1, + LinkedHashMap::new)); + String root = ""; + for (Map.Entry entry : formPropertyMapping.entrySet()) { + AppBuilderConfigFormPropertyDto dto = entry.getValue(); + String group = entry.getValue().getGroup(); + if (group.equals(FORM_PROPERTY_GROUP_NULL)) { + root = dto.getName(); + } else { + group = dto.getGroup(); + AppBuilderConfigFormPropertyDto parent = formPropertyMapping.get(group); + if (parent == null) { + throw new AippException(AippErrCode.FORM_PROPERTY_PARENT_NOT_EXIST); + } + parent.addChild(dto); + } + } + AppBuilderConfigFormPropertyDto rootProperty = formPropertyMapping.get(root); + return rootProperty == null ? Collections.emptyList() : Collections.singletonList(rootProperty); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToCreateDtoConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToCreateDtoConverter.java new file mode 100644 index 0000000000..94076baed1 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToCreateDtoConverter.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Optional; + +/** + * {@link AppVersion} -> {@link AppBuilderAppCreateDto}. + * + * @author 张越 + * @since 2025-02-18 + */ +@Component +public class AppVersionToCreateDtoConverter implements EntityConverter { + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AppBuilderAppCreateDto.class; + } + + @Override + public AppBuilderAppCreateDto convert(Object appVersion) { + return Optional.ofNullable(appVersion) + .map(ObjectUtils::cast) + .map(s -> AppBuilderAppCreateDto.builder() + .name(s.getData().getName()) + .description(s.getDescription()) + .icon(s.getIcon()) + .greeting(s.getGreeting()) + .appType(s.getData().getAppType()) + .type(s.getData().getType()) + .storeId(s.getData().getUniqueName()) + .appBuiltType(s.getData().getAppBuiltType()) + .appCategory(s.getData().getAppCategory()) + .build()) + .orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToExportAppConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToExportAppConverter.java new file mode 100644 index 0000000000..7a491afc1d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToExportAppConverter.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.export.AppExportApp; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Optional; + +/** + * {@link AppVersion} -> {@link AppExportApp}. + * + * @author 张越 + * @since 2025-02-14 + */ +@Component +public class AppVersionToExportAppConverter implements EntityConverter { + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AppExportApp.class; + } + + @Override + public AppExportApp convert(Object appVersion) { + return Optional.ofNullable(appVersion).map(ObjectUtils::cast).map(s -> { + AppBuilderAppPo appBuilderAppPo = s.getData(); + return AppExportApp.builder() + .name(appBuilderAppPo.getName()) + .tenantId(appBuilderAppPo.getTenantId()) + .type(appBuilderAppPo.getType()) + .appBuiltType(appBuilderAppPo.getAppBuiltType()) + .version(appBuilderAppPo.getVersion()) + .attributes(JsonUtils.parseObject(appBuilderAppPo.getAttributes())) + .appCategory(appBuilderAppPo.getAppCategory()) + .appType(appBuilderAppPo.getAppType()) + .build(); + }).orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToMetaDtoConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToMetaDtoConverter.java new file mode 100644 index 0000000000..a3f1de3144 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToMetaDtoConverter.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AppBuilderAppMetadataDto; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +/** + * {@link AppVersion} -> {@link AppBuilderAppMetadataDto}. + * + * @author 张越 + * @since 2025-02-18 + */ +@Component +public class AppVersionToMetaDtoConverter implements EntityConverter { + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AppBuilderAppMetadataDto.class; + } + + @Override + public AppBuilderAppMetadataDto convert(Object appVersion) { + return Optional.ofNullable(appVersion).map(ObjectUtils::cast).map(s -> { + List tags = new ArrayList<>(); + tags.add(s.getData().getType().toUpperCase(Locale.ROOT)); + return AppBuilderAppMetadataDto.builder() + .id(s.getData().getId()) + .name(s.getData().getName()) + .type(s.getData().getType()) + .state(s.getData().getState()) + .appType(s.getData().getAppType()) + .attributes(s.getAttributes()) + .version(s.getData().getVersion()) + .createBy(s.getData().getCreateBy()) + .updateBy(s.getData().getUpdateBy()) + .createAt(s.getData().getCreateAt()) + .updateAt(s.getData().getUpdateAt()) + .appCategory(s.getData().getAppCategory()) + .tags(tags) + .appBuiltType(s.getData().getAppBuiltType()) + .build(); + }).orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToTemplateConverter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToTemplateConverter.java new file mode 100644 index 0000000000..28487825f9 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/converters/impl/AppVersionToTemplateConverter.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.converters.impl; + +import modelengine.fit.jober.aipp.converters.EntityConverter; +import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.Optional; + +/** + * {@link AppVersion} -> {@link AppTemplate}. + * + * @author 张越 + * @since 2025-02-18 + */ +@Component +public class AppVersionToTemplateConverter implements EntityConverter { + @Override + public Class source() { + return AppVersion.class; + } + + @Override + public Class target() { + return AppTemplate.class; + } + + @Override + public AppTemplate convert(Object appVersion) { + return Optional.ofNullable(appVersion) + .map(ObjectUtils::cast) + .map(s -> AppTemplate.builder() + .id(s.getData().getId()) + .name(s.getData().getName()) + .builtType(s.getData().getAppBuiltType()) + .appType(s.getData().getAppType()) + .category(s.getData().getAppCategory()) + .attributes(s.getAttributes()) + .like(0) + .collection(0) + .usage(0) + .version("1.0.0") + .configId(s.getData().getConfigId()) + .flowGraphId(s.getData().getFlowGraphId()) + .createBy(s.getData().getCreateBy()) + .build()) + .orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AippSystemConfig.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AippSystemConfig.java index 7f83c0be8b..5ad08a2c8e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AippSystemConfig.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AippSystemConfig.java @@ -26,15 +26,10 @@ @Slf4j public class AippSystemConfig extends BaseDomain { private Long id; - private String configKey; - private String configValue; - private String configGroup; - private String configParent; - private JSONObject json; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderApp.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderApp.java index d67481b7ec..724b38d3a0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderApp.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderApp.java @@ -6,18 +6,20 @@ package modelengine.fit.jober.aipp.domain; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.SuperBuilder; -import lombok.extern.slf4j.Slf4j; import modelengine.fit.jober.aipp.enums.AppState; import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.SuperBuilder; +import lombok.extern.slf4j.Slf4j; import modelengine.fitframework.inspection.Validation; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -33,45 +35,31 @@ @Slf4j public class AppBuilderApp extends BaseDomain { private String id; - private String name; - + private String appId; + private String appSuiteId; private String tenantId; - private String configId; - private String flowGraphId; - private String type; - private String version; - private Map attributes; - private String path; - private String state; - private String appType; - private String appBuiltType; - private String appCategory; - + private Boolean isActive; + private String status; + private String uniqueName; + private LocalDateTime publishAt; private AppBuilderFlowGraph flowGraph; - private AppBuilderConfig config; - private List formProperties; - private AppBuilderFlowGraphRepository flowGraphRepository; - private AppBuilderConfigRepository configRepository; - private AppBuilderConfigPropertyRepository configPropertyRepository; - private AppBuilderFormRepository formRepository; - private AppBuilderFormPropertyRepository formPropertyRepository; public AppBuilderApp(AppBuilderFlowGraphRepository flowGraphRepository, AppBuilderConfigRepository configRepository, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderComponent.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderComponent.java index 4e84be1559..3597b4f43e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderComponent.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderComponent.java @@ -6,13 +6,14 @@ package modelengine.fit.jober.aipp.domain; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; import lombok.extern.slf4j.Slf4j; -import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fitframework.inspection.Validation; /** @@ -28,27 +29,18 @@ @Slf4j public class AppBuilderComponent extends BaseDomain { private String id; - private String name; - private String type; - private String description; - private String formId; - private String serviceId; - private String tenantId; - private AppBuilderForm form; - private AppBuilderFormRepository formRepository; - private AppBuilderFormPropertyRepository formPropertyRepository; public AppBuilderComponent(AppBuilderFormRepository formRepository, - AppBuilderFormPropertyRepository formPropertyRepository) { + AppBuilderFormPropertyRepository formPropertyRepository) { this.formRepository = formRepository; this.formPropertyRepository = formPropertyRepository; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfig.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfig.java index 8ff44bc116..df9e42feb1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfig.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfig.java @@ -6,18 +6,34 @@ package modelengine.fit.jober.aipp.domain; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.util.Entities; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.jadeconfig.JadeConfig; +import modelengine.fit.jober.aipp.domains.jadeconfig.JadeShape; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fit.jober.aipp.util.UsefulUtils; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; import lombok.extern.slf4j.Slf4j; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.util.StringUtils; +import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 应用构建器配置类 @@ -32,34 +48,26 @@ @Slf4j public class AppBuilderConfig extends BaseDomain { private String id; - private String formId; - private String appId; - private String tenantId; - private AppBuilderForm form; - - private AppBuilderApp app; - + private AppVersion appVersion; private List configProperties; - private AppBuilderFormRepository formRepository; - private AppBuilderFormPropertyRepository formPropertyRepository; - private AppBuilderConfigPropertyRepository configPropertyRepository; - private AppBuilderAppRepository appRepository; + private AppVersionService appVersionService; + private List formProperties; public AppBuilderConfig(AppBuilderFormRepository formRepository, AppBuilderFormPropertyRepository formPropertyMapper, - AppBuilderConfigPropertyRepository configPropertyRepository, AppBuilderAppRepository appRepository) { + AppBuilderConfigPropertyRepository configPropertyRepository, AppVersionService appVersionService) { this.formRepository = formRepository; this.formPropertyRepository = formPropertyMapper; this.configPropertyRepository = configPropertyRepository; - this.appRepository = appRepository; + this.appVersionService = appVersionService; } public AppBuilderForm getForm() { @@ -82,12 +90,185 @@ private List loadConfigProperties() { return this.configPropertyRepository.selectWithConfigId(this.id); } - public AppBuilderApp getApp() { - return lazyGet(this.app, this::loadApp, this::setApp); + /** + * 获取应用版本. + * + * @return {@link AppVersion} 对象. + */ + public AppVersion getAppVersion() { + return lazyGet(this.appVersion, () -> this.appVersionService.retrieval(this.appId), + v -> this.appVersion = v); + } + + /** + * 获取表单配置项集合. + * + * @return {@link List}{@code <}{@link AppBuilderFormProperty}{@code >} 集合. + */ + public List getFormProperties() { + return UsefulUtils.lazyGet(this.formProperties, + () -> this.formPropertyRepository.selectWithAppId(this.appId), + ps -> this.formProperties = ps); + } + + /** + * 创建app时调用,用于刷新id等操作. + * + * @param formProperties 表单属性列表. + * @param context 操作人上下文信息. + */ + public void clone(List formProperties, OperationContext context) { + AppBuilderForm appBuilderForm = this.getForm(); + List configPropertyList = this.getConfigProperties(); + + // 这里先根据旧的formId查询得到formProperties + Map formPropertyMap = formProperties.stream() + .collect(Collectors.toMap(AppBuilderFormProperty::getId, Function.identity())); + + // 先根据旧的configId查询得到configProperties + this.setId(Entities.generateId()); + configPropertyList.forEach(cp -> this.resetProperty(cp, formPropertyMap, appBuilderForm.getId())); + + // 其他属性设置. + LocalDateTime now = LocalDateTime.now(); + this.setFormId(appBuilderForm.getId()); + this.setCreateBy(context.getOperator()); + this.setCreateAt(now); + this.setUpdateBy(context.getOperator()); + this.setUpdateAt(now); + appBuilderForm.setCreateBy(context.getOperator()); + appBuilderForm.setCreateAt(now); + appBuilderForm.setUpdateBy(context.getOperator()); + appBuilderForm.setUpdateAt(now); + } + + private void resetProperty(AppBuilderConfigProperty configProperty, + Map idToFormPropertyMap, String formId) { + configProperty.setId(Entities.generateId()); + configProperty.setConfigId(this.getId()); + AppBuilderFormProperty formProperty = idToFormPropertyMap.get(configProperty.getFormPropertyId()); + formProperty.setId(Entities.generateId()); + formProperty.setFormId(formId); + configProperty.setFormPropertyId(formProperty.getId()); + } + + /** + * 通过新的properties对之前的进行删除、新增、修改操作. + * + * @param properties 新的属性集合. + */ + public void updateByProperties(List properties) { + Map newPropertyMap = properties.stream() + .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getId, Function.identity())); + + // 删除 + this.deleteByProperties(properties); + + // 新增 + this.addProperties(properties); + + // 修改, 待修改的内容, 循环修改 + this.getFormProperties().stream() + .filter(formProperty -> newPropertyMap.containsKey(formProperty.getId())) + .forEach(formProperty -> { + AppBuilderConfigFormPropertyDto propertyDto = newPropertyMap.get(formProperty.getId()); + formProperty.setName(propertyDto.getName()); + formProperty.setDataType(propertyDto.getDataType()); + formProperty.setDefaultValue(propertyDto.getDefaultValue()); + this.formPropertyRepository.updateOne(formProperty); + }); + } + + private void deleteByProperties(List properties) { + Map newPropertyMap = properties.stream() + .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getId, Function.identity())); + List toDeleteConfigPropertyIds = this.getConfigProperties() + .stream() + .filter(cp -> !newPropertyMap.containsKey(cp.getFormPropertyId())) + .map(AppBuilderConfigProperty::getId) + .collect(Collectors.toList()); + List toDeleteFormPropertyIds = this.getFormProperties() + .stream() + .map(AppBuilderFormProperty::getId) + .filter(id -> !newPropertyMap.containsKey(id)) + .collect(Collectors.toList()); + this.configPropertyRepository.deleteMore(toDeleteConfigPropertyIds); + this.formPropertyRepository.deleteMore(toDeleteFormPropertyIds); + } + + private void addProperties(List properties) { + Set formPropertyIds = this.getFormProperties() + .stream() + .map(AppBuilderFormProperty::getId) + .collect(Collectors.toSet()); + List toAddConfigProperties = properties.stream() + .filter(pd -> this.isLegalProperty(pd, formPropertyIds)) + .map(this::buildAppBuilderConfigProperty) + .collect(Collectors.toList()); + List toAddFormProperties = toAddConfigProperties.stream() + .map(AppBuilderConfigProperty::getFormProperty) + .collect(Collectors.toList()); + + this.configPropertyRepository.insertMore(toAddConfigProperties); + this.formPropertyRepository.insertMore(toAddFormProperties); } - private AppBuilderApp loadApp() { - Validation.notNull(this.appId, "App builder config can not be null."); - return this.appRepository.selectWithId(this.appId); + private boolean isLegalProperty(AppBuilderConfigFormPropertyDto pd, Set formPropertyIds) { + return StringUtils.isBlank(pd.getId()) || !formPropertyIds.contains(pd.getId()); + } + + private AppBuilderConfigProperty buildAppBuilderConfigProperty(AppBuilderConfigFormPropertyDto propertyDto) { + AppBuilderFormProperty formProperty = AppBuilderFormProperty.builder() + .formId(this.getFormId()) + .name(propertyDto.getName()) + .dataType(propertyDto.getDataType()) + .defaultValue(propertyDto.getDefaultValue()) + .id(Entities.generateId()) + .appId(this.getAppId()) + .build(); + return AppBuilderConfigProperty.builder() + .id(Entities.generateId()) + .configId(this.getId()) + .nodeId(propertyDto.getNodeId()) + .formPropertyId(formProperty.getId()) + .formProperty(formProperty) + .build(); + } + + /** + * 通过appearance修改配置. + * + * @param appearance graph数据序列化数据. + */ + public void updateByAppearance(String appearance) { + // 这个map {nodeId:{name:value}} + JadeConfig jadeConfig = new JadeConfig(appearance); + Map idToFormPropertyMap = this.getFormProperties() + .stream() + .collect(Collectors.toMap(AppBuilderFormProperty::getId, Function.identity())); + + // 这样写避免循环的时候去查询数据库获取configProperty对应的formProperty + for (AppBuilderConfigProperty cp : this.getConfigProperties()) { + if (!idToFormPropertyMap.containsKey(cp.getFormPropertyId())) { + // 2024/4/29 0029 这里可能拿到null,这里暂时不知道什么问题,先把拿不到的跳过 + continue; + } + cp.setFormProperty(idToFormPropertyMap.get(cp.getFormPropertyId())); + String nodeId = cp.getNodeId(); + if (StringUtils.isBlank(nodeId)) { + // 这里排除掉空nodeId的config + continue; + } + Optional shapeOp = jadeConfig.getShapeById(nodeId); + AppBuilderFormProperty formProperty = cp.getFormProperty(); + if (shapeOp.isEmpty()) { + // 2024/4/29 0029 暂时先不删除了,仅修改现存的内容 + continue; + } + formProperty.updateByShape(shapeOp.get()); + + // 更新 + this.formPropertyRepository.updateOne(formProperty); + } } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfigProperty.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfigProperty.java index 3e3ac35325..e1244d7319 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfigProperty.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderConfigProperty.java @@ -8,14 +8,14 @@ import static modelengine.fit.jober.aipp.domain.BaseDomain.lazyGet; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; -import modelengine.fitframework.inspection.Validation; /** * 应用构建器配置属性类 @@ -29,21 +29,13 @@ @NoArgsConstructor public class AppBuilderConfigProperty { private String id; - private String nodeId; - private String formPropertyId; - private String configId; - private AppBuilderFormProperty formProperty; - private AppBuilderConfig config; - private AppBuilderConfigRepository configRepository; - private AppBuilderFormRepository formRepository; - private AppBuilderFormPropertyRepository formPropertyRepository; public AppBuilderConfigProperty(AppBuilderConfigRepository configRepository, @@ -60,15 +52,4 @@ public AppBuilderFormProperty getFormProperty() { private AppBuilderFormProperty loadFormProperty() { return this.formPropertyRepository.selectWithId(this.formPropertyId); } - - private AppBuilderConfig getConfig() { - return lazyGet(this.config, this::loadConfig, this::setConfig); - } - - private AppBuilderConfig loadConfig() { - AppBuilderConfig appBuilderConfig = this.configRepository.selectWithId(this.configId); - Validation.notNull(appBuilderConfig, "App builder config can not be null."); - appBuilderConfig.setFormRepository(this.formRepository); - return appBuilderConfig; - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFlowGraph.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFlowGraph.java index c16e9dd936..a4815ece9c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFlowGraph.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFlowGraph.java @@ -6,10 +6,44 @@ package modelengine.fit.jober.aipp.domain; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.util.Entities; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; +import modelengine.jade.knowledge.dto.KnowledgeDto; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; /** * 应用构建器流程图类 @@ -22,9 +56,335 @@ @AllArgsConstructor @SuperBuilder public class AppBuilderFlowGraph extends BaseDomain { - private String id; + private static final Logger LOGGER = Logger.get(AppBuilderFlowGraph.class); + private static final String APP_BUILDER_DEFAULT_MODEL_NAME = "#app_builder_default_model_name#"; + private static final String APP_BUILDER_DEFAULT_SERVICE_NAME = "#app_builder_default_service_name#"; + private static final String APP_BUILDER_DEFAULT_TAG = "#app_builder_default_tag#"; + private static final String APP_BUILDER_DEFAULT_KNOWLEDGE_SET = "#app_builder_default_knowledge_set#"; + private static final int MODEL_LIST_SERVICE_NAME = 0; + private static final int MODEL_LIST_TAG = 1; + private String id; private String name; - private String appearance; + + /** + * 设置模型信息. + * + * @param modelInfo 模型信息. + */ + public void setModelInfo(String[] modelInfo) { + this.setAppearance(this.getAppearance() + .replace(APP_BUILDER_DEFAULT_MODEL_NAME, modelInfo[MODEL_LIST_SERVICE_NAME]) + .replace(APP_BUILDER_DEFAULT_SERVICE_NAME, modelInfo[MODEL_LIST_SERVICE_NAME]) + .replace(APP_BUILDER_DEFAULT_TAG, modelInfo[MODEL_LIST_TAG])); + } + + /** + * 设置知识库信息. + * + * @param knowledgeInfo 知识库信息. + */ + public void setKnowledgeInfo(KnowledgeDto knowledgeInfo) { + this.setAppearance(this.getAppearance().replace(APP_BUILDER_DEFAULT_KNOWLEDGE_SET, knowledgeInfo.getGroupId())); + } + + /** + * 当创建app时,对应的执行逻辑. + * + * @param context 操作人上下文. + */ + public void clone(OperationContext context) { + LocalDateTime now = LocalDateTime.now(); + this.setId(Entities.generateId()); + this.setCreateBy(context.getOperator()); + this.setCreateAt(now); + this.setUpdateBy(context.getOperator()); + this.setUpdateAt(now); + this.resetGraphId(); + } + + /** + * 重置 graph id. + * + */ + public void resetGraphId() { + try { + Map jsonAppearance = JSONObject.parseObject(this.getAppearance(), + new TypeReference>() {}); + jsonAppearance.computeIfPresent("id", (k, v) -> this.getId()); + + // 这里在创建应用时需要保证graph中的title+version唯一,否则在发布flow时会报错 + jsonAppearance.put("title", this.getId()); + + // 动态修改graph中的model为可选model的第一个 + this.setAppearance(JSONObject.toJSONString(jsonAppearance)); + } catch (JSONException e) { + LOGGER.error("Import config failed, cause: {}", e); + throw new AippException(AippErrCode.IMPORT_CONFIG_FIELD_ERROR, "flowGraph.appearance"); + } + } + + /** + * 通过properties修改appearance. + * + * @param formProperties 新的表单属性列表. + */ + public void updateByProperties(List formProperties) { + // 将dto的properties转成 {nodeId : {name:value, name:value}, ... }形式 + Map> nodeIdToPropertyNameValueMap = formProperties.stream() + .filter(fp -> StringUtils.isNotBlank(fp.getNodeId())) + .collect(Collectors.groupingBy(AppBuilderConfigFormPropertyDto::getNodeId)) + .entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() + .stream() + .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getName, + appBuilderConfigFormPropertyDto -> JsonUtils.toJsonString( + appBuilderConfigFormPropertyDto.getDefaultValue()))))); + JSONObject oldAppearanceObject = JSONObject.parseObject(this.appearance); + JSONObject page = ObjectUtils.cast(oldAppearanceObject.getJSONArray("pages").get(0)); + JSONArray shapes = page.getJSONArray("shapes"); + + for (int j = 0; j < shapes.size(); j++) { + JSONObject node = shapes.getJSONObject(j); + String nodeId = node.getString("id"); + String type = node.getString("type"); + if (!StringUtils.equals(type, "startNodeStart") && !type.endsWith("NodeState")) { + continue; + } + + Map nameValue = nodeIdToPropertyNameValueMap.get(nodeId); + + String flowMetaString = node.get("flowMeta").toString(); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode flowMeta = null; + try { + flowMeta = mapper.readTree(flowMetaString); + JsonNode params = flowMeta.findPath("inputParams"); + for (int i = 0; i < params.size(); i++) { + JsonNode child = params.get(i); + processParam(child, nameValue); + } + } catch (IOException e) { + e.printStackTrace(); + } + Object tt = JSON.parse(flowMeta.toString()); + node.put("flowMeta", tt); + } + + this.appearance = JSONObject.toJSONString(oldAppearanceObject); + } + + private void processParam(JsonNode node, Map params) { + List singleLayerParams = new ArrayList<>(Arrays.asList("model", "temperature", "systemPrompt")); + List doubleLayerParams = new ArrayList<>(Arrays.asList("tools", "workflows")); + if (params == null) { + return; + } + for (Map.Entry param : params.entrySet()) { + handleParam(node, param, singleLayerParams, doubleLayerParams); + } + } + + private void handleParam(JsonNode node, Map.Entry param, List singleLayerParams, + List doubleLayerParams) { + if (StringUtils.equals(node.get("name").asText(), param.getKey())) { + if (singleLayerParams.contains(param.getKey())) { + this.handleParamTemperature(node, param); + return; + } + + if (doubleLayerParams.contains(param.getKey())) { + ArrayNode valueArrayNode = convertList(param.getValue()); + ObjectUtils.cast(node).set("value", valueArrayNode); + return; + } + + if (StringUtils.equals("knowledge", param.getKey())) { + this.handleParamKnowledge(node, param); + return; + } + + if (StringUtils.equals("memory", param.getKey())) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + ArrayNode valueArrayNode = nodeFactory.arrayNode(); + Map res = JsonUtils.parseObject(param.getValue(), Map.class); + if (Objects.equals(res.get("type"), "UserSelect")) { + this.parseUserSelect(res, valueArrayNode); + } else { + this.parseOtherMemoryType(res, valueArrayNode); + } + ObjectUtils.cast(node).set("value", valueArrayNode); + } + } + } + + private void handleParamTemperature(JsonNode node, Map.Entry param) { + if (StringUtils.equals(param.getKey(), "temperature")) { + ObjectUtils.cast(node).put("value", JsonUtils.parseObject(param.getValue(), Float.class)); + } else { + ObjectUtils.cast(node).put("value", JsonUtils.parseObject(param.getValue(), String.class)); + } + } + + private ArrayNode convertList(String value) { + String[] res = JsonUtils.parseObject(value, String[].class); + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + + List> re = Arrays.stream(res).map(this::convert).toList(); + + ArrayNode valueArrayNode = nodeFactory.arrayNode(); + for (Map rr : re) { + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : rr.entrySet()) { + mapNode.put(entry.getKey(), entry.getValue()); + } + valueArrayNode.add(mapNode); + } + return valueArrayNode; + } + + private Map convert(String value) { + Map map = new HashMap<>(); + map.put("id", UUID.randomUUID().toString()); + map.put("from", "input"); + map.put("type", "String"); + map.put("value", value); + return map; + } + + private void handleParamKnowledge(JsonNode node, Map.Entry param) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + ArrayNode valueArrayNode = nodeFactory.arrayNode(); + List> res = + ObjectUtils.>>cast(JsonUtils.parseObject(param.getValue(), List.class)); + res.forEach(r -> { + ArrayNode valueArrayNode1 = nodeFactory.arrayNode(); + for (Map.Entry rr : r.entrySet()) { + if (StringUtils.equals(rr.getKey(), "id")) { + valueArrayNode1.add(convertId(rr.getKey(), ObjectUtils.cast(rr.getValue()).longValue())); + } else { + valueArrayNode1.add(convertObject(rr.getKey(), String.valueOf(rr.getValue()))); + } + } + Map a = new HashMap<>(); + a.put("id", UUID.randomUUID().toString()); + a.put("type", "Object"); + a.put("from", "Expand"); + a.put("value", valueArrayNode1); + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : a.entrySet()) { + if (StringUtils.equals(entry.getKey(), "value")) { + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } else { + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } + } + valueArrayNode.add(mapNode); + }); + ObjectUtils.cast(node).set("value", valueArrayNode); + } + + private ObjectNode convertId(String key, Long value) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + Map map = new HashMap<>(); + map.put("id", UUID.randomUUID().toString()); + map.put("name", key); + map.put("from", "input"); + map.put("type", "String"); + map.put("value", value); + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : map.entrySet()) { + if (StringUtils.equals(entry.getKey(), "value")) { + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } else { + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } + } + return mapNode; + } + + private ObjectNode convertObject(String key, String value) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + Map map = new HashMap<>(); + map.put("id", UUID.randomUUID().toString()); + map.put("name", key); + map.put("from", "input"); + map.put("type", "String"); + map.put("value", value); + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : map.entrySet()) { + mapNode.put(entry.getKey(), entry.getValue()); + } + return mapNode; + } + + private void parseUserSelect(Map res, ArrayNode valueArrayNode) { + for (Map.Entry resEntry : res.entrySet()) { + if (Objects.equals(resEntry.getKey(), AippConst.MEMORY_SWITCH_KEY)) { + this.checkEntryType(resEntry, Boolean.class); + valueArrayNode.add(this.convertMemorySwitch(resEntry.getKey(), ObjectUtils.cast(resEntry.getValue()))); + } else if (Objects.equals(resEntry.getKey(), "value")) { + valueArrayNode.add(this.convertValueForUserSelect(resEntry.getKey(), + String.valueOf(resEntry.getValue()))); + } else { + valueArrayNode.add(this.convertObject(resEntry.getKey(), String.valueOf(resEntry.getValue()))); + } + } + } + + private void checkEntryType(Map.Entry entry, Class clazz) { + if (!clazz.isInstance(entry.getValue())) { + throw new AippException(AippErrCode.UPDATE_APP_CONFIGURATION_FAILED, entry.getValue().getClass().getName()); + } + } + + private ObjectNode convertMemorySwitch(String key, Boolean isOpenSwitch) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + Map map = new HashMap<>(); + map.put("id", UUID.randomUUID().toString()); + map.put("name", key); + map.put("from", "Input"); + map.put("type", "Boolean"); + map.put("value", isOpenSwitch); + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : map.entrySet()) { + if (StringUtils.equals(entry.getKey(), "value")) { + this.checkEntryType(entry, Boolean.class); + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } else { + this.checkEntryType(entry, String.class); + mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); + } + } + return mapNode; + } + + private ObjectNode convertValueForUserSelect(String key, String value) { + JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + Map map = new HashMap<>(); + map.put("id", UUID.randomUUID().toString()); + map.put("name", key); + map.put("from", "input"); + map.put("type", StringUtils.EMPTY); + map.put("value", value); + ObjectNode mapNode = nodeFactory.objectNode(); + for (Map.Entry entry : map.entrySet()) { + mapNode.put(entry.getKey(), entry.getValue()); + } + return mapNode; + } + + private void parseOtherMemoryType(Map res, ArrayNode valueArrayNode) { + for (Map.Entry resEntry : res.entrySet()) { + if (Objects.equals(resEntry.getKey(), AippConst.MEMORY_SWITCH_KEY)) { + this.checkEntryType(resEntry, Boolean.class); + valueArrayNode.add(this.convertMemorySwitch(resEntry.getKey(), ObjectUtils.cast(resEntry.getValue()))); + } else { + valueArrayNode.add(this.convertObject(resEntry.getKey(), String.valueOf(resEntry.getValue()))); + } + } + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderForm.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderForm.java index d26e0898e3..28dfcf7b3f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderForm.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderForm.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.domain; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; import lombok.extern.slf4j.Slf4j; -import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import java.util.Map; @@ -28,19 +29,12 @@ @Slf4j public class AppBuilderForm extends BaseDomain { private String id; - private String name; - private String tenantId; - private Map appearance; - private String type; - private String version; - private String formSuiteId; - private AppBuilderFormPropertyRepository formPropertyRepository; public AppBuilderForm(AppBuilderFormPropertyRepository formPropertyRepository) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFormProperty.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFormProperty.java index da2c0615ed..5526d36b80 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFormProperty.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderFormProperty.java @@ -8,13 +8,19 @@ import static modelengine.fit.jober.aipp.domain.BaseDomain.lazyGet; +import modelengine.fit.jober.aipp.domains.jadeconfig.JadeShape; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; import java.util.ArrayList; +import java.util.Map; +import java.util.Optional; /** * 应用构建器表单属性实体类 @@ -27,27 +33,16 @@ @Builder public class AppBuilderFormProperty { private String id; - private String formId; - private String name; - private String dataType; - private Object defaultValue; - private String from; - private String group; - private String description; - private int index; - private String appId; - private AppBuilderForm form; - private AppBuilderFormRepository formRepository; public AppBuilderFormProperty(String id, String formId, String name, String dataType, Object defaultValue, @@ -91,6 +86,28 @@ public static AppBuilderConfigFormPropertyDto toAppBuilderConfigFormPropertyDto( .build(); } + /** + * 通过 {@link JadeShape} 修改数据. + * + * @param shape {@link JadeShape} 对象. + */ + public void updateByShape(JadeShape shape) { + Optional valueOp = shape.getValue(this.getName()); + if (valueOp.isEmpty()) { + // 2024/4/29 0029 暂时先不删除了,仅修改现存的内容 + return; + } + Object value = valueOp.get(); + if (StringUtils.equals(this.getName(), "model")) { + shape.getValue("accessInfo") + .ifPresentOrElse( + (v) -> this.setDefaultValue(ObjectUtils.>cast(v).get("serviceName")), + () -> this.setDefaultValue(value)); + } else { + this.setDefaultValue(value); + } + } + private AppBuilderForm loadForm() { return this.formRepository.selectWithId(this.formId); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderRuntimeInfo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderRuntimeInfo.java index 880db94b87..56ccb996c4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderRuntimeInfo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppBuilderRuntimeInfo.java @@ -6,10 +6,11 @@ package modelengine.fit.jober.aipp.domain; +import modelengine.fit.runtime.entity.Parameter; + import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; -import modelengine.fit.runtime.entity.Parameter; import java.util.List; @@ -24,29 +25,17 @@ @SuperBuilder public class AppBuilderRuntimeInfo extends BaseDomain { private Long id; - private String traceId; - private String flowDefinitionId; - private String instanceId; - private String nodeId; - private String nodeType; - private long startTime; - private long endTime; - private String status; - private boolean published; - private String errorMsg; - private String nextPositionId; - private List parameters; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppTemplate.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppTemplate.java index 10e8fba751..80fe6c023e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppTemplate.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/AppTemplate.java @@ -6,15 +6,18 @@ package modelengine.fit.jober.aipp.domain; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; +import static modelengine.fitframework.util.ObjectUtils.cast; + import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import modelengine.fitframework.inspection.Validation; import java.util.List; @@ -32,43 +35,24 @@ @SuperBuilder public class AppTemplate extends BaseDomain { private String id; - private String name; - private String builtType; - private String category; - private Map attributes; - private String appType; - private long like; - private long collection; - private long usage; - private String version; - private String configId; - private String flowGraphId; - private AppBuilderFlowGraph flowGraph; - private AppBuilderConfig config; - private List formProperties; - private AppBuilderFlowGraphRepository flowGraphRepository; - private AppBuilderConfigRepository configRepository; - private AppBuilderConfigPropertyRepository configPropertyRepository; - private AppBuilderFormRepository formRepository; - private AppBuilderFormPropertyRepository formPropertyRepository; public AppTemplate(AppBuilderFlowGraphRepository flowGraphRepository, AppBuilderConfigRepository configRepository, @@ -126,4 +110,40 @@ public List getFormProperties() { private List loadFormProperties() { return this.formPropertyRepository.selectWithAppId(this.id); } + + /** + * 获取 icon. + * + * @return {@link String} icon路径. + */ + public String getIcon() { + return cast(this.attributes.get("icon")); + } + + /** + * 设置icon. + * + * @param icon 图标. + */ + public void setIcon(String icon) { + this.attributes.put("icon", icon); + } + + /** + * 获取描述. + * + * @return {@link String} 描述信息. + */ + public String getDescription() { + return cast(this.attributes.get("description")); + } + + /** + * 设置描述信息. + * + * @param description 描述信息. + */ + public void setDescription(String description) { + this.attributes.put("description", description); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/BaseDomain.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/BaseDomain.java index b8841f59ee..62dca44a7f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/BaseDomain.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domain/BaseDomain.java @@ -27,13 +27,9 @@ @NoArgsConstructor public class BaseDomain { private String createBy; - private String updateBy; - private LocalDateTime createAt; - private LocalDateTime updateAt; - private Boolean isDeleted; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/App.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/App.java new file mode 100644 index 0000000000..0daae52edc --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/App.java @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.enums.AppCategory; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.util.UsefulUtils; +import modelengine.jade.store.entity.transfer.PluginToolData; +import modelengine.jade.store.service.AppService; + +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.StringUtils; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.PluginToolService; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * 应用. + * + * @author 张越 + * @since 2025-01-14 + */ +public class App { + private final String appSuiteId; + + // 注入. + private final AppVersionService appVersionService; + private final AppVersionRepository appVersionRepository; + private final AppVersionFactory appVersionFactory; + private final AppBuilderConfigRepository configRepository; + private final AppBuilderFlowGraphRepository flowGraphRepository; + private final AppBuilderFormPropertyRepository formPropertyRepository; + private final AippLogMapper aippLogMapper; + private final AppService appService; + private final AippChatMapper aippChatMapper; + private final Map exportMeta; + private final PluginToolService pluginToolService; + private final PluginService pluginService; + + // 懒加载数据. + private List appVersionList; + + App(String appSuiteId, AppVersionService appVersionService, AppBuilderConfigRepository configRepository, + AppBuilderFlowGraphRepository flowGraphRepository, AppBuilderFormPropertyRepository formPropertyRepository, + AippLogMapper aippLogMapper, AppService appService, AippChatMapper aippChatMapper, + AppVersionRepository appVersionRepository, AppVersionFactory appVersionFactory, + Map exportMeta, PluginToolService pluginToolService, PluginService pluginService) { + this.appSuiteId = appSuiteId; + this.appVersionService = appVersionService; + this.configRepository = configRepository; + this.flowGraphRepository = flowGraphRepository; + this.formPropertyRepository = formPropertyRepository; + this.aippLogMapper = aippLogMapper; + this.appService = appService; + this.aippChatMapper = aippChatMapper; + this.appVersionRepository = appVersionRepository; + this.appVersionFactory = appVersionFactory; + this.exportMeta = exportMeta; + this.pluginToolService = pluginToolService; + this.pluginService = pluginService; + } + + /** + * 获取版本数据. + * + * @return {@link List}{@code <}{@link AppVersion}{@code >} 列表. + */ + public List getVersions() { + return UsefulUtils.lazyGet(this.appVersionList, + () -> this.appVersionService.getByAppSuiteId(this.appSuiteId), + vs -> this.appVersionList = vs); + } + + /** + * 导出应用。 + * + * @param context 操作上下文。 + * @return {@link AppExportDto} 应用导出对象。 + */ + public AppExportDto export(OperationContext context) { + AppVersion latestVersion = this.getVersions() + .stream() + .max(Comparator.comparing(version -> version.getData().getUpdateAt())) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, "app version not exists.")); + return latestVersion.export(context, this.exportMeta); + } + + /** + * 导入版本数据。 + * + * @param appDto 应用导入导出基本信息。 + * @param contextRoot 请求上下文根 + * @param context 操作上下文。 + * @return {@link AppVersion} 版本对象。 + */ + public AppVersion importData(AppExportDto appDto, String contextRoot, OperationContext context) { + AppVersion appVersion = this.appVersionFactory.create(new AppBuilderAppPo(), this.appVersionRepository); + appVersion.importData(appDto, this.appSuiteId, contextRoot, context, this.exportMeta); + this.appVersionService.validateAppName(appVersion.getData().getName(), context); + this.appVersionService.save(appVersion); + return appVersion; + } + + /** + * 获取最新的版本对象. + * + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 应用版本op. + */ + public Optional getLatestVersion() { + return this.appVersionService.getLatestCreatedByAppSuiteId(this.appSuiteId); + } + + /** + * 删除整个app. + * + * @param context 操作人上下文信息. + */ + public void delete(OperationContext context) { + List configIds = this.getVersions().stream().map(a -> a.getData().getConfigId()).toList(); + this.configRepository.delete(configIds); + + List flowGraphIds = this.getVersions().stream().map(a -> a.getData().getFlowGraphId()).toList(); + this.flowGraphRepository.delete(flowGraphIds); + + List appIds = this.getVersions().stream().map(a -> a.getData().getAppId()).toList(); + this.appVersionService.deleteByIds(appIds); + this.formPropertyRepository.deleteByAppIds(appIds); + + List appTasks = this.getVersions().stream().flatMap(a -> a.getTasks(context).stream()).toList(); + if (CollectionUtils.isEmpty(appTasks)) { + return; + } + List instances = appTasks.stream().flatMap(t -> t.getInstances(context).stream()).toList(); + List instanceIds = instances.stream().map(i -> i.getEntity().getInstanceId()).toList(); + if (!CollectionUtils.isEmpty(instanceIds)) { + this.aippLogMapper.deleteByInstanceIds(instanceIds); + } + appTasks.forEach(t -> t.delete(context)); + this.aippChatMapper.deleteAppByAippId(this.appSuiteId); + Optional optionalAppVersion = this.getVersions().stream().findAny(); + if (optionalAppVersion.isEmpty()) { + return; + } + String type = optionalAppVersion.get().getData().getType(); + AppCategory appCategory = AppCategory.findByType(type).orElse(null); + if (appCategory == null) { + return; + } + List uniqueNames = appTasks.stream() + .filter(Objects::nonNull) + .map(t -> t.getEntity().getUniqueName()) + .filter(StringUtils::isNotBlank) + .distinct() + .toList(); + if (appCategory == AppCategory.WATER_FLOW) { + List pluginTools = this.pluginToolService.getPluginTools(uniqueNames); + pluginTools.forEach(pluginTool -> this.pluginService.deletePlugin(pluginTool.getPluginId())); + } else { + uniqueNames.forEach(this.appService::deleteApp); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/AppFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/AppFactory.java new file mode 100644 index 0000000000..2a1d648679 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/AppFactory.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app; + +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.jade.store.service.AppService; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.PluginToolService; + +import java.util.Map; + +/** + * {@link App} 工厂类. + * + * @author 张越 + * @since 2025-01-14 + */ +@Component +public class AppFactory { + private final AppVersionService appVersionService; + private final AppBuilderConfigRepository configRepository; + private final AppBuilderFlowGraphRepository flowGraphRepository; + private final AppBuilderFormPropertyRepository formPropertyRepository; + private final AippLogMapper aippLogMapper; + private final AppService appService; + private final AippChatMapper aippChatMapper; + private final AppVersionRepository appVersionRepository; + private final AppVersionFactory appVersionFactory; + private final Map exportMeta; + private final PluginToolService pluginToolService; + private final PluginService pluginService; + + public AppFactory(AppVersionService appVersionService, AppBuilderConfigRepository configRepository, + AppBuilderFlowGraphRepository flowGraphRepository, AppBuilderFormPropertyRepository formPropertyRepository, + AippLogMapper aippLogMapper, AppService appService, AippChatMapper aippChatMapper, + AppVersionRepository appVersionRepository, AppVersionFactory appVersionFactory, + @Value("${export-meta}") Map exportMeta, PluginToolService pluginToolService, + PluginService pluginService) { + this.appVersionService = appVersionService; + this.configRepository = configRepository; + this.flowGraphRepository = flowGraphRepository; + this.formPropertyRepository = formPropertyRepository; + this.aippLogMapper = aippLogMapper; + this.appService = appService; + this.aippChatMapper = aippChatMapper; + this.appVersionRepository = appVersionRepository; + this.appVersionFactory = appVersionFactory; + this.exportMeta = exportMeta; + this.pluginToolService = pluginToolService; + this.pluginService = pluginService; + } + + /** + * 创建 {@link App} 对象. + * + * @param appSuiteId app的唯一标识. + * @return {@link AppVersion} 对象. + */ + public App create(String appSuiteId) { + return new App(appSuiteId, + this.appVersionService, + this.configRepository, + this.flowGraphRepository, + this.formPropertyRepository, + this.aippLogMapper, + this.appService, + this.aippChatMapper, + this.appVersionRepository, + this.appVersionFactory, + this.exportMeta, + this.pluginToolService, + this.pluginService); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/AppDomainService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/AppDomainService.java new file mode 100644 index 0000000000..232b8d1c2a --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/AppDomainService.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app.service; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; + +import java.util.Map; + +/** + * 应用服务. + * + * @author 张越 + * @since 2025-01-14 + */ +public interface AppDomainService { + /** + * 通过appId删除应用. + * + * @param appId 应用版本id. + * @param context 操作人上下文对象. + */ + void deleteByAppId(String appId, OperationContext context); + + /** + * 导入一个应用。 + * + * @param appConfig 应用导入配置。 + * @param context 操作上下文。 + * @return {@link AppBuilderAppDto} 应用dto对象。 + */ + AppBuilderAppDto importApp(String appConfig, OperationContext context); + + /** + * 导出一个应用。 + * + * @param appId 导出应用的版本id。 + * @param exportMeta 导出应用元数据。 + * @param context 操作上下文。 + * @return {@link AppExportDto} 导出应用配置dto对象。 + */ + AppExportDto exportApp(String appId, Map exportMeta, OperationContext context); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/impl/AppDomainServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/impl/AppDomainServiceImpl.java new file mode 100644 index 0000000000..1aa83aec02 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/app/service/impl/AppDomainServiceImpl.java @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app.service.impl; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.util.Entities; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.app.App; +import modelengine.fit.jober.aipp.domains.app.AppFactory; +import modelengine.fit.jober.aipp.domains.app.service.AppDomainService; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fitframework.annotation.Value; +import modelengine.jade.app.engine.base.service.UsrAppCollectionService; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.AllArgsConstructor; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.transaction.Transactional; + +import java.util.Collections; +import java.util.Map; + +/** + * 应用服务实现类. + * + * @author 张越 + * @since 2025-01-14 + */ +@Component +public class AppDomainServiceImpl implements AppDomainService { + private static final Logger log = Logger.get(AppDomainServiceImpl.class); + + private final AppFactory appFactory; + private final AppVersionService appVersionService; + private final UploadedFileManageService uploadedFileManageService; + private final UsrAppCollectionService usrAppCollectionService; + private final ConverterFactory converterFactory; + private final String contextRoot; + + public AppDomainServiceImpl(AppFactory appFactory, AppVersionService appVersionService, + UploadedFileManageService uploadedFileManageService, UsrAppCollectionService usrAppCollectionService, + ConverterFactory converterFactory, @Value("${app-engine.contextRoot}") String contextRoot) { + this.appFactory = appFactory; + this.appVersionService = appVersionService; + this.uploadedFileManageService = uploadedFileManageService; + this.usrAppCollectionService = usrAppCollectionService; + this.converterFactory = converterFactory; + this.contextRoot = contextRoot; + } + + @Override + @Transactional + public void deleteByAppId(String appId, OperationContext context) { + AppVersion appVersion = this.appVersionService.retrieval(appId); + String appSuiteId = appVersion.getData().getAppSuiteId(); + App app = this.appFactory.create(appSuiteId); + app.delete(context); + this.uploadedFileManageService.cleanAippFiles(Collections.singletonList(appId)); + this.usrAppCollectionService.deleteByAppId(appId); + } + + @Override + @Transactional + public AppBuilderAppDto importApp(String appConfig, OperationContext context) { + try { + AppExportDto appExportDto = new ObjectMapper().readValue(appConfig, AppExportDto.class); + String suiteId = Entities.generateId(); + App app = this.appFactory.create(suiteId); + AppVersion appVersion = app.importData(appExportDto, this.contextRoot, context); + return this.converterFactory.convert(appVersion, AppBuilderAppDto.class); + } catch (JsonProcessingException e) { + log.error("Imported config file is not json", e); + throw new AippException(AippErrCode.IMPORT_CONFIG_NOT_JSON, e.getLocation().getLineNr(), + e.getLocation().getColumnNr()); + } + } + + @Override + public AppExportDto exportApp(String appId, Map exportMeta, OperationContext context) { + AppVersion appVersion = this.appVersionService.retrieval(appId); + return appVersion.export(context, exportMeta); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersion.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersion.java new file mode 100644 index 0000000000..7f071cdc33 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersion.java @@ -0,0 +1,1112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_CHAT_ERROR; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_CHAT_PUBLISHED_META_NOT_FOUND; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_HAS_PUBLISHED; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_NOT_FOUND_WHEN_CHAT; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_VERSION_HAS_ALREADY; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.INPUT_PARAM_IS_INVALID; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.NEW_VERSION_IS_LOWER; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.UPDATE_APP_CONFIGURATION_FAILED; +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_AIPP_TYPE_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_APP_IS_UPDATE; +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_META_STATUS_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_QUESTION_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.RESTART_MODE; +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.ACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.PREVIEW; +import static modelengine.fit.jober.aipp.enums.AppTypeEnum.APP; +import static modelengine.fit.jober.aipp.enums.RestartModeEnum.OVERWRITE; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotBlank; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNull; +import static modelengine.fitframework.util.ObjectUtils.cast; + +import lombok.Getter; +import modelengine.fel.tool.service.ToolService; +import modelengine.fit.jade.aipp.model.dto.ModelAccessInfo; +import modelengine.fit.jade.aipp.model.dto.ModelListDto; +import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; +import modelengine.fit.jade.waterflow.service.FlowDefinitionService; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.util.Entities; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.appversion.publish.FormProperyPublisher; +import modelengine.fit.jober.aipp.domains.appversion.publish.FlowPublisher; +import modelengine.fit.jober.aipp.domains.appversion.publish.GraphPublisher; +import modelengine.fit.jober.aipp.domains.appversion.publish.Publisher; +import modelengine.fit.jober.aipp.domains.appversion.publish.StorePublisher; +import modelengine.fit.jober.aipp.domains.appversion.publish.TaskPublisher; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.TaskDecorator; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.AippCreateDto; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.dto.AippNodeForms; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.dto.AppInputParam; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.dto.export.AppExportApp; +import modelengine.fit.jober.aipp.dto.export.AppExportConfig; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.dto.export.AppExportFlowGraph; +import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; +import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.enums.AppStatus; +import modelengine.fit.jober.aipp.factory.AppTemplateFactory; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.util.AippFileUtils; +import modelengine.fit.jober.aipp.util.AippStringUtils; +import modelengine.fit.jober.aipp.util.AppImExportUtil; +import modelengine.fit.jober.aipp.util.FlowInfoUtil; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.aipp.util.RandomPathUtils; +import modelengine.fit.jober.aipp.util.Retryable; +import modelengine.fit.jober.aipp.util.TemplateUtils; +import modelengine.fit.jober.aipp.util.UUIDUtil; +import modelengine.fit.jober.aipp.util.UsefulUtils; +import modelengine.fit.jober.aipp.util.VersionUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.fit.jober.common.exceptions.JobberException; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.merge.ConflictResolutionPolicy; +import modelengine.fitframework.transaction.DataAccessException; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.MapUtils; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; +import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.knowledge.KnowledgeCenterService; +import modelengine.jade.knowledge.dto.KnowledgeDto; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 应用版本. + * + * @author 张越 + * @since 2025-01-14 + */ +public class AppVersion { + private static final Logger LOGGER = Logger.get(AppVersion.class); + private static final String PUBLISH_UPDATE_DESCRIPTION_KEY = "publishedDescription"; + private static final String PUBLISH_UPDATE_LOG_KEY = "publishedUpdateLog"; + private static final int RETRY_PATH_GENERATION_TIMES = 3; + private static final int PATH_LENGTH = 16; + private static final String VERSION_FORMAT = "{0}.{1}.{2}"; + private static final int VERSION_LENGTH = 8; + private static final int RETRY_PREVIEW_TIMES = 5; + private static final Set TEMPLATE_DEFAULT_ATTRIBUTE_KEYS = new HashSet<>( + List.of("icon", "description", "greeting", "app_type")); + + @Getter + private AppBuilderAppPo data; + + @Getter + private Map attributes; + private final Integer maxQuestionLen; + private final Integer maxUserContextLen; + + // 注入属性. + private final AppBuilderFormPropertyRepository formPropertyRepository; + private final AppBuilderConfigRepository configRepository; + private final AppBuilderFormRepository formRepository; + private final AppBuilderConfigPropertyRepository configPropertyRepository; + private final AppBuilderFlowGraphRepository flowGraphRepository; + private final AppVersionRepository appVersionRepository; + private final AppTaskService appTaskService; + private final AppTaskInstanceService appTaskInstanceService; + private final FlowsService flowsService; + private final AppService appService; + private final PluginService pluginService; + private final ToolService toolService; + private final AppChatRepository appChatRepository; + private final AppDefinitionService appDefinitionService; + private final AippLogService aippLogService; + private final UploadedFileManageService uploadedFileManageService; + private final AppTemplateFactory templateFactory; + private final LocaleService localeService; + private final AippModelCenter aippModelCenter; + private final ConverterFactory converterFactory; + + // 加载属性. + private List formProperties; + private List tasks; + private AppBuilderConfig config; + private AppBuilderFlowGraph flowGraph; + private LocalDateTime baselineCreateTime; + private final AippFlowDefinitionService aippFlowDefinitionService; + private final FlowDefinitionService flowDefinitionService; + private final KnowledgeCenterService knowledgeCenterService; + private final String resourcePath; + + AppVersion(AppBuilderAppPo data, Dependencies dependencies) { + this.data = data; + this.attributes = StringUtils.isBlank(data.getAttributes()) + ? new HashMap<>() + : JsonUtils.parseObject(data.getAttributes()); + this.formPropertyRepository = dependencies.getFormPropertyRepository(); + this.appTaskService = dependencies.getAppTaskService(); + this.configRepository = dependencies.getConfigRepository(); + this.formRepository = dependencies.getFormRepository(); + this.configPropertyRepository = dependencies.getConfigPropertyRepository(); + this.flowGraphRepository = dependencies.getFlowGraphRepository(); + this.flowsService = dependencies.getFlowsService(); + this.appService = dependencies.getAppService(); + this.pluginService = dependencies.getPluginService(); + this.toolService = dependencies.getToolService(); + this.appVersionRepository = dependencies.getAppVersionRepository(); + this.appChatRepository = dependencies.getAppChatRepository(); + this.appDefinitionService = dependencies.getAppDefinitionService(); + this.aippLogService = dependencies.getAippLogService(); + this.uploadedFileManageService = dependencies.getUploadedFileManageService(); + this.templateFactory = dependencies.getTemplateFactory(); + this.appTaskInstanceService = dependencies.getAppTaskInstanceService(); + this.localeService = dependencies.getLocaleService(); + this.aippModelCenter = dependencies.getAippModelCenter(); + this.converterFactory = dependencies.getConverterFactory(); + this.aippFlowDefinitionService = dependencies.getAippFlowDefinitionService(); + this.flowDefinitionService = dependencies.getFlowDefinitionService(); + this.maxQuestionLen = dependencies.getMaxQuestionLen(); + this.maxUserContextLen = dependencies.getMaxUserContextLen(); + this.knowledgeCenterService = dependencies.getKnowledgeCenterService(); + this.resourcePath = dependencies.getResourcePath(); + } + + /** + * 获取baseLine创建时间. + * + * @param context 操作人上下文信息. + * @return {LocalDateTime} 对象. + */ + public LocalDateTime getBaselineCreateTime(OperationContext context) { + return UsefulUtils.lazyGet(this.baselineCreateTime, () -> { + List appTaskList = this.getTasks(context); + if (CollectionUtils.isEmpty(appTaskList)) { + return null; + } + return appTaskList.get(0).getEntity().getCreationTime(); + }, b -> this.baselineCreateTime = b); + } + + /** + * 获取表单配置项集合. + * + * @return {@link List}{@code <}{@link AppBuilderFormProperty}{@code >} 集合. + */ + public List getFormProperties() { + return UsefulUtils.lazyGet(this.formProperties, + () -> this.formPropertyRepository.selectWithAppId(this.data.getId()), ps -> this.formProperties = ps); + } + + /** + * 通过id获取 {@link AppBuilderFormProperty}. + * + * @param id {@link AppBuilderFormProperty} 唯一标识. + * @return {@link AppBuilderFormProperty} 对象. + */ + public AppBuilderFormProperty getFormProperty(String id) { + List appBuilderFormPropertyList = this.getFormProperties(); + return appBuilderFormPropertyList.stream() + .filter(p -> StringUtils.equals(id, p.getId())) + .findFirst() + .orElse(new AppBuilderFormProperty()); + } + + /** + * 获取任务列表,默认按创建时间降序排列. + * + * @param context 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 任务列表. + */ + public List getTasks(OperationContext context) { + return UsefulUtils.lazyGet(this.tasks, () -> this.appTaskService.getTasksByAppId(this.data.getAppId(), context) + .stream() + .peek(t -> t.setAppVersion(this)) + .toList(), ts -> this.tasks = ts); + } + + /** + * 获取配置. + * + * @return {@link AppBuilderConfig} 对象. + */ + public AppBuilderConfig getConfig() { + return UsefulUtils.lazyGet(this.config, this::loadConfig, v -> this.config = v); + } + + private AppBuilderConfig loadConfig() { + String configId = Validation.notNull(this.data.getConfigId(), "App config id can not be null."); + AppBuilderConfig appBuilderConfig = this.configRepository.selectWithId(configId); + Validation.notNull(appBuilderConfig, "App builder config can not be null."); + appBuilderConfig.setFormRepository(this.formRepository); + appBuilderConfig.setFormPropertyRepository(this.formPropertyRepository); + appBuilderConfig.setConfigPropertyRepository(this.configPropertyRepository); + appBuilderConfig.setAppVersion(this); + return appBuilderConfig; + } + + /** + * 获取画布数据. + * + * @return {@link AppBuilderFlowGraph} 数据. + */ + public AppBuilderFlowGraph getFlowGraph() { + return UsefulUtils.lazyGet(this.flowGraph, this::loadFlowGraph, g -> this.flowGraph = g); + } + + private AppBuilderFlowGraph loadFlowGraph() { + String flowGraphId = Validation.notNull(this.data.getFlowGraphId(), "App flow graph id can not be null."); + return this.flowGraphRepository.selectWithId(flowGraphId); + } + + /** + * app是否已经发布. + * + * @return true/false, true表示已发布; 否则, 未发布. + */ + public boolean isPublished() { + return StringUtils.equals(AppStatus.PUBLISHED.getName(), this.data.getStatus()); + } + + /** + * 发布一个应用 + * + * @param context 发布上下文. + */ + public void publish(PublishContext context) { + if (this.isPublished()) { + throw new AippException(APP_HAS_PUBLISHED); + } + + // 判断版本是否已存在. + this.validateVersion(context); + + // 发布. + List publishers = new ArrayList<>(); + publishers.add(new GraphPublisher(this.flowGraphRepository)); + publishers.add(new FormProperyPublisher(this.formPropertyRepository)); + publishers.add(new FlowPublisher(this.flowsService)); + publishers.add(new StorePublisher(this.appService, this.pluginService, this.toolService)); + publishers.add(new TaskPublisher(this.appTaskService)); + publishers.forEach(p -> p.publish(context, this)); + + // 修改appVersion的状态等属性并保存. + this.data.setState(AppState.PUBLISHED.getName()); + this.data.setStatus(AppStatus.PUBLISHED.getName()); + this.data.setIsActive(true); + this.data.setUpdateAt(LocalDateTime.now()); + this.data.setUpdateBy(context.getOperationContext().getOperator()); + this.data.setVersion(context.getPublishData().getVersion()); + this.data.setPublishAt(LocalDateTime.now()); + this.attributes.put(PUBLISH_UPDATE_DESCRIPTION_KEY, context.getPublishData().getPublishedDescription()); + this.attributes.put(PUBLISH_UPDATE_LOG_KEY, context.getPublishData().getPublishedUpdateLog()); + this.attributes.put(ATTR_APP_IS_UPDATE, true); + if (StringUtils.isBlank(this.data.getPath())) { + this.data.setPath(this.generateUniquePath()); + } + this.appVersionRepository.update(this); + } + + private String generateUniquePath() { + String path; + int retryTimes = RETRY_PATH_GENERATION_TIMES; + do { + path = RandomPathUtils.generateRandomString(PATH_LENGTH); + if (!this.appVersionRepository.checkPathExists(path)) { + return path; + } + LOGGER.warn("Path already exists, retrying... {} times left", retryTimes - 1); + } while (retryTimes-- > 0); + + LOGGER.error("Failed to generate a unique path for app after {} retries.", RETRY_PATH_GENERATION_TIMES); + throw new AippException(UPDATE_APP_CONFIGURATION_FAILED); + } + + /* + * 1、校验版本号的大小,若当前版本号比发布的版本号大,抛出AippErrCode.NEW_VERSION_IS_LOWER异常 + * 2、去任务表中查询,若已存在该版本的任务,抛出AippErrCode.APP_VERSION_HAS_ALREADY异常. + */ + private void validateVersion(PublishContext context) { + if (VersionUtils.compare(this.data.getVersion(), context.getPublishData().getVersion()) > 0) { + throw new AippParamException(NEW_VERSION_IS_LOWER); + } + RangedResultSet resultSet = this.appTaskService.getTasks(AppTask.asQueryEntity(0, 1) + .latest() + .addVersion(context.getPublishData().getVersion()) + .addAppSuiteId(this.data.getAppSuiteId()) + .putQueryAttribute(ATTR_AIPP_TYPE_KEY, AippTypeEnum.NORMAL.type()) + .putQueryAttribute(ATTR_META_STATUS_KEY, AippMetaStatusEnum.ACTIVE.getCode()) + .build(), context.getOperationContext()); + if (!resultSet.isEmpty()) { + throw new AippException(APP_VERSION_HAS_ALREADY); + } + } + + /** + * 运行 AppVersion,只能运行发布过的任务. + * + * @param context 运行上下文信息. + * @param session 会话对象. + */ + public void run(RunContext context, ChatSession session) { + // chatId不存在,创建一个新的chatId. + String appId = this.getAppIdByChatId(context); + + // 若chatId不存在,则创建个新的. + context.setChatId(StringUtils.blankIf(context.getChatId(), UUIDUtil.uuid())); + + // 如果是当前的appVersion,直接启动task + // 否则,执行被艾特的appVersion. + if (StringUtils.equals(appId, this.data.getAppId())) { + this.startTask(context, session); + } else { + AppVersion appVersion = this.appVersionRepository.selectById(appId) + .orElseThrow(() -> new AippException(APP_NOT_FOUND_WHEN_CHAT)); + RunContext clonedContext = context.businessDeepClone(); + clonedContext.setOriginAppId(this.data.getAppId()); + clonedContext.setOriginChatId(context.getChatId()); + clonedContext.setAppId(appId); + clonedContext.setChatId(context.getAtChatId()); + AppVersionDecorator.decorate(appVersion, this, this.appChatRepository).run(clonedContext, session); + } + } + + + public void validate(RunContext context, boolean isDebug) { + // 校验问题是否符合规范. + this.validateQuestion(context); + + // 添加用户上下文数据,输入参数校验. + OperationContext ctx = context.getOperationContext(); + AppTask task = isDebug ? this.getLatestTask(ctx) : this.getLatestPublishedTask(ctx); + this.validateUserContext(task, context.getUserContext(), context.getOperationContext()); + } + + /** + * 调试 AppVersion,和运行的唯一区别是不需要运行发布过的任务. + * + * @param context 运行上下文信息. + * @param session 会话对象. + */ + public void debug(RunContext context, ChatSession session) { + context.setDebug(true); + this.run(context, session); + } + + private void validateQuestion(RunContext context) { + if (!this.isApp()) { + return; + } + if (context.getQuestion() == null || !StringUtils.lengthBetween(context.getQuestion(), 1, this.maxQuestionLen, + true, true)) { + throw new AippParamException(INPUT_PARAM_IS_INVALID, BS_AIPP_QUESTION_KEY); + } + } + + private String getAppIdByChatId(RunContext context) { + String atChatId = context.getAtChatId(); + if (StringUtils.isNotBlank(atChatId)) { + return this.appChatRepository.getChatById(atChatId, context.getOperationContext().getAccount()) + .orElseThrow(() -> new AippException(APP_CHAT_ERROR)) + .getAppId(); + } + return StringUtils.isNotBlank(context.getAtAppId()) ? context.getAtAppId() : this.getData().getAppId(); + } + + private void startTask(RunContext context, ChatSession session) { + LOGGER.info("[perf] [{}] chat updateFlow end, appId={}", System.currentTimeMillis(), this.data.getAppId()); + + // 获取将要运行的任务对象. + OperationContext ctx = context.getOperationContext(); + AppTask task = context.isDebug() ? this.getLatestTask(ctx) : this.getLatestPublishedTask(ctx); + + // 执行任务. + context.initStartParams(); + doIfNull(context.getRestartMode(), () -> context.setRestartMode(OVERWRITE.getMode())); + context.setStartTime(LocalDateTime.now()); + TaskDecorator.create(task, this.aippLogService, this.appTaskInstanceService, this.localeService) + .exceptionLog() + .run(context, session); + + LOGGER.info("[perf] [{}] chat createInstanceByApp end, appId={}", System.currentTimeMillis(), + this.data.getAppId()); + } + + private void validateUserContext(AppTask task, Map userContext, OperationContext context) { + String flowDefinitionId = task.getEntity().getFlowDefinitionId(); + FlowInfo flowInfo = this.flowsService.getFlows(flowDefinitionId, context); + List inputParams = flowInfo.getInputParamsByName("input") + .stream() + .peek(map -> map.put("stringMaxLength", this.maxUserContextLen)) + .map(AppInputParam::from) + .toList(); + + if (this.isApp()) { + inputParams = inputParams.stream() + .filter(param -> !StringUtils.equals("Question", param.getName())) + .toList(); + } + if (MapUtils.isEmpty(userContext)) { + if (inputParams.stream().noneMatch((AppInputParam::isRequired))) { + return; + } + LOGGER.error("No user context when starting a chat."); + throw new AippParamException(INPUT_PARAM_IS_INVALID, "user context"); + } + inputParams.forEach(ip -> ip.validate(userContext)); + } + + /** + * 更新 flow + * + * @param context 操作上下文 + */ + public void updateFlows(OperationContext context) { + if (!this.isUpdated()) { + return; + } + this.preview(this.data.getVersion(), this.converterFactory.convert(this, AippDto.class), context); + this.attributes.put(ATTR_APP_IS_UPDATE, false); + this.appVersionRepository.update(this); + } + + /** + * 获取最新创建的任务. + * + * @param ctx 操作人上下文信息. + * @return {@link AppTask} 任务对象. + */ + public AppTask getLatestTask(OperationContext ctx) { + return this.appTaskService.getTasksByAppId(this.data.getAppId(), ctx) + .stream() + .peek(t -> t.setAppVersion(this)) + .findFirst() + .orElseThrow(() -> new AippException(AippErrCode.APP_CHAT_DEBUG_META_NOT_FOUND)); + } + + /** + * 获取任意已发布的任务,默认最新创建. + * + * @param ctx 操作人上下文信息. + * @return {@link AppTask} 任务对象. + */ + public AppTask getLatestPublishedTask(OperationContext ctx) { + return this.getPublishedTasks(ctx) + .stream() + .findFirst() + .orElseThrow(() -> new AippException(APP_CHAT_PUBLISHED_META_NOT_FOUND)); + } + + /** + * 获取任意已发布的任务集合 + * + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 任务对象. + */ + public List getPublishedTasks(OperationContext ctx) { + return this.appTaskService.getTaskList(this.data.getAppSuiteId(), NORMAL.name(), ACTIVE.getCode(), ctx) + .stream() + .peek(t -> t.setAppVersion(this)) + .toList(); + } + + /** + * 通过指定任务id,以及任务实例id的方式,重新启动流程. + * + * @param instance 任务实例. + * @param restartParams 重启参数. + * @param session SSE会话. + * @param context 操作人上下文对象. + * @param onFinished 完成时的回调. + */ + public void restart(AppTaskInstance instance, Map restartParams, ChatSession session, + OperationContext context, Consumer onFinished) { + List instanceLogs = instance.getLogs(); + List chatList = instance.getChats(); + + // 合并参数. + AppLog appLog = instanceLogs.iterator().next(); + Map mergedRestartParams = MapUtils.merge(restartParams, + appLog.getInput().orElseGet(HashMap::new), ConflictResolutionPolicy.OVERRIDE); + + RunContext runContext = new RunContext(new HashMap<>(), context); + runContext.setRestartMode(cast(mergedRestartParams.getOrDefault(RESTART_MODE, OVERWRITE.getMode()))); + runContext.setAppId(chatList.get(0).getAppId()); + runContext.setChatId(chatList.get(0).getChatId()); + runContext.setUserContext(mergedRestartParams); + runContext.putAllToBusiness(mergedRestartParams); + runContext.setQuestion(appLog.getLogData().getQuestion()); + if (chatList.size() == 2) { + runContext.setAtChatId(chatList.get(1).getChatId()); + } + if (runContext.isOverWriteMode()) { + instance.overWrite(); + } + this.run(runContext, session); + onFinished.accept(runContext); + } + + /** + * 获取 icon. + * + * @return {@link String} icon路径. + */ + public String getIcon() { + return cast(this.attributes.get("icon")); + } + + /** + * 获取描述. + * + * @return {@link String} 描述信息. + */ + public String getDescription() { + return cast(this.attributes.get("description")); + } + + /** + * 设置图标. + * + * @param icon 图标. + */ + public void setIcon(String icon) { + this.attributes.put("icon", icon); + } + + /** + * 设置描述. + * + * @param description 描述信息. + */ + public void setDescription(String description) { + this.attributes.put("description", description); + } + + /** + * 获取开场白. + * + * @return {@link String} 开场白. + */ + public String getGreeting() { + return cast(this.attributes.getOrDefault("greeting", StringUtils.EMPTY)); + } + + /** + * 获取分类. + * + * @return {@link String} 分类信息. + */ + public String getClassification() { + return cast(this.attributes.get("app_type")); + } + + /** + * 是否是App. + * + * @return true/false. + */ + public boolean isApp() { + return StringUtils.equals(APP.code(), this.data.getType()); + } + + /** + * 是否被修改过. + * + * @return true/false. + */ + public boolean isUpdated() { + return ObjectUtils.cast(this.attributes.getOrDefault(AippConst.ATTR_APP_IS_UPDATE, true)); + } + + /** + * 当新建appVersion时调用. + * + * @param dto 创建时的数据集合. + * @param version 版本号. + * @param type 应用类型. + * @param context 操作人上下文信息. + */ + public void cloneVersion(AppBuilderAppCreateDto dto, String version, String type, OperationContext context) { + String newAppId = Entities.generateId(); + + // 画布数据. + AppBuilderFlowGraph graph = this.getFlowGraph(); + graph.setModelInfo(this.getFirstModelInfo(context)); + graph.setKnowledgeInfo(this.getFirstKnowledgeInfo(context)); + graph.clone(context); + this.data.setFlowGraphId(graph.getId()); + + // 配置. + AppBuilderConfig appBuilderConfig = this.getConfig(); + appBuilderConfig.clone(this.getFormProperties(), context); + appBuilderConfig.setAppId(newAppId); + this.data.setConfigId(appBuilderConfig.getId()); + + LocalDateTime now = LocalDateTime.now(); + this.data.setId(newAppId); + this.data.setType(type); + this.data.setTenantId(context.getTenantId()); + this.data.setCreateBy(context.getOperator()); + this.data.setCreateAt(now); + this.data.setUpdateBy(context.getOperator()); + this.data.setUpdateAt(now); + this.data.setAppId(newAppId); + this.data.setIsActive(false); + this.data.setStatus(AppStatus.DRAFT.getName()); + this.data.setVersion(version); + this.getFormProperties().forEach(p -> p.setAppId(newAppId)); + + if (Objects.nonNull(dto)) { + this.attributes.clear(); + this.attributes.put("description", dto.getDescription()); + this.attributes.put("icon", dto.getIcon()); + this.attributes.put("greeting", dto.getGreeting()); + this.attributes.put("app_type", dto.getAppType()); + if (StringUtils.isNotBlank(dto.getStoreId())) { + this.attributes.put("store_id", dto.getStoreId()); + this.data.setUniqueName(dto.getStoreId()); + } + this.data.setName(dto.getName()); + this.data.setType(dto.getType()); + this.data.setAppCategory(dto.getAppCategory()); + this.data.setAppBuiltType(dto.getAppBuiltType()); + this.data.setAppType(dto.getAppType()); + } + + AippCreateDto aippCreateDto = this.preview(version, this.converterFactory.convert(this, AippDto.class), + context); + this.data.setAppSuiteId(aippCreateDto.getAippId()); + } + + private KnowledgeDto getFirstKnowledgeInfo(OperationContext context) { + return this.knowledgeCenterService.getSupportKnowledges(context.getOperator()).get(0); + } + + private String[] getFirstModelInfo(OperationContext context) { + ModelListDto modelList = this.aippModelCenter.fetchModelList(AippConst.CHAT_MODEL_TYPE, null, context); + if (modelList != null && modelList.getModels() != null && !modelList.getModels().isEmpty()) { + ModelAccessInfo firstModel = modelList.getModels().get(0); + return new String[] {firstModel.getServiceName(), firstModel.getTag()}; + } else { + return new String[] {StringUtils.EMPTY, StringUtils.EMPTY}; + } + } + + /** + * 将当前应用版本发布为模板. + * + * @param createDto 模板创建参数. + * @param context 操作人上下文信息. + * @return {@link TemplateInfoDto} 对象. + */ + public TemplateInfoDto publishTemplate(TemplateAppCreateDto createDto, OperationContext context) { + String newAppID = Entities.generateId(); + + AppBuilderFlowGraph graph = this.getFlowGraph(); + graph.setId(Entities.generateId()); + this.data.setFlowGraphId(graph.getId()); + + // 配置. + AppBuilderConfig appBuilderConfig = this.getConfig(); + appBuilderConfig.clone(this.getFormProperties(), context); + appBuilderConfig.setAppId(newAppID); + this.data.setConfigId(appBuilderConfig.getId()); + + this.data.setId(newAppID); + this.data.setAppId(newAppID); + + // 只保留模板相关的属性. + this.attributes = this.attributes.entrySet() + .stream() + .filter(e -> TEMPLATE_DEFAULT_ATTRIBUTE_KEYS.contains(e.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + // 创建参数设置. + if (createDto != null) { + this.data.setName(createDto.getName()); + this.data.setAppType(createDto.getAppType()); + this.setDescription(createDto.getDescription()); + String icon = this.getIcon(); + if (this.isLegalIcon(createDto, icon)) { + try { + String copiedIcon = this.uploadedFileManageService.copyIconFiles(icon, this.getData().getId(), + context.getAccount()); + this.setIcon(copiedIcon); + } catch (IOException e) { + LOGGER.warn("Failed to create a copy of icon when publish template.", e); + this.setIcon(StringUtils.EMPTY); + } + } + } + + LocalDateTime now = LocalDateTime.now(); + this.data.setCreateBy(context.getOperator()); + this.data.setCreateAt(now); + this.data.setUpdateBy(context.getOperator()); + this.data.setUpdateAt(now); + + graph.setCreateBy(context.getOperator()); + graph.setCreateAt(now); + graph.setUpdateBy(context.getOperator()); + graph.setUpdateAt(now); + + AppTemplate template = this.converterFactory.convert(this, AppTemplate.class); + this.templateFactory.save(template); + String icon = this.getIcon(); + if (StringUtils.isNotBlank(icon)) { + this.uploadedFileManageService.updateRecord(this.data.getId(), AippFileUtils.getFileNameFromIcon(icon), 0); + } + return TemplateUtils.convertToTemplateDto(template); + } + + private boolean isLegalIcon(TemplateAppCreateDto createDto, String icon) { + return StringUtils.isNotBlank(icon) && StringUtils.equals(icon, createDto.getIcon()); + } + + /** + * 升级应用. + * + * @param dto 创建时的数据集合. + * @param appType 应用类型. + * @param context 操作人上下文信息. + */ + public void upgrade(AppBuilderAppCreateDto dto, String appType, OperationContext context) { + // 构建新的版本号. + String preVersion = this.data.getVersion(); + String[] parts = preVersion.split("\\."); + parts[2] = String.valueOf(Integer.parseInt(parts[2]) + 1); + String newVersion = StringUtils.format(VERSION_FORMAT, parts[0], parts[1], parts[2]); + String nextVersion = newVersion.length() > VERSION_LENGTH ? preVersion : newVersion; + + this.cloneVersion(dto, nextVersion, appType, context); + this.data.setState(AppState.INACTIVE.getName()); + this.data.setIsActive(false); + this.data.setStatus(AppStatus.DRAFT.getName()); + this.attributes.put("latest_version", preVersion); + } + + /** + * 创建应用. + */ + public void create() { + this.data.setState(AppState.INACTIVE.getName()); + } + + /** + * 导入应用数据。 + * + * @param appDto 导入应用的基础信息。 + * @param appSuiteId app唯一标识。 + * @param contextRoot 请求上下文根 + * @param context 操作上下文。 + * @param exportMeta 应用导入导出元数据。 + */ + public void importData(AppExportDto appDto, String appSuiteId, String contextRoot, OperationContext context, + Map exportMeta) { + // 检查导入应用配置是否合法 + if (!StringUtils.equals(appDto.getVersion(), exportMeta.get("version"))) { + throw new AippException(AippErrCode.IMPORT_CONFIG_UNMATCHED_VERSION, exportMeta.get("version"), + appDto.getVersion()); + } + AppImExportUtil.checkAppExportDto(appDto); + + this.data = this.converterFactory.convert(appDto.getApp(), AppBuilderAppPo.class); + this.data.setAppSuiteId(appSuiteId); + this.data.setState(AppState.IMPORTING.getName()); + + // 设置应用名称 + String initAppName = appDto.getApp().getName(); + List similarNames = this.appVersionRepository.selectWithSimilarName(initAppName); + String newName = AppImExportUtil.generateNewAppName(similarNames, initAppName); + this.data.setName(newName); + + this.attributes = JsonUtils.parseObject(this.data.getAttributes()); + this.config = AppImExportUtil.convertToAppBuilderConfig(appDto.getConfig(), context); + this.flowGraph = AppImExportUtil.convertToAppBuilderFlowGraph(appDto.getFlowGraph(), context); + this.formProperties = AppImExportUtil.getFormProperties(this.config.getConfigProperties()); + + // 对于有头像的应用数据,需要保存头像文件 + String iconPath = appDto.getIconPath(contextRoot, this.resourcePath, context); + if (!StringUtils.isBlank(iconPath)) { + this.setIcon(iconPath); + this.uploadedFileManageService.addFileRecord(this.getData().getAppId(), context.getAccount(), + AippFileUtils.getFileNameFromIcon(iconPath), Entities.generateId()); + } + this.cloneVersion(null, "1.0.0", appDto.getType(), context); + } + + /** + * 导出. + * + * @param context 操作人上下文信息. + * @param exportMeta 导出元数据. + * @return {@link AppExportDto} 导出的数据. + */ + public AppExportDto export(OperationContext context, Map exportMeta) { + if (!StringUtils.equals(this.getData().getCreateBy(), context.getName())) { + throw new AippException(AippErrCode.EXPORT_CONFIG_UNAUTHED); + } + // 校验流程编排合法性 + try { + String flowDefinitionData = + this.aippFlowDefinitionService.getParsedGraphData(this.getFlowGraph().getAppearance(), + this.getData().getVersion()); + this.flowDefinitionService.validateDefinitionData(flowDefinitionData); + } catch (Exception e) { + LOGGER.error("app config export failed", e); + throw new AippException(AippErrCode.EXPORT_INVALID_FLOW_EXCEPTION); + } + try { + AppExportApp exportAppInfo = this.converterFactory.convert(this, AppExportApp.class); + String icon = this.getIcon(); + doIfNotBlank(icon, exportAppInfo::setIcon); + + return AppExportDto.builder() + .version(exportMeta.get("version")) + .app(exportAppInfo) + .config(this.converterFactory.convert(this.getConfig(), AppExportConfig.class)) + .flowGraph(this.converterFactory.convert(this.getFlowGraph(), AppExportFlowGraph.class)) + .build(); + } catch (DataAccessException e) { + LOGGER.error("app config export failed", e); + throw new AippException(AippErrCode.EXPORT_CONFIG_DB_EXCEPTION); + } + } + + /** + * 判断两个应用是否相同. + * + * @param appVersion 应用版本对象. + * @return true/false. + */ + public boolean isEqual(AppVersion appVersion) { + return StringUtils.equals(this.getData().getAppId(), appVersion.getData().getAppId()); + } + + /** + * put所有属性. + * + * @param attributes 属性集合. + */ + public void putAttributes(Map attributes) { + this.attributes.putAll(attributes); + this.attributes.put(AippConst.ATTR_APP_IS_UPDATE, true); + } + + /** + * 配置配置. + * + * @param configDto 待更新数据. + * @param properties 新的属性列表. + * @param context 操作人上下文信息. + */ + public void updateConfig(AppBuilderConfigDto configDto, List properties, + OperationContext context) { + this.getConfig().updateByProperties(properties); + this.getConfig().setUpdateBy(context.getOperator()); + this.getConfig().setUpdateAt(LocalDateTime.now()); + this.configRepository.updateOne(this.getConfig()); + AppBuilderForm form = this.getConfig().getForm(); + form.setUpdateBy(context.getOperator()); + form.setUpdateAt(LocalDateTime.now()); + form.setName(configDto.getForm().getName()); + form.setAppearance(configDto.getForm().getAppearance()); + this.formRepository.updateOne(form); + } + + /** + * 更新graph. + * + * @param properties 新的form属性列表. + * @param context 操作人上下文信息. + */ + public void updateGraph(List properties, OperationContext context) { + this.getFlowGraph().setUpdateBy(context.getOperator()); + this.getFlowGraph().setUpdateAt(LocalDateTime.now()); + this.getFlowGraph().updateByProperties(properties); + this.flowGraphRepository.updateOne(this.getFlowGraph()); + } + + /** + * 预览appVersion. + * + * @param baselineVersion 基线版本. + * @param aippDto 数据. + * @param context 操作人上下文信息. + * @return {@link AippCreateDto} 对象. + * @throws AippException 流程异常. + */ + public AippCreateDto preview(String baselineVersion, AippDto aippDto, OperationContext context) + throws AippException { + List appTasks = this.getTasks(context); + if (CollectionUtils.isNotEmpty(appTasks)) { + AppTask task = appTasks.get(0); + if (task.isPublished()) { + return AippCreateDto.builder() + .aippId(task.getEntity().getAppSuiteId()) + .version(task.getEntity().getVersion()) + .build(); + } + } + FlowDefinitionResult definitionResult = this.appDefinitionService.getSameFlowDefinition(aippDto); + if (definitionResult != null) { + RangedResultSet resultSet = this.appTaskService.getTasks(AppTask.asQueryEntity(0, 1) + .latest() + .putQueryAttribute(AippConst.ATTR_FLOW_DEF_ID_KEY, definitionResult.getFlowDefinitionId()) + .putQueryAttribute(AippConst.ATTR_FLOW_CONFIG_ID_KEY, definitionResult.getMetaId()) + .build(), context); + if (!resultSet.isEmpty()) { + AppTask task = resultSet.getResults().get(0); + return AippCreateDto.builder() + .aippId(task.getEntity().getAppSuiteId()) + .version(task.getEntity().getVersion()) + .build(); + } + } + // 过滤预览版本 + if (AippStringUtils.isPreview(baselineVersion)) { + throw new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, "version is preview"); + } + + // 创建预览版本 + Retryable retryable = new Retryable<>( + () -> this.createPreviewAipp(baselineVersion, aippDto, context), RETRY_PREVIEW_TIMES); + retryable.setObserveException(JobberException.class); + retryable.setBreakCondition(e -> e.getCode() != ErrorCodes.FLOW_ALREADY_EXIST.getErrorCode()); + retryable.setExceptionConsumer((e, times) -> this.handleException(e, times, aippDto)); + return retryable.retry().orElseThrow(e -> this.handleException(context, e)); + } + + private void handleException(JobberException e, int times, AippDto aippDto) { + LOGGER.warn("create preview aipp failed, times {} aippId {} version {}, error {}", times, + this.getData().getAppSuiteId(), aippDto.getPreviewVersion(), e.getMessage()); + } + + private AippCreateDto createPreviewAipp(String baselineVersion, AippDto aippDto, OperationContext context) { + String previewVersion = VersionUtils.buildPreviewVersion(baselineVersion); + aippDto.setPreviewVersion(previewVersion); + + // 创建、发布流程定义 + FlowInfo flowInfo = this.flowsService.publishFlowsWithoutElsa(aippDto.getFlowId(), + previewVersion, + JsonUtils.toJsonString(aippDto.getFlowViewData()), + context); + + // 预览时,aipp 的 version 用的是 flowInfo 的 version,是否合理待确认 + aippDto.setVersion(flowInfo.getVersion()); + List aippNodeForms = FlowInfoUtil.buildAippNodeForms(flowInfo, this.getFormProperties()); + + // 构建创建参数. + AppTask createArgs = AppTask.asCreateEntity() + .fetch(aippDto) + .setBaseLineVersion(baselineVersion) + .setAppSuiteId(this.getData().getAppSuiteId()) + .fetch(aippNodeForms) + .setFlowConfigId(flowInfo.getFlowId()) + .setFlowDefinitionId(flowInfo.getFlowDefinitionId()) + .setAippType(PREVIEW.name()) + .setStatus(ACTIVE.getCode()) + .setPublishTime(LocalDateTime.now().toString()) + .build(); + LOGGER.debug("create aipp, task info {}", createArgs.getEntity().toString()); + AppTask appTask = this.appTaskService.createTask(createArgs, context); + return AippCreateDto.builder().aippId(appTask.getEntity().getAppSuiteId()).version(previewVersion).build(); + } + + private AippException handleException(OperationContext context, JobberException exception) { + LOGGER.error("Failed to preview aipp.[errorMsg={}]", exception.getMessage()); + switch (ErrorCodes.getErrorCodes(exception.getCode())) { + case INVALID_FLOW_NODE_SIZE: + return new AippException(context, AippErrCode.INVALID_FLOW_NODE_SIZE); + case INVALID_START_NODE_EVENT_SIZE: + return new AippException(context, AippErrCode.INVALID_START_NODE_EVENT_SIZE); + case INVALID_EVENT_CONFIG: + case INVALID_STATE_NODE_EVENT_SIZE: + return new AippException(context, AippErrCode.INVALID_EVENT_CONFIG); + default: + return new AippException(context, AippErrCode.INVALID_FLOW_CONFIG); + } + } + + /** + * 克隆应用版本 + * + * @param cloneApp 需要克隆的应用版本的 {@link AppVersion}。 + */ + public void cloneVersion(AppVersion cloneApp) { + List resetFormProperties = cloneApp.getFormProperties(); + List currentFormProperties = this.getFormProperties(); + Map currentPropMap = currentFormProperties.stream() + .collect(Collectors.toMap(AppBuilderFormProperty::getName, Function.identity())); + resetFormProperties.forEach(resetProp -> { + AppBuilderFormProperty currentProp = currentPropMap.get(resetProp.getName()); + if (currentProp != null) { + currentProp.setDefaultValue(resetProp.getDefaultValue()); + } + }); + this.formPropertyRepository.updateMany(currentFormProperties); + AppBuilderFlowGraph resetGraph = cloneApp.getFlowGraph(); + AppBuilderFlowGraph currentGraph = this.getFlowGraph(); + String currentGraphId = cloneApp.getFlowGraph().getId(); + resetGraph.setId(currentGraphId); + resetGraph.resetGraphId(); + + currentGraph.setAppearance(resetGraph.getAppearance()); + this.flowGraphRepository.updateOne(currentGraph); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionDecorator.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionDecorator.java new file mode 100644 index 0000000000..9477283585 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionDecorator.java @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_CHAT_ORIGIN_APP_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_CHAT_ORIGIN_APP_VERSION_KEY; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.service.AippChatService; +import modelengine.fit.jober.aipp.util.UsefulUtils; +import modelengine.fitframework.util.MapBuilder; + +import java.util.Map; + +/** + * 装饰器. + * + * @author 张越 + * @since 2025-02-10 + */ +public class AppVersionDecorator { + private final AppVersion appVersion; + private final AppChatRepository appChatRepository; + private final AppVersion origin; + + private AppVersionDecorator(AppVersion appVersion, AppChatRepository appChatRepository) { + this(appVersion, null, appChatRepository); + } + + private AppVersionDecorator(AppVersion appVersion, AppVersion origin, AppChatRepository appChatRepository) { + this.appVersion = appVersion; + this.appChatRepository = appChatRepository; + this.origin = origin; + } + + /** + * 对 appVersion 进行装饰. + * + * @param appVersion {@link AppVersion} 对象. + * @param appChatRepository {@link AippChatService} 对象. + * @return {@link AppVersionDecorator} 对象. + */ + public static AppVersionDecorator decorate(AppVersion appVersion, AppChatRepository appChatRepository) { + return new AppVersionDecorator(appVersion, appChatRepository); + } + + /** + * 对 appVersion 进行装饰. + * + * @param appVersion {@link AppVersion} 对象. + * @param origin 最开始的 {@link AppVersion} 对象. + * @param appChatRepository {@link AppChatRepository} 对象. + * @return {@link AppVersionDecorator} 对象. + */ + public static AppVersionDecorator decorate(AppVersion appVersion, AppVersion origin, + AppChatRepository appChatRepository) { + return new AppVersionDecorator(appVersion, origin, appChatRepository); + } + + /** + * 运行. + * + * @param context 上下文. + * @param session 会话session. + */ + public void run(RunContext context, ChatSession session) { + this.appVersion.run(context, session); + this.saveChat(context); + } + + /** + * 调试 AppVersion,和运行的唯一区别是不需要运行发布过的任务. + * + * @param context 运行上下文信息. + * @param session 会话对象. + */ + public void debug(RunContext context, ChatSession session) { + this.appVersion.debug(context, session); + this.saveChat(context); + } + + /** + * 通过指定任务id,以及任务实例id的方式,重新启动流程. + * + * @param instance 任务实例. + * @param restartParams 重启参数. + * @param session SSE会话. + * @param context 操作人上下文对象. + */ + public void restart(AppTaskInstance instance, Map restartParams, ChatSession session, + OperationContext context) { + this.appVersion.restart(instance, restartParams, session, context, this::saveChat); + } + + private void saveChat(RunContext rc) { + Map attributes = MapBuilder.get() + .put(AippConst.ATTR_CHAT_INST_ID_KEY, rc.getTaskInstanceId()) + .put(AippConst.ATTR_CHAT_STATE_KEY, this.appVersion.getData().getState()) + .put(AippConst.BS_AIPP_ID_KEY, this.appVersion.getData().getAppSuiteId()) + .build(); + + if (this.origin != null) { + attributes.put(ATTR_CHAT_ORIGIN_APP_KEY, this.origin.getData().getAppId()); + attributes.put(ATTR_CHAT_ORIGIN_APP_VERSION_KEY, this.origin.getData().getVersion()); + } else { + UsefulUtils.doIfNotBlank(rc.getDimensionId(), (dId) -> attributes.put(AippConst.BS_DIMENSION_ID_KEY, dId)); + } + + this.appChatRepository.saveChat(ChatCreateEntity.builder() + .appId(this.appVersion.getData().getAppId()) + .appVersion(this.appVersion.getData().getVersion()) + .chatName(rc.getQuestion()) + .chatId(rc.getChatId()) + .taskInstanceId(rc.getTaskInstanceId()) + .attributes(attributes) + .build(), rc.getOperationContext()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionFactory.java new file mode 100644 index 0000000000..ebc8e3a83b --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionFactory.java @@ -0,0 +1,151 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import modelengine.fel.tool.service.ToolService; +import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.service.FlowDefinitionService; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.factory.AppTemplateFactory; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fitframework.annotation.Value; +import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.knowledge.KnowledgeCenterService; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; + +import modelengine.fitframework.annotation.Component; + +import java.util.Optional; + +/** + * {@link AppVersion} 工厂类. + * + * @author 张越 + * @since 2025-01-14 + */ +@Component +public class AppVersionFactory { + private final AppBuilderFormPropertyRepository formPropertyRepository; + private final AppTaskService appTaskService; + private final AppBuilderConfigRepository configRepository; + private final AppBuilderFormRepository formRepository; + private final AppBuilderConfigPropertyRepository configPropertyRepository; + private final AppBuilderFlowGraphRepository flowGraphRepository; + private final FlowsService flowsService; + private final AppService appService; + private final PluginService pluginService; + private final ToolService toolService; + private final AppChatRepository appChatRepository; + private final AppDefinitionService appDefinitionService; + private final AippLogService aippLogService; + private final UploadedFileManageService uploadedFileManageService; + private final AppTemplateFactory templateFactory; + private final AppTaskInstanceService appTaskInstanceService; + private final LocaleService localeService; + private final AippModelCenter aippModelCenter; + private final ConverterFactory converterFactory; + private final AippFlowDefinitionService aippFlowDefinitionService; + private final FlowDefinitionService flowDefinitionService; + private final Integer maxQuestionLen; + private final Integer maxUserContextLen; + private final KnowledgeCenterService knowledgeCenterService; + private final String resourcePath; + + public AppVersionFactory(AppBuilderFormPropertyRepository formPropertyRepository, AppTaskService appTaskService, + AppBuilderConfigRepository configRepository, AppBuilderFormRepository formRepository, + AppBuilderConfigPropertyRepository configPropertyRepository, + AppBuilderFlowGraphRepository flowGraphRepository, FlowsService flowsService, AppService appService, + PluginService pluginService, ToolService toolService, AppChatRepository appChatRepository, + AppDefinitionService appDefinitionService, AippLogService aippLogService, + UploadedFileManageService uploadedFileManageService, AppTemplateFactory templateFactory, + AppTaskInstanceService appTaskInstanceService, LocaleService localeService, AippModelCenter aippModelCenter, + ConverterFactory converterFactory, AippFlowDefinitionService aippFlowDefinitionService, + FlowDefinitionService flowDefinitionService, + @Value("${app-engine.question.max-length}") Integer maxQuestionLen, + @Value("${app-engine.user-context.max-length}") Integer maxUserContextLen, + KnowledgeCenterService knowledgeCenterService, + @Value("${app-engine.resource.path}") String resourcePath) { + this.formPropertyRepository = formPropertyRepository; + this.appTaskService = appTaskService; + this.configRepository = configRepository; + this.formRepository = formRepository; + this.configPropertyRepository = configPropertyRepository; + this.flowGraphRepository = flowGraphRepository; + this.flowsService = flowsService; + this.appService = appService; + this.pluginService = pluginService; + this.toolService = toolService; + this.appChatRepository = appChatRepository; + this.appDefinitionService = appDefinitionService; + this.aippLogService = aippLogService; + this.uploadedFileManageService = uploadedFileManageService; + this.templateFactory = templateFactory; + this.appTaskInstanceService = appTaskInstanceService; + this.localeService = localeService; + this.aippModelCenter = aippModelCenter; + this.converterFactory = converterFactory; + this.aippFlowDefinitionService = aippFlowDefinitionService; + this.flowDefinitionService = flowDefinitionService; + this.maxQuestionLen = maxQuestionLen != null ? maxQuestionLen : 20000; + this.maxUserContextLen = maxUserContextLen != null ? maxUserContextLen : 500; + this.knowledgeCenterService = knowledgeCenterService; + this.resourcePath = resourcePath; + } + + /** + * 创建 {@link AppVersion} 对象. + * + * @param data 数据类. + * @param appVersionRepository {@link AppVersionRepository} 对象. + * @return {@link AppVersion} 对象. + */ + public AppVersion create(AppBuilderAppPo data, AppVersionRepository appVersionRepository) { + return new AppVersion(Optional.ofNullable(data).orElseGet(AppBuilderAppPo::new), Dependencies.builder() + .formPropertyRepository(this.formPropertyRepository) + .appTaskService(this.appTaskService) + .configRepository(this.configRepository) + .formRepository(this.formRepository) + .configPropertyRepository(this.configPropertyRepository) + .flowGraphRepository(this.flowGraphRepository) + .flowsService(this.flowsService) + .appService(this.appService) + .pluginService(this.pluginService) + .toolService(this.toolService) + .appVersionRepository(appVersionRepository) + .appChatRepository(this.appChatRepository) + .appDefinitionService(this.appDefinitionService) + .aippLogService(this.aippLogService) + .uploadedFileManageService(this.uploadedFileManageService) + .templateFactory(this.templateFactory) + .appTaskInstanceService(this.appTaskInstanceService) + .localeService(this.localeService) + .aippModelCenter(this.aippModelCenter) + .converterFactory(this.converterFactory) + .aippFlowDefinitionService(this.aippFlowDefinitionService) + .flowDefinitionService(this.flowDefinitionService) + .maxQuestionLen(this.maxQuestionLen) + .maxUserContextLen(this.maxUserContextLen) + .knowledgeCenterService(this.knowledgeCenterService) + .resourcePath(this.resourcePath) + .build()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/Dependencies.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/Dependencies.java new file mode 100644 index 0000000000..8b4fe15bc2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/Dependencies.java @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import modelengine.fel.tool.service.ToolService; +import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.service.FlowDefinitionService; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.factory.AppTemplateFactory; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.knowledge.KnowledgeCenterService; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; + +import lombok.Builder; +import lombok.Data; + +/** + * 注入依赖项. + * + * @author 张越 + * @since 2025-01-26 + */ +@Data +@Builder +public class Dependencies { + private AppBuilderFormPropertyRepository formPropertyRepository; + private AppTaskService appTaskService; + private AppBuilderConfigRepository configRepository; + private AppBuilderFormRepository formRepository; + private AppBuilderConfigPropertyRepository configPropertyRepository; + private AppBuilderFlowGraphRepository flowGraphRepository; + private FlowsService flowsService; + private AppService appService; + private PluginService pluginService; + private ToolService toolService; + private AppVersionRepository appVersionRepository; + private AppChatRepository appChatRepository; + private AppDefinitionService appDefinitionService; + private AippLogService aippLogService; + private UploadedFileManageService uploadedFileManageService; + private AppTemplateFactory templateFactory; + private AppTaskInstanceService appTaskInstanceService; + private LocaleService localeService; + private AippModelCenter aippModelCenter; + private ConverterFactory converterFactory; + private AippFlowDefinitionService aippFlowDefinitionService; + private FlowDefinitionService flowDefinitionService; + private Integer maxQuestionLen; + private Integer maxUserContextLen; + private KnowledgeCenterService knowledgeCenterService; + private String resourcePath; +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/PublishContext.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/PublishContext.java new file mode 100644 index 0000000000..90d8f1b5d2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/PublishContext.java @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.enums.AppCategory; + +import lombok.Getter; +import lombok.Setter; +import modelengine.fitframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 发布上下文. + * + * @author 张越 + * @since 2025-01-16 + */ +@Getter +public class PublishContext { + private final AppBuilderAppDto publishData; + private final OperationContext operationContext; + private final LocalDateTime operateTime; + + @Setter + private FlowInfo flowInfo; + + public PublishContext(AppBuilderAppDto publishData, OperationContext operationContext) { + this.publishData = publishData; + this.operationContext = operationContext; + this.operateTime = LocalDateTime.now(); + } + + /** + * 获取graph数据. + * + * @return {@link Map}{@code <}{@link String}{@code ,}{@link Object}{@code >} 对象. + */ + public Map getAppearance() { + return this.publishData.getFlowGraph().getAppearance(); + } + + /** + * 是否是app. + * + * @return true/false. + */ + public boolean isApp() { + return StringUtils.equalsIgnoreCase(this.publishData.getType(), AppCategory.APP.getType()); + } + + /** + * 是否是waterFlow. + * + * @return true/false. + */ + public boolean isWaterFlow() { + return StringUtils.equalsIgnoreCase(this.publishData.getType(), AppCategory.WATER_FLOW.getType()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/ToolSchemaBuilder.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/ToolSchemaBuilder.java new file mode 100644 index 0000000000..854f4d88ae --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/ToolSchemaBuilder.java @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.enums.AppCategory; + +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * 工具schema构建类. + * + * @author 张越 + * @since 2025-01-16 + */ +public class ToolSchemaBuilder { + private final PublishContext context; + private final AppCategory appCategory; + + ToolSchemaBuilder(PublishContext context) { + this.context = context; + this.appCategory = AppCategory.findByType(this.context.getPublishData().getType()) + .orElseThrow(() -> new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID)); + } + + /** + * 创建构造器. + * + * @param context 发布上下文. + * @return {@link ToolSchemaBuilder} 构建器对象. + */ + public static ToolSchemaBuilder create(PublishContext context) { + return new ToolSchemaBuilder(context); + } + + /** + * 构建. + * + * @return {@link Map}{@code <}{@link String}{@code ,}{@link Object}{@code >} schema. + */ + public Map build() { + return MapBuilder.get() + .put("name", this.context.getPublishData().getName()) + .put("description", this.context.getPublishData().getDescription()) + .put("parameters", this.buildParameters()) + .put("order", Arrays.asList("tenantId", "aippId", "version", "inputParams")) + .put("return", this.buildReturn()) + .put("manualIntervention", Objects.equals(this.appCategory, AppCategory.WATER_FLOW)) + .build(); + } + + private Map buildParameters() { + Map parameterMap = new HashMap<>(); + parameterMap.put("type", "object"); + parameterMap.put("properties", this.buildProperties()); + parameterMap.put("required", Arrays.asList("tenantId", "aippId", "version", "inputParams")); + return parameterMap; + } + + private Map buildProperties() { + Map propertiesMap = new HashMap<>(); + propertiesMap.put("tenantId", + MapBuilder.get() + .put("type", "string") + .put("description", "the tenant id of the waterFlow tool") + .put("default", this.context.getOperationContext().getTenantId()) + .build()); + propertiesMap.put("aippId", + MapBuilder.get() + .put("type", "string") + .put("description", "the aipp id of the waterFlow tool") + .put("default", this.context.getPublishData().getId()) + .build()); + propertiesMap.put("version", + MapBuilder.get() + .put("type", "string") + .put("description", "the aipp version of the waterFlow tool") + .put("default", this.context.getPublishData().getVersion()) + .build()); + propertiesMap.put("inputParams", this.buildInputParamsSchema()); + return propertiesMap; + } + + private Map buildInputParamsSchema() { + Map propertiesMapOfInputParam = new HashMap<>(); + List required = new ArrayList<>(); + List order = new ArrayList<>(); + Optional.ofNullable(this.context.getFlowInfo()).ifPresent(f -> { + List> inputParams = f.getInputParamsByName("input"); + inputParams.forEach(ip -> { + String name = ip.getOrDefault("name", StringUtils.EMPTY).toString(); + String type = ip.getOrDefault("type", StringUtils.EMPTY).toString(); + String description = ip.getOrDefault("description", StringUtils.EMPTY).toString(); + propertiesMapOfInputParam.put(name, + MapBuilder.get().put("type", type).put("description", description).build()); + if (ObjectUtils.cast(ip.getOrDefault("isRequired", false))) { + required.add(name); + } + order.add(name); + }); + }); + return MapBuilder.get() + .put("type", "object") + .put("properties", propertiesMapOfInputParam) + .put("required", required) + .put("order", order) + .build(); + } + + private Map buildReturn() { + // 返参的具体属性信息暂不填充,需要考虑多end节点的情况 + return MapBuilder.get().put("type", "object").put("properties", new HashMap<>()).build(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FlowPublisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FlowPublisher.java new file mode 100644 index 0000000000..e8267f9118 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FlowPublisher.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; + +import lombok.AllArgsConstructor; + +/** + * 流程发布器. + * + * @author 张越 + * @since 2025-01-16 + */ +@AllArgsConstructor +public class FlowPublisher implements Publisher { + private final FlowsService flowsService; + + @Override + public void publish(PublishContext context, AppVersion appVersion) { + String configData = JsonUtils.toJsonString(context.getAppearance()); + FlowInfo createFlowInfo = this.flowsService.createFlows(configData, context.getOperationContext()); + try { + FlowInfo flowInfo = this.flowsService.publishFlows(createFlowInfo.getFlowId(), + context.getPublishData().getVersion(), configData, + context.getOperationContext()); + context.setFlowInfo(flowInfo); + } catch (JobberException e) { + AippErrCode retCode = (e.getCode() == ErrorCodes.FLOW_ALREADY_EXIST.getErrorCode()) + ? AippErrCode.FLOW_ALREADY_EXIST + : AippErrCode.APP_PUBLISH_FAILED; + throw new AippException(context.getOperationContext(), retCode); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FormProperyPublisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FormProperyPublisher.java new file mode 100644 index 0000000000..7ce86e6689 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/FormProperyPublisher.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import lombok.AllArgsConstructor; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import java.util.List; + +/** + * 配置发布器. + * + * @author 张越 + * @since 2025-01-16 + */ +@AllArgsConstructor +public class FormProperyPublisher implements Publisher { + private final AppBuilderFormPropertyRepository formPropertyRepository; + + @Override + public void publish(PublishContext context, AppVersion appVersion) { + AppBuilderSaveConfigDto saveConfigDto = AppBuilderSaveConfigDto.builder() + .graph(JsonUtils.toJsonString(context.getAppearance())) + .input(context.getPublishData().getConfigFormProperties()) + .build(); + List formProperties = saveConfigDto.getInput().stream() + .map(formPropertyDto -> AppBuilderFormProperty.builder() + .id(formPropertyDto.getId()) + .formId("null") + .name(formPropertyDto.getName()) + .dataType(formPropertyDto.getDataType()) + .defaultValue(formPropertyDto.getDefaultValue()) + .from(formPropertyDto.getFrom()) + .group(formPropertyDto.getGroup()) + .description(formPropertyDto.getDescription()) + .build()) + .toList(); + this.formPropertyRepository.updateMany(formProperties); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/GraphPublisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/GraphPublisher.java new file mode 100644 index 0000000000..fc400baaaf --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/GraphPublisher.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; + +import com.alibaba.fastjson.JSONObject; + +import lombok.AllArgsConstructor; + +/** + * graph发布器. + * + * @author 张越 + * @since 2025-01-16 + */ +@AllArgsConstructor +public class GraphPublisher implements Publisher { + private final AppBuilderFlowGraphRepository flowGraphRepository; + + @Override + public void publish(PublishContext context, AppVersion appVersion) { + // graph数据设置版本信息. + context.getAppearance().put(AippConst.ATTR_VERSION_KEY, context.getPublishData().getVersion()); + + AppBuilderFlowGraph graph = appVersion.getFlowGraph(); + graph.setUpdateAt(context.getOperateTime()); + graph.setUpdateBy(context.getOperationContext().getOperator()); + graph.setName(context.getPublishData().getFlowGraph().getName()); + graph.setAppearance(JSONObject.toJSONString(context.getAppearance())); + this.flowGraphRepository.updateOne(graph); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/Publisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/Publisher.java new file mode 100644 index 0000000000..44ba73f23c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/Publisher.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; + +/** + * 发布接口. + * + * @author 张越 + * @since 2025-01-14 + */ +public interface Publisher { + /** + * 发布. + * + * @param context 发布上下文信息. + * @param appVersion 应用版本对象. + */ + void publish(PublishContext context, AppVersion appVersion); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/StorePublisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/StorePublisher.java new file mode 100644 index 0000000000..e26a0d66d4 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/StorePublisher.java @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import modelengine.fel.tool.service.ToolService; +import modelengine.fit.jane.task.util.Entities; +import modelengine.fit.jober.WaterFlowService; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.domains.appversion.ToolSchemaBuilder; +import modelengine.fit.jober.aipp.enums.AppCategory; +import modelengine.jade.store.entity.transfer.AppData; +import modelengine.jade.store.entity.transfer.AppPublishData; +import modelengine.jade.store.entity.transfer.PluginData; +import modelengine.jade.store.entity.transfer.PluginToolData; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.support.DeployStatus; + +import lombok.AllArgsConstructor; +import modelengine.fitframework.util.MapBuilder; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 仓库工具发布器. + * + * @author 张越 + * @since 2025-01-16 + */ +@AllArgsConstructor +public class StorePublisher implements Publisher { + private final AppService appService; + private final PluginService pluginService; + private final ToolService toolService; + + @Override + public void publish(PublishContext context, AppVersion appVersion) { + AppPublishData appData = this.buildItemData(context, appVersion); + String uniqueName = this.getUniqueName(context, appData); + + // 设置uniqueName,在工具中的唯一标识. + appVersion.getData().setUniqueName(uniqueName); + } + + private String getUniqueName(PublishContext context, AppPublishData appData) { + if (context.isApp()) { + return this.appService.publishApp(appData); + } + if (!context.isWaterFlow()) { + throw new AippException(AippErrCode.ILLEGAL_AIPP_TYPE); + } + if (appData.getUniqueName() == null) { + AppData.fillAppData(appData); + this.pluginService.addPlugin(this.buildPluginData(appData)); + return appData.getUniqueName(); + } + PluginData pluginData = this.buildPluginData(appData); + return this.toolService.upgradeTool(pluginData.getPluginToolDataList().get(0)); + } + + private AppPublishData buildItemData(PublishContext context, AppVersion appVersion) { + AppCategory appCategory = AppCategory.findByType(context.getPublishData().getType()) + .orElseThrow(() -> new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID)); + AppPublishData itemData = new AppPublishData(); + itemData.setCreator(context.getOperationContext().getOperator()); + itemData.setModifier(context.getOperationContext().getOperator()); + itemData.setIcon(context.getPublishData().getIcon()); + itemData.setName(context.getPublishData().getName()); + itemData.setDescription(context.getPublishData().getDescription()); + itemData.setVersion(context.getPublishData().getVersion()); + itemData.setUniqueName(appVersion.getData().getUniqueName()); + itemData.setSchema(ToolSchemaBuilder.create(context).build()); + itemData.setSource(appCategory.getSource()); + itemData.setTags(Set.of(appCategory.getTag())); + itemData.setRunnables(this.buildRunnables(context, appVersion)); + return itemData; + } + + private Map buildRunnables(PublishContext context, AppVersion appVersion) { + Map runnablesMap = new HashMap<>(); + runnablesMap.put("FIT", MapBuilder.get() + .put("genericableId", WaterFlowService.GENERICABLE_WATER_FLOW_INVOKER) + .put("fitableId", "water.flow.invoke") + .build()); + Map app = MapBuilder.get() + .put("appId", appVersion.getData().getAppId()) + .put("aippId", appVersion.getData().getAppSuiteId()) + .put("version", context.getPublishData().getVersion()) + .put("appCategory", context.getPublishData().getAppCategory()) + .build(); + runnablesMap.put("APP", app); + return runnablesMap; + } + + private PluginData buildPluginData(AppData appData) { + PluginData pluginData = new PluginData(); + pluginData.setDeployStatus(DeployStatus.RELEASED.name()); + pluginData.setCreator(appData.getCreator()); + pluginData.setModifier(appData.getModifier()); + pluginData.setPluginName(appData.getName()); + pluginData.setExtension(new HashMap<>()); + pluginData.setPluginId(Entities.generateId() + Entities.generateId()); + PluginToolData pluginToolData = this.buildPluginToolData(appData, pluginData); + pluginData.setPluginToolDataList(Collections.singletonList(pluginToolData)); + pluginData.setDefinitionGroupDataList(List.of(AppData.toDefGroup(appData))); + pluginData.setToolGroupDataList(List.of(AppData.toToolGroup(appData))); + return pluginData; + } + + private PluginToolData buildPluginToolData(AppData appData, PluginData pluginData) { + PluginToolData pluginToolData = new PluginToolData(); + pluginToolData.setCreator(appData.getCreator()); + pluginToolData.setModifier(appData.getModifier()); + pluginToolData.setName(appData.getName()); + pluginToolData.setDescription(appData.getDescription()); + pluginToolData.setSchema(appData.getSchema()); + pluginToolData.setRunnables(appData.getRunnables()); + pluginToolData.setSource(appData.getSource()); + pluginToolData.setIcon(appData.getIcon()); + pluginToolData.setTags(appData.getTags()); + pluginToolData.setVersion(appData.getVersion()); + pluginToolData.setLikeCount(appData.getLikeCount()); + pluginToolData.setDownloadCount(appData.getDownloadCount()); + pluginToolData.setPluginId(pluginData.getPluginId()); + if (appData.getUniqueName() != null) { + pluginToolData.setUniqueName(appData.getUniqueName()); + } + return pluginToolData; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/TaskPublisher.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/TaskPublisher.java new file mode 100644 index 0000000000..c730970a30 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/publish/TaskPublisher.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.publish; + +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.ACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; + +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.util.FlowInfoUtil; + +import lombok.AllArgsConstructor; +import modelengine.fitframework.log.Logger; + +import java.time.LocalDateTime; +import java.util.concurrent.CompletableFuture; + +/** + * 任务修改发布器. + * + * @author 张越 + * @since 2025-01-16 + */ +@AllArgsConstructor +public class TaskPublisher implements Publisher { + private static final Logger log = Logger.get(TaskPublisher.class); + + private final AppTaskService appTaskService; + + @Override + public void publish(PublishContext context, AppVersion appVersion) { + FlowInfo flowInfo = context.getFlowInfo(); + OperationContext operationContext = context.getOperationContext(); + + // 清除所有的preview任务. + CompletableFuture.runAsync( + () -> this.appTaskService.getPreviewTasks(appVersion.getData().getAppSuiteId(), operationContext) + .forEach(t -> t.cleanResource(operationContext))); + + // 创建任务. + AppTask createArgs = AppTask.asCreateEntity() + .setName(context.getPublishData().getName()) + .setStatus(ACTIVE.getCode()) + .setDescription(context.getPublishData().getDescription()) + .setIcon(context.getPublishData().getIcon()) + .setPublishTime(LocalDateTime.now().toString()) + .setPublishDescription(context.getPublishData().getPublishedDescription()) + .setPublishLog(context.getPublishData().getPublishedUpdateLog()) + .setVersion(context.getPublishData().getVersion()) + .setAttributeVersion(context.getPublishData().getVersion()) + .setAppId(context.getPublishData().getId()) + .setUniqueName(appVersion.getData().getUniqueName()) + .fetch(FlowInfoUtil.buildAippNodeForms(flowInfo, appVersion.getFormProperties())) + .setAippType(NORMAL.name()) + .setFlowConfigId(flowInfo.getFlowId()) + .setFlowDefinitionId(flowInfo.getFlowDefinitionId()) + .build(); + log.debug("create aipp, task info {}", createArgs.getEntity().toString()); + this.appTaskService.createTask(createArgs, context.getOperationContext()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/AppVersionRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/AppVersionRepository.java new file mode 100644 index 0000000000..37b0e14f42 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/AppVersionRepository.java @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.repository; + +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; + +import java.util.List; +import java.util.Optional; + +/** + * app version 相关数据库操作对象 + * + * @author 张越 + * @since 2025-01-14 + */ +public interface AppVersionRepository { + /** + * 根据 app 唯一标识获取 app 对象。 + * + * @param id 表示 app 的唯一标识的 {@link String}。 + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象。 + */ + Optional selectById(String id); + + /** + * 修改一个应用版本. + * + * @param appVersion {@link AppVersion} 版本对象. + */ + void update(AppVersion appVersion); + + /** + * 根据 path 查看是否重复。 + * + * @param path 表示 app 的短链唯一标识的 {@link String}。 + * @return 表示短链是否重复 {@link Boolean}。 + */ + boolean checkPathExists(String path); + + /** + * 通过条件查询. + * + * @param cond 条件对象. + * @return {@link List}{@code <}{@link AppVersion}{@code >} 列表. + */ + List selectByCondition(AppQueryCondition cond); + + /** + * 根据名称查询相似应用名称。 + * + * @param appName 应用名称。 + * @return {@link List}{@code <}{@link String}{@code >} 相似名称列表。 + */ + List selectWithSimilarName(String appName); + + /** + * 通过path路径查询. + * + * @param path 路径. + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象. + */ + Optional selectByPath(String path); + + /** + * 通过应用的id来查询版本列表. + * + * @param appSuiteId 应用id. + * @return {@link AppVersion} 列表. + */ + List selectByAppSuiteId(String appSuiteId); + + /** + * 通过tenantId以及查询条件分页查询. + * + * @param cond 查询条件. + * @param tenantId 租户id. + * @param offset 偏移量. + * @param limit 条数限制. + * @return {@link AppVersion} 列表. + */ + List pageListByTenantId(AppQueryCondition cond, String tenantId, long offset, int limit); + + /** + * 保存应用版本。 + * + * @param appVersion 应用版本对象。 + */ + void save(AppVersion appVersion); + + /** + * 通过查询条件和tenantId计算应用数量. + * + * @param cond 查询条件. + * @param tenantId 租户id. + * @return 数量. + */ + long countByTenantId(AppQueryCondition cond, String tenantId); + + /** + * 通过id批量删除. + * + * @param appIds 版本id集合. + */ + void deleteByIds(List appIds); + + /** + * 通过appId获取appSuiteId. + * + * @param appId app版本id. + * @return appSuiteId,应用唯一id. + */ + String getAppSuiteIdByAppId(String appId); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/impl/AppVersionRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/impl/AppVersionRepositoryImpl.java new file mode 100644 index 0000000000..b92116b382 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/repository/impl/AppVersionRepositoryImpl.java @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.repository.impl; + +import modelengine.fit.jane.common.enums.DirectionEnum; +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.serializer.AppVersionSerializer; +import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; +import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.StringUtils; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 应用创建仓库实现类 + * + * @author 张越 + * @since 2025-01-14 + */ +@Component +public class AppVersionRepositoryImpl implements AppVersionRepository { + private final AppBuilderAppMapper mapper; + private final AppVersionSerializer serializer; + + public AppVersionRepositoryImpl(AppBuilderAppMapper mapper, AppVersionFactory appVersionFactory) { + this.mapper = mapper; + this.serializer = new AppVersionSerializer(appVersionFactory, this); + } + + @Override + public Optional selectById(String id) { + return Optional.ofNullable(this.mapper.selectWithId(id)).map(this.serializer::deserialize); + } + + @Override + public void update(AppVersion appVersion) { + this.mapper.updateOne(this.serializer.serialize(appVersion)); + } + + @Override + public boolean checkPathExists(String path) { + return this.mapper.checkPathExists(path); + } + + @Override + public List selectWithSimilarName(String appName) { + return this.mapper.selectWithSimilarName(appName); + } + + @Override + public void save(AppVersion appVersion) { + this.mapper.insertOne(this.serializer.serialize(appVersion)); + } + + @Override + public List selectByCondition(AppQueryCondition cond) { + // 校验,同时重新设置sort的值. + if (StringUtils.isNotBlank(cond.getSort())) { + cond.setSort(DirectionEnum.getDirection(cond.getSort()).getValue()); + } + + // 校验orderBy. + if (StringUtils.isNotBlank(cond.getOrderBy())) { + AippSortKeyEnum.getSortKey(cond.getOrderBy()); + } + return this.mapper.selectWithCondition(cond) + .stream() + .map(this.serializer::deserialize) + .collect(Collectors.toList()); + } + + @Override + public Optional selectByPath(String path) { + return Optional.of(this.mapper.selectWithPath(path)).map(this.serializer::deserialize); + } + + @Override + public List selectByAppSuiteId(String appSuiteId) { + return this.mapper.selectByAppSuiteId(appSuiteId) + .stream() + .map(this.serializer::deserialize) + .collect(Collectors.toList()); + } + + @Override + public List pageListByTenantId(AppQueryCondition cond, String tenantId, long offset, int limit) { + return this.mapper.selectByTenantIdWithPage(cond, tenantId, offset, limit) + .stream() + .map(this.serializer::deserialize) + .collect(Collectors.toList()); + } + + @Override + public long countByTenantId(AppQueryCondition cond, String tenantId) { + return this.mapper.countByTenantId(tenantId, cond); + } + + @Override + public void deleteByIds(List appIds) { + this.mapper.delete(appIds); + } + + @Override + public String getAppSuiteIdByAppId(String appId) { + return this.selectById(appId).map(av -> av.getData().getAppSuiteId()).orElse(null); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/serializer/AppVersionSerializer.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/serializer/AppVersionSerializer.java new file mode 100644 index 0000000000..769aa6b412 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/serializer/AppVersionSerializer.java @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.serializer; + +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.serializer.BaseSerializer; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import lombok.AllArgsConstructor; + +import java.util.Optional; + +/** + * 应用版本序列化与反序列化实现类 + * + * @author 张越 + * @since 2025-01-14 + */ +@AllArgsConstructor +public class AppVersionSerializer implements BaseSerializer { + private final AppVersionFactory factory; + private final AppVersionRepository appVersionRepository; + + @Override + public AppBuilderAppPo serialize(AppVersion appVersion) { + return Optional.ofNullable(appVersion) + .map(av -> { + AppBuilderAppPo data = appVersion.getData(); + data.setAttributes(JsonUtils.toJsonString(appVersion.getAttributes())); + return data; + }) + .orElseGet(() -> AppBuilderAppPo.builder().build()); + } + + @Override + public AppVersion deserialize(AppBuilderAppPo dataObject) { + return this.factory.create(dataObject, this.appVersionRepository); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/AppVersionService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/AppVersionService.java new file mode 100644 index 0000000000..99869ab29d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/AppVersionService.java @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.service; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; +import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; +import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; +import modelengine.fit.jober.common.RangedResultSet; + +import modelengine.fitframework.flowable.Choir; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 应用版本服务. + * + * @author 张越 + * @since 2025-01-14 + */ +public interface AppVersionService { + /** + * 通过appId获取 {@link AppVersion}. + * + * @param appId app多版本中的唯一标识. + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象. + */ + Optional getByAppId(String appId); + + /** + * 通过path获取 {@link AppVersion}. + * + * @param path 路径. + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象. + */ + Optional getByPath(String path); + + /** + * 强制获取一个应用,若获取不到则抛出应用版本不存在的异常. + * + * @param appId 应用版本id. + * @return {@link AppVersion} 对象 + * @throws AippException 异常. + */ + AppVersion retrieval(String appId); + + /** + * 通过appSuiteId获取所有的 {@link AppVersion} 列表. + * + * @param appSuiteId 应用的唯一id. + * @return {@link List}{@code <}{@link AppVersion}{@code >} 集合. + */ + List getByAppSuiteId(String appSuiteId); + + /** + * 运行 App 的某个版本. + * + * @param request 运行请求. + * @param context 操作人上下文信息. + * @return {@link Choir}{@code <}{@link Object}{@code >} SSE对象. + */ + Choir run(CreateAppChatRequest request, OperationContext context); + + /** + * 调试 App 的某个版本. + * + * @param request 运行请求. + * @param context 操作人上下文信息. + * @return {@link Choir}{@code <}{@link Object}{@code >} SSE对象. + */ + Choir debug(CreateAppChatRequest request, OperationContext context); + + /** + * 重新启动任务实例. + * + * @param instanceId 任务实例id. + * @param params 重启参数. + * @param context 操作人上下文. + * @return {@link Choir}{@code <}{@link Object}{@code >} SSE对象. + */ + Choir restart(String instanceId, Map params, OperationContext context); + + /** + * 创建一个 {@link AppVersion} 对象. + * + * @param templateId 模板id. + * @param dto 创建参数. + * @param context 操作人上下文信息. + * @return {@link AppVersion} 对象. + */ + AppVersion create(String templateId, AppBuilderAppCreateDto dto, OperationContext context); + + /** + * 通过模板对象创建app. + * + * @param template 模板对象. + * @param context 操作人上下文. + * @return {@link AppVersion} 应用版本. + */ + AppVersion createByTemplate(AppTemplate template, OperationContext context); + + /** + * 升级并创建一个新版本. + * + * @param appId 待升级的appid. + * @param dto 升级参数. + * @param context 操作人上下文信息. + * @return {@link AppVersion} 对象. + */ + AppVersion upgrade(String appId, AppBuilderAppCreateDto dto, OperationContext context); + + /** + * 校验app名称是否符合规范. + * + * @param name 名称. + * @param context 操作人上下文信息. + * @throws AippException 业务异常. + */ + void validateAppName(String name, OperationContext context) throws AippException; + + /** + * 通过应用id获取最新创建的应用版本. + * + * @param appSuiteId 应用id. + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象. + */ + Optional getLatestCreatedByAppSuiteId(String appSuiteId); + + /** + * 通过应用id获取最先创建的应用版本. + * + * @param appSuiteId 应用id. + * @return {@link Optional}{@code <}{@link AppVersion}{@code >} 对象. + */ + Optional getFirstCreatedByAppSuiteId(String appSuiteId); + + /** + * 通过tenantId以及查询条件分页查询. + * + * @param cond 查询条件. + * @param tenantId 租户id. + * @param offset 偏移量. + * @param limit 条数限制. + * @return {@link AppVersion} 分页集合. + */ + RangedResultSet pageListByTenantId(AppQueryCondition cond, String tenantId, long offset, int limit); + + /** + * 根据条件以及tenantId统计app数量. + * + * @param cond 查询条件. + * @param tenantId 租户id. + * @return 数量. + */ + long countByTenantId(AppQueryCondition cond, String tenantId); + + /** + * 根据传入的 {@link AppBuilderAppDto} 数据进行修改. + * + * @param appId 版本id. + * @param appDto 待修改数据. + * @param context 操作人上下文信息. + * @return {@link AppVersion} 对象. + */ + AppVersion update(String appId, AppBuilderAppDto appDto, OperationContext context); + + /** + * 根据传入的 {@link AppBuilderFlowGraphDto} 数据进行修改. + * + * @param appId 版本id. + * @param graphDto 待修改数据. + * @param context 操作人上下文信息. + * @return {@link AppVersion} 对象. + */ + AppVersion update(String appId, AppBuilderFlowGraphDto graphDto, OperationContext context); + + /** + * 根据传入的 {@link AppBuilderSaveConfigDto} 数据进行修改. + * + * @param appId 版本id. + * @param appBuilderSaveConfigDto 待修改数据. + * @param context 操作人上下文信息. + * @return {@link AppVersion} 对象. + */ + AppVersion update(String appId, AppBuilderSaveConfigDto appBuilderSaveConfigDto, OperationContext context); + + /** + * 根据传入的 {@link AppVersion} 进行修改. + * + * @param appVersion {@link AppVersion} 对象. + */ + void update(AppVersion appVersion); + + /** + * 通过id批量删除. + * + * @param appIds 版本id集合. + */ + void deleteByIds(List appIds); + + /** + * 判断应用名称是否已经存在. + * + * @param appName 应用名称. + * @param context 操作人上下文. + * @return true/false. + */ + boolean isNameExists(String appName, OperationContext context); + + /** + * 保存. + * + * @param appVersion {@link AppVersion} 对象. + */ + void save(AppVersion appVersion); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/impl/AppVersionServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/impl/AppVersionServiceImpl.java new file mode 100644 index 0000000000..692c3e9678 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/service/impl/AppVersionServiceImpl.java @@ -0,0 +1,439 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion.service.impl; + +import static modelengine.fit.jober.aipp.service.impl.UploadedFileMangeServiceImpl.IRREMOVABLE; +import static modelengine.fit.jober.aipp.service.impl.UploadedFileMangeServiceImpl.REMOVABLE; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.common.enums.DirectionEnum; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionDecorator; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; +import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; +import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.enums.AppTypeEnum; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.util.AippFileUtils; +import modelengine.fit.jober.aipp.util.TemplateUtils; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.jade.common.locale.LocaleUtil; + +import com.alibaba.fastjson.JSONObject; + +import io.opentelemetry.api.trace.Span; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.fitframework.flowable.Choir; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.transaction.Transactional; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +/** + * {@link AppVersionService} 服务. + * + * @author 张越 + * @since 2025-01-14 + */ +@Component +public class AppVersionServiceImpl implements AppVersionService { + private static final Logger LOGGER = Logger.get(AppVersionServiceImpl.class); + private static final String APP_NAME_FORMAT = "^[\\u4E00-\\u9FA5A-Za-z0-9][\\u4E00-\\u9FA5A-Za-z0-9-_]*$"; + private static final String DEFAULT_APP_VERSION = "1.0.0"; + + private final AppVersionRepository repository; + private final AppChatRepository appChatRepository; + private final AppTaskInstanceService appTaskInstanceService; + private final UploadedFileManageService uploadedFileManageService; + private final AppBuilderConfigRepository configRepository; + private final AppBuilderFlowGraphRepository flowGraphRepository; + private final AppBuilderFormPropertyRepository formPropertyRepository; + private final AppBuilderConfigPropertyRepository configPropertyRepository; + private final AppTaskService appTaskService; + private final AppVersionFactory appVersionFactory; + private final int nameLengthMaximum; + + public AppVersionServiceImpl(AppVersionRepository repository, AppChatRepository appChatRepository, + AppTaskInstanceService appTaskInstanceService, UploadedFileManageService uploadedFileManageService, + AppBuilderConfigRepository configRepository, AppBuilderFlowGraphRepository flowGraphRepository, + AppBuilderFormPropertyRepository formPropertyRepository, + AppBuilderConfigPropertyRepository configPropertyRepository, AppTaskService appTaskService, + AppVersionFactory appVersionFactory, + @Value("${validation.task.name.length.maximum:64}") int nameLengthMaximum) { + this.repository = repository; + this.appChatRepository = appChatRepository; + this.appTaskInstanceService = appTaskInstanceService; + this.uploadedFileManageService = uploadedFileManageService; + this.configRepository = configRepository; + this.flowGraphRepository = flowGraphRepository; + this.formPropertyRepository = formPropertyRepository; + this.configPropertyRepository = configPropertyRepository; + this.appTaskService = appTaskService; + this.appVersionFactory = appVersionFactory; + this.nameLengthMaximum = nameLengthMaximum; + } + + @Override + public Optional getByAppId(String appId) { + return this.repository.selectById(appId); + } + + @Override + public Optional getByPath(String path) { + return this.repository.selectByPath(path); + } + + @Override + public AppVersion retrieval(String appId) { + return this.getByAppId(appId) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("app version[{0}] not exists.", appId))); + } + + @Override + public List getByAppSuiteId(String appSuiteId) { + return this.repository.selectByAppSuiteId(appSuiteId); + } + + @Override + @Transactional + public Choir run(CreateAppChatRequest request, OperationContext context) { + AppVersion appVersion = this.retrieval(request.getAppId()); + RunContext runContext = RunContext.from(request, context); + appVersion.validate(runContext, false); + Locale locale = LocaleUtil.getLocale(); + return Choir.create(emitter -> { + ChatSession session = new ChatSession<>(emitter, request.getAppId(), false, locale); + AppVersionDecorator.decorate(appVersion, this.appChatRepository).run(runContext, session); + }); + } + + @Override + @Transactional + public Choir debug(CreateAppChatRequest request, OperationContext context) { + AppVersion appVersion = this.retrieval(request.getAppId()); + appVersion.updateFlows(context); + RunContext runContext = RunContext.from(request, context); + appVersion.validate(runContext, true); + Locale locale = LocaleUtil.getLocale(); + return Choir.create(emitter -> { + ChatSession session = new ChatSession<>(emitter, request.getAppId(), true, locale); + AppVersionDecorator.decorate(appVersion, this.appChatRepository).debug(runContext, session); + }); + } + + @Override + @Transactional + public Choir restart(String instanceId, Map restartParams, OperationContext context) { + String taskId = this.appTaskInstanceService.getTaskId(instanceId); + AppTask task = this.appTaskService.getTaskById(taskId, context) + .orElseThrow(() -> new AippException(AippErrCode.TASK_NOT_FOUND, taskId)); + + // 这边instance的获取暂时没有放在 Choir.create 里:Choir 会把异常吞掉 + AppTaskInstance instance = this.appTaskInstanceService.getInstanceById(instanceId, context) + .orElseThrow(() -> new AippException(AippErrCode.CHAT_NOT_FOUND_BY_INSTANCE_ID, instanceId)); + String parentInstanceId = instance.getParentInstanceId(); + List chatList = instance.getChats(); + if (CollectionUtils.isEmpty(chatList)) { + LOGGER.error("chatList is empty."); + throw new AippParamException(AippErrCode.RE_CHAT_FAILED, parentInstanceId); + } + List instanceLogs = instance.getLogs(); + if (CollectionUtils.isEmpty(instanceLogs)) { + throw new AippParamException(AippErrCode.AIPP_INSTANCE_LOG_IS_NULL, parentInstanceId); + } + String appId = task.getEntity().getAppId(); + Locale locale = LocaleUtil.getLocale(); + return Choir.create(emitter -> { + ChatSession session = new ChatSession<>(emitter, appId, false, locale); + AppVersion appVersion = this.retrieval(appId); + AppVersionDecorator.decorate(appVersion, this.appChatRepository) + .restart(instance, restartParams, session, context); + }); + } + + @Override + @Transactional + public AppVersion create(String templateId, AppBuilderAppCreateDto dto, OperationContext context) { + this.validateAppName(dto.getName(), context); + if (dto.getDescription() != null) { + this.validateAppDescription(dto, context); + } + this.validateAppCategory(dto, context); + if (this.isNameExists(dto.getName(), context)) { + LOGGER.error("Create aipp failed, [name={}, tenantId={}]", dto.getName(), context.getTenantId()); + throw new AippException(context, AippErrCode.AIPP_NAME_IS_DUPLICATE); + } + AppVersion template = this.retrieval(templateId); + template.create(); + template.cloneVersion(dto, DEFAULT_APP_VERSION, AppTypeEnum.APP.name(), context); + this.save(template); + return template; + } + + private void validateAppDescription(AppBuilderAppCreateDto dto, OperationContext context) { + if (dto.getDescription().length() > 300) { + LOGGER.error("Create aipp failed, [name={}, tenantId={}], app description is larger than 300.", + dto.getName(), context.getTenantId()); + throw new AippException(context, AippErrCode.APP_DESCRIPTION_LENGTH_OUT_OF_BOUNDS); + } + } + + private void validateAppCategory(AppBuilderAppCreateDto dto, OperationContext context) { + if (dto.getAppCategory() == null) { + LOGGER.error("Create aipp failed, [name={}, tenantId={}], app category is null.", + dto.getName(), context.getTenantId()); + throw new AippException(context, AippErrCode.APP_CATEGORY_IS_NULL); + } + } + + @Override + public AppVersion createByTemplate(AppTemplate template, OperationContext context) { + this.validateAppName(template.getName(), context); + if (this.isNameExists(template.getName(), context)) { + LOGGER.error("Create aipp by template failed, [name={}, tenantId={}]", template.getName(), + context.getTenantId()); + throw new AippException(context, AippErrCode.AIPP_NAME_IS_DUPLICATE); + } + AppVersion appVersion = this.appVersionFactory.create(new AppBuilderAppPo(), this.repository); + appVersion.getData().setConfigId(template.getConfigId()); + appVersion.getData().setFlowGraphId(template.getFlowGraphId()); + appVersion.cloneVersion(TemplateUtils.toAppCreateDTO(template), DEFAULT_APP_VERSION, AppTypeEnum.APP.name(), + context); + this.save(appVersion); + return appVersion; + } + + @Override + @Transactional + public AppVersion upgrade(String appId, AppBuilderAppCreateDto dto, OperationContext context) { + AppVersion template = this.retrieval(appId); + template.upgrade(dto, AppTypeEnum.APP.name(), context); + this.save(template); + return template; + } + + @Override + public void validateAppName(String name, OperationContext context) throws AippException { + String trimName = StringUtils.trim(name); + if (StringUtils.isEmpty(trimName)) { + LOGGER.error("Create aipp failed: name can not be empty."); + throw new AippParamException(context, AippErrCode.APP_NAME_IS_INVALID); + } + if (!name.matches(APP_NAME_FORMAT)) { + LOGGER.error("Create aipp failed: the name format is incorrect. [name={}]", name); + throw new AippParamException(context, AippErrCode.APP_NAME_IS_INVALID); + } + if (name.length() > this.nameLengthMaximum) { + LOGGER.error("Create aipp failed: the length of task name is out of bounds. [name={}]", name); + throw new AippParamException(context, AippErrCode.AIPP_NAME_LENGTH_OUT_OF_BOUNDS); + } + } + + @Override + public Optional getLatestCreatedByAppSuiteId(String appSuiteId) { + List appVersionList = this.repository.selectByCondition(AppQueryCondition.builder() + .appSuiteId(appSuiteId) + .orderBy(AippSortKeyEnum.CREATE_AT.name()) + .sort(DirectionEnum.DESCEND.name()) + .offset(0L) + .limit(1) + .build()); + return appVersionList.stream().findFirst(); + } + + @Override + public Optional getFirstCreatedByAppSuiteId(String appSuiteId) { + List appVersionList = this.repository.selectByCondition(AppQueryCondition.builder() + .appSuiteId(appSuiteId) + .orderBy(AippSortKeyEnum.CREATE_AT.name()) + .sort(DirectionEnum.ASCEND.name()) + .offset(0L) + .limit(1) + .build()); + return appVersionList.stream().findFirst(); + } + + @Override + public RangedResultSet pageListByTenantId(AppQueryCondition cond, String tenantId, long offset, + int limit) { + List versions = this.repository.pageListByTenantId(cond, tenantId, offset, limit); + long total = this.repository.countByTenantId(cond, tenantId); + return RangedResultSet.create(versions, offset, limit, total); + } + + @Override + public long countByTenantId(AppQueryCondition cond, String tenantId) { + return this.repository.countByTenantId(cond, tenantId); + } + + @Override + public AppVersion update(String appId, AppBuilderAppDto appDto, OperationContext context) { + AppVersion appVersion = this.retrieval(appId); + if (appVersion.isPublished()) { + throw new AippException(AippErrCode.APP_HAS_ALREADY); + } + List tasks = appVersion.getPublishedTasks(context); + if (CollectionUtils.isNotEmpty(tasks) && !StringUtils.equals(tasks.get(0).getEntity().getName(), + appDto.getName())) { + throw new AippException(AippErrCode.APP_NAME_HAS_PUBLISHED); + } + this.validateAppName(appDto.getName(), context); + appVersion.getData().setName(appDto.getName()); + appVersion.getData().setUpdateBy(context.getOperator()); + appVersion.getData().setUpdateAt(LocalDateTime.now()); + appVersion.getData().setType(appDto.getType()); + appVersion.getData().setAppType(appDto.getAppType()); + + // 避免前端更新将app表的attributes覆盖了 + String oldIcon = appVersion.getIcon(); + appVersion.putAttributes(appDto.getAttributes()); + + // 更新状态. + if (StringUtils.equals(appVersion.getData().getState(), AppState.IMPORTING.getName()) + && StringUtils.equals(appDto.getState(), AppState.INACTIVE.getName())) { + appVersion.getData().setState(AppState.INACTIVE.getName()); + } + + appVersion.getData().setVersion(appDto.getVersion()); + this.repository.update(appVersion); + + String newIcon = appVersion.getIcon(); + if (StringUtils.isNotBlank(newIcon) && !StringUtils.equals(oldIcon, newIcon)) { + this.uploadedFileManageService.changeRemovable(AippFileUtils.getFileNameFromIcon(oldIcon), REMOVABLE); + this.uploadedFileManageService.changeRemovable(AippFileUtils.getFileNameFromIcon(newIcon), IRREMOVABLE); + } + return appVersion; + } + + @Override + public AppVersion update(String appId, AppBuilderFlowGraphDto graphDto, OperationContext context) { + AppVersion appVersion = this.retrieval(appId); + if (appVersion.isPublished()) { + throw new AippException(AippErrCode.APP_HAS_ALREADY); + } + Span.current().setAttribute("name", appVersion.getData().getName()); + LocalDateTime operateTime = LocalDateTime.now(); + appVersion.getFlowGraph().setUpdateAt(operateTime); + appVersion.getFlowGraph().setUpdateBy(context.getOperator()); + appVersion.getFlowGraph().setName(graphDto.getName()); + appVersion.getFlowGraph().setAppearance(JSONObject.toJSONString(graphDto.getAppearance())); + this.flowGraphRepository.updateOne(appVersion.getFlowGraph()); + + appVersion.getConfig().updateByAppearance(appVersion.getFlowGraph().getAppearance()); + appVersion.getConfig().setUpdateAt(operateTime); + appVersion.getConfig().setUpdateBy(context.getOperator()); + this.configRepository.updateOne(appVersion.getConfig()); + + appVersion.getData().setUpdateAt(operateTime); + appVersion.getData().setUpdateBy(context.getOperator()); + appVersion.putAttributes(new HashMap<>()); + this.repository.update(appVersion); + return appVersion; + } + + @Override + @Transactional + public AppVersion update(String appId, AppBuilderSaveConfigDto appBuilderSaveConfigDto, OperationContext context) { + AppVersion appVersion = this.retrieval(appId); + List formProperties = appBuilderSaveConfigDto.getInput().stream() + .map(formPropertyDto -> AppBuilderFormProperty.builder() + .id(formPropertyDto.getId()) + .formId("null") + .name(formPropertyDto.getName()) + .dataType(formPropertyDto.getDataType()) + .defaultValue(formPropertyDto.getDefaultValue()) + .from(formPropertyDto.getFrom()) + .group(formPropertyDto.getGroup()) + .description(formPropertyDto.getDescription()) + .build()) + .toList(); + appVersion.putAttributes(new HashMap<>()); + this.repository.update(appVersion); + this.formPropertyRepository.updateMany(formProperties); + appVersion.getFlowGraph().setAppearance(appBuilderSaveConfigDto.getGraph()); + this.flowGraphRepository.updateOne(appVersion.getFlowGraph()); + return appVersion; + } + + @Override + public void update(AppVersion appVersion) { + Optional.ofNullable(appVersion).ifPresent(this.repository::update); + } + + @Override + public void deleteByIds(List appIds) { + if (CollectionUtils.isEmpty(appIds)) { + return; + } + this.repository.deleteByIds(appIds); + } + + @Override + public boolean isNameExists(String appName, OperationContext context) { + AppQueryCondition queryCondition = AppQueryCondition.builder() + .tenantId(context.getTenantId()) + .name(appName) + .build(); + return !this.repository.selectByCondition(queryCondition).isEmpty(); + } + + @Override + public void save(AppVersion appVersion) { + if (appVersion == null) { + return; + } + this.repository.save(appVersion); + String icon = appVersion.getIcon(); + if (StringUtils.isNotBlank(icon)) { + this.uploadedFileManageService.updateRecord(appVersion.getData().getId(), + AippFileUtils.getFileNameFromIcon(icon), + IRREMOVABLE); + } + this.flowGraphRepository.insertOne(appVersion.getFlowGraph()); + this.configRepository.insertOne(appVersion.getConfig()); + this.configPropertyRepository.insertMore(appVersion.getConfig().getConfigProperties()); + List formProperties = appVersion.getFormProperties(); + formProperties.forEach(property -> property.setAppId(appVersion.getData().getId())); + this.formPropertyRepository.insertMore(formProperties); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryConfig.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryConfig.java new file mode 100644 index 0000000000..4872c0c181 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryConfig.java @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.business; + +import modelengine.fit.jober.aipp.constants.AippConst; + +import lombok.Getter; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 历史配置类. + * + * @author 张越 + * @since 2025-01-09 + */ +@Getter +public class MemoryConfig { + private static final String TYPE_KEY = "type"; + private static final String NAME_KEY = "name"; + private static final String VALUE_KEY = "value"; + + private List> memoryConfigs; + private String memoryType; + private Boolean enableMemory; + private Object value; + + /** + * MemoryConfig 的构造方法 + * + * @param memoryConfigs 用于初始化历史配置类的配置集合的 {@link List}{@code <}{@link Map}{@code <}{@link String}{@code , + * }{@link Object}{@code >>}。 + */ + public MemoryConfig(List> memoryConfigs) { + if (CollectionUtils.isEmpty(memoryConfigs)) { + return; + } + this.memoryConfigs = memoryConfigs; + this.memoryConfigs.forEach(this::setMemoryConfig); + } + + private void setMemoryConfig(Map config) { + if (this.isValue(config)) { + this.value = config.get(VALUE_KEY); + return; + } + if (this.isMemorySwitch(config)) { + this.enableMemory = ObjectUtils.cast(config.get(VALUE_KEY)); + return; + } + if (this.isType(config)) { + this.memoryType = ObjectUtils.cast(config.get(VALUE_KEY)); + } + } + + /** + * 是否启用memory. + * + * @return true/false. + */ + public boolean getEnableMemory() { + return Optional.ofNullable(this.enableMemory).orElse(false); + } + + private boolean isValue(Map config) { + return StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), VALUE_KEY); + } + + private boolean isMemorySwitch(Map config) { + return StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), AippConst.MEMORY_SWITCH_KEY); + } + + private boolean isType(Map config) { + return StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), TYPE_KEY); + } + + /** + * 配置是否为空. + * + * @return true/false. + */ + public boolean isEmpty() { + return CollectionUtils.isEmpty(this.memoryConfigs); + } + + /** + * 获取memory类型. + * + * @return {@link String} memory类型. + */ + public String getMemoryType() { + return Optional.ofNullable(this.memoryType).orElse(StringUtils.EMPTY); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryGetter.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryGetter.java new file mode 100644 index 0000000000..92c22bef71 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryGetter.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.business; + +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotNull; + +import lombok.Getter; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +/** + * 历史获取类. + * + * @author 张越 + * @since 2025-01-09 + */ +@Getter +public class MemoryGetter { + private static final String DEFAULT_VALUE = "5"; + + private final MemoryConfig memoryConfig; + private final Map>>> memoryGenerators; + private final MemoryTypeEnum defaultType; + + public MemoryGetter(MemoryConfig memoryConfig) { + this.memoryConfig = memoryConfig; + this.memoryGenerators = new HashMap<>(); + this.defaultType = MemoryTypeEnum.BY_CONVERSATION_TURN; + } + + /** + * 注册不同类型下的数据获取器. + * + * @param memoryTypeEnum memory类型. + * @param generator 数据生成器. + */ + public void register(MemoryTypeEnum memoryTypeEnum, Function>> generator) { + doIfNotNull(memoryTypeEnum, t -> doIfNotNull(generator, g -> this.memoryGenerators.put(t, g))); + } + + /** + * 获取memory数据. + * + * @return {@link List}{@code <}{@link String}{@code ,}{@link Object}{@code >}{@code >} 数据对象. + */ + public List> get() { + Optional typeOp = MemoryTypeEnum.getType(this.memoryConfig.getMemoryType()); + if (this.memoryConfig.isEmpty() || typeOp.isEmpty()) { + return this.memoryGenerators.get(this.defaultType).apply(DEFAULT_VALUE); + } + MemoryTypeEnum type = typeOp.get(); + return this.memoryGenerators.get(type).apply(this.memoryConfig.getValue()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryTypeEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryTypeEnum.java new file mode 100644 index 0000000000..bdeadb948a --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/MemoryTypeEnum.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.business; + +import modelengine.fitframework.util.StringUtils; + +import java.util.Arrays; +import java.util.Optional; + +/** + * memory类型 + * + * @author 张越 + * @since 2025/01/09 + */ +public enum MemoryTypeEnum { + /** + * 通过对话轮数. + */ + BY_CONVERSATION_TURN("ByConversationTurn"), + + /** + * 自定义. + */ + CUSTOMIZING("Customizing"), + + /** + * 不使用memory. + */ + NOT_USE_MEMORY("NotUseMemory"), + + /** + * 用户选择. + */ + USER_SELECT("UserSelect"); + + private final String type; + + MemoryTypeEnum(String type) { + this.type = type; + } + + /** + * 将字符串类型转换为枚举. + * + * @param type 字符串类型. + * @return {@link Optional}{@code <}{@link MemoryTypeEnum}{@code >} 对象. + */ + public static Optional getType(String type) { + return Arrays.stream(values()) + .filter(item -> StringUtils.equalsIgnoreCase(type, item.type)) + .findFirst(); + } + + /** + * 枚举的类型描述. + * + * @return {@link String} 类型描述. + */ + public String type() { + return this.type; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/RunContext.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/RunContext.java new file mode 100644 index 0000000000..f9b9887158 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/business/RunContext.java @@ -0,0 +1,613 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.business; + +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_AIPP_TYPE_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_FILE_DESC_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_ID_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_INST_ID_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_MEMORIES_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_QUESTION_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_USE_MEMORY_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_VERSION_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_CHAT_ID; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_HTTP_CONTEXT_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_META_VERSION_ID_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.CONTEXT_USER_ID; +import static modelengine.fit.jober.aipp.constants.AippConst.PARENT_CALLBACK_ID; +import static modelengine.fit.jober.aipp.constants.AippConst.PARENT_INSTANCE_ID; +import static modelengine.fit.jober.aipp.constants.AippConst.RESTART_MODE; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; +import modelengine.fit.jober.aipp.enums.RestartModeEnum; + +import com.alibaba.fastjson.JSONObject; + +import lombok.Getter; +import lombok.Setter; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 运行时的上下文. + * + * @author 张越 + * @since 2025-01-08 + */ +public class RunContext { + private static final String START_NODE_INPUT_PARAMS = "startNodeInputParams"; + + @Getter + private final Map businessData; + + @Getter + private final OperationContext operationContext; + + @Setter + @Getter + private boolean isDebug; + + private MemoryConfig memoryConfig; + + @Setter + @Getter + private Map userContext; + + @Setter + @Getter + private AppTask appTask; + + public RunContext(Map businessData, OperationContext context) { + this.businessData = businessData; + this.operationContext = context; + } + + /** + * 通过 {@link CreateAppChatRequest} 和 {@link OperationContext} 创建 {@link RunContext}. + * + * @param request 请求参数. + * @param context 操作人上线文信息. + * @return {@link RunContext} 对象. + */ + public static RunContext from(CreateAppChatRequest request, OperationContext context) { + CreateAppChatRequest.Context requestContext = Optional.ofNullable(request.getContext()) + .orElseGet(() -> CreateAppChatRequest.Context.builder().build()); + RunContext runContext = new RunContext(new HashMap<>(), context); + runContext.putAllToBusiness(requestContext.getUserContext()); + runContext.setUseMemory(requestContext.getUseMemory()); + runContext.setDimension(requestContext.getDimension()); + runContext.setChatId(request.getChatId()); + runContext.setQuestion(request.getQuestion()); + runContext.setAtChatId(requestContext.getAtChatId()); + runContext.setAtAppId(requestContext.getAtAppId()); + runContext.setUserContext(requestContext.getUserContext()); + runContext.setDimensionId(requestContext.getDimensionId()); + return runContext; + } + + /** + * 业务数据深拷贝. + * + * @return {@link RunContext} 运行时上下文信息. + */ + public RunContext businessDeepClone() { + return new RunContext( + this.businessData.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), + this.getOperationContext()); + } + + /** + * 将整个keyValue集合放入businessData中. + * + * @param keyValues 键值对集合. + */ + public void putAllToBusiness(Map keyValues) { + Optional.ofNullable(keyValues).ifPresent(this.businessData::putAll); + } + + /** + * 设置memory配置数据. + * + * @param memoryConfigs memory配置数据. + */ + public void setMemoryConfig(List> memoryConfigs) { + this.memoryConfig = new MemoryConfig(memoryConfigs); + } + + /** + * 获取memory配置. + * + * @return {@link MemoryConfig} 对象. + */ + public MemoryConfig getMemoryConfig() { + return Optional.ofNullable(this.memoryConfig).orElseGet(() -> new MemoryConfig(new ArrayList<>())); + } + + /** + * 是否开启memory. + * + * @return true/false. + */ + public boolean shouldUseMemory() { + Boolean enableMemory = this.memoryConfig.getEnableMemory(); + Boolean shouldUseMemory = Optional.ofNullable(this.isUseMemory()).orElse(enableMemory); + this.setUseMemory(shouldUseMemory); + return shouldUseMemory; + } + + /** + * 是否是用户自定义历史记录.此时,需要用户在前端手动选择需要的历史记录,然后再启动流程. + * + * @return true/false. + */ + public boolean isUserCustomMemory() { + return StringUtils.equalsIgnoreCase(MemoryTypeEnum.USER_SELECT.type(), this.memoryConfig.getMemoryType()); + } + + /** + * 初始化启动参数. + */ + public void initStartParams() { + this.businessData.put(START_NODE_INPUT_PARAMS, JSONObject.parse(JSONObject.toJSONString(this.businessData))); + } + + /** + * 设置appSuiteId. + * + * @param appSuiteId app唯一标识. + */ + public void setAppSuiteId(String appSuiteId) { + this.businessData.put(BS_AIPP_ID_KEY, appSuiteId); + } + + /** + * 获取应用id. + * + * @return 应用id. + */ + public String getAppSuiteId() { + return ObjectUtils.cast(this.businessData.get(AippConst.ATTR_AIPP_TYPE_KEY)); + } + + /** + * 设置appVersion. + * + * @param appVersion app版本号. + */ + public void setAppVersion(String appVersion) { + this.businessData.put(BS_AIPP_VERSION_KEY, appVersion); + } + + /** + * 获取应用版本. + * + * @return 应用版本. + */ + public String getAppVersion() { + return ObjectUtils.cast(this.businessData.get(BS_AIPP_VERSION_KEY)); + } + + /** + * 设置任务id. + * + * @param taskId 任务id. + */ + public void setTaskId(String taskId) { + this.businessData.put(BS_META_VERSION_ID_KEY, taskId); + } + + /** + * 获取任务id. + * + * @return 任务id. + */ + public String getTaskId() { + return ObjectUtils.cast(this.businessData.get(BS_META_VERSION_ID_KEY)); + } + + /** + * 设置aipp类型. + * + * @param aippType aipp类型. + */ + public void setAippType(String aippType) { + this.businessData.put(ATTR_AIPP_TYPE_KEY, aippType); + } + + /** + * 获取应用aipp类型. + * + * @return aipp类型. + */ + public String getAippType() { + return ObjectUtils.cast(this.businessData.get(AippConst.ATTR_AIPP_TYPE_KEY)); + } + + /** + * 设置任务实例id. + * + * @param taskInstanceId 任务实例id. + */ + public void setTaskInstanceId(String taskInstanceId) { + this.businessData.put(BS_AIPP_INST_ID_KEY, taskInstanceId); + } + + /** + * 设置文件urls. + * + * @param fileUrls 文件urls. + */ + public void setFileUrls(List fileUrls) { + this.businessData.put(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY, fileUrls); + this.businessData.put(AippConst.BS_AIPP_FILE_DOWNLOAD_KEY, fileUrls.get(0)); + } + + /** + * 设置开始时间. + * + * @param startTime 开始时间. + */ + public void setStartTime(Object startTime) { + this.businessData.put(AippConst.INSTANCE_START_TIME, startTime); + } + + /** + * 设置restart模式. + * + * @param restartMode restart模式. + */ + public void setRestartMode(String restartMode) { + this.businessData.put(AippConst.RESTART_MODE, restartMode); + } + + /** + * 是否是覆盖写模式. + * + * @return true/false. + */ + public boolean isOverWriteMode() { + return StringUtils.equals(RestartModeEnum.OVERWRITE.getMode(), this.getRestartMode()); + } + + /** + * 设置问题. + * + * @param question 问题. + */ + public void setQuestion(String question) { + this.businessData.put(AippConst.BS_AIPP_QUESTION_KEY, question); + } + + /** + * 获取restart模式. + * + * @return {@link String} 对象 + */ + public String getRestartMode() { + return ObjectUtils.cast(this.businessData.get(AippConst.RESTART_MODE)); + } + + /** + * 获取任务实例id. + * + * @return {@link String} 对象 + */ + public String getTaskInstanceId() { + return ObjectUtils.cast(this.businessData.get(BS_AIPP_INST_ID_KEY)); + } + + /** + * 设置app版本id. + * + * @param appId app版本id. + */ + public void setAppId(String appId) { + this.businessData.put(AippConst.CONTEXT_APP_ID, appId); + } + + /** + * 获取appId. + * + * @return appId. + */ + public String getAppId() { + return ObjectUtils.cast(this.businessData.get(AippConst.CONTEXT_APP_ID)); + } + + /** + * 设置http请求上下文. + * + * @param httpContext http请求上下文. + */ + public void setHttpContext(String httpContext) { + this.businessData.put(BS_HTTP_CONTEXT_KEY, httpContext); + } + + /** + * 设置父流程id. + * + * @param parentInstanceId 父流程id. + */ + public void setParentInstanceId(String parentInstanceId) { + this.businessData.put(PARENT_INSTANCE_ID, parentInstanceId); + } + + /** + * 获取父任务实例id. + * + * @return 父任务实例id. + */ + public String getParentInstanceId() { + return ObjectUtils.cast(this.businessData.get(PARENT_INSTANCE_ID)); + } + + /** + * 设置回调id. + * + * @param callbackId 回调id. + */ + public void setCallbackId(String callbackId) { + this.businessData.put(PARENT_CALLBACK_ID, callbackId); + } + + /** + * 设置用户id. + * + * @param userId 用户id. + */ + public void setUserId(String userId) { + this.businessData.put(CONTEXT_USER_ID, userId); + } + + /** + * 清除 memories. + */ + public void clearMemories() { + this.setMemories(new ArrayList<>()); + } + + /** + * 设置memories. + * + * @param memories 记忆集. + */ + public void setMemories(List> memories) { + this.businessData.put(BS_AIPP_MEMORIES_KEY, memories); + } + + /** + * 获取会话的id. + * + * @return 会话id. + */ + public String getChatId() { + return ObjectUtils.cast(this.businessData.get(BS_CHAT_ID)); + } + + /** + * 设置会话id. + * + * @param chatId 会话id. + */ + public void setChatId(String chatId) { + this.businessData.put(BS_CHAT_ID, chatId); + } + + /** + * 获取被艾特的会话id. + * + * @return {@link String} 对象 + */ + public String getAtChatId() { + return ObjectUtils.cast(this.businessData.get(AippConst.BS_AT_CHAT_ID)); + } + + /** + * 设置被艾特的会话id. + * + * @param atChatId 被at的会话id + */ + public void setAtChatId(String atChatId) { + this.businessData.put(AippConst.BS_AT_CHAT_ID, atChatId); + } + + /** + * 设置被艾特的应用版本id. + * + * @param atAppId 被at的应用版本id + */ + public void setAtAppId(String atAppId) { + this.businessData.put(AippConst.BS_AT_APP_ID, atAppId); + } + + /** + * 获取被at的应用版本id. + * + * @return 应用版本id. + */ + public String getAtAppId() { + return ObjectUtils.cast(this.businessData.get(AippConst.BS_AT_APP_ID)); + } + + /** + * 是否使用memory. + * + * @return {@link Boolean} 对象 + */ + public Boolean isUseMemory() { + return ObjectUtils.cast(this.businessData.get(BS_AIPP_USE_MEMORY_KEY)); + } + + /** + * 设置是否使用memory. + * + * @param useMemory 是否使用memory. + */ + public void setUseMemory(Boolean useMemory) { + this.businessData.put(BS_AIPP_USE_MEMORY_KEY, useMemory); + } + + /** + * 设置dimension. + * + * @param dimension 维度. + */ + public void setDimension(String dimension) { + this.businessData.put("dimension", dimension); + } + + /** + * 获取问题. + * + * @return {@link String} 对象 + */ + public String getQuestion() { + return ObjectUtils.cast(this.businessData.get(BS_AIPP_QUESTION_KEY)); + } + + /** + * 获取实例名称. + * + * @return {@link String} 对象 + */ + public String getInstanceName() { + return ObjectUtils.cast(this.businessData.get(AippConst.INST_NAME_KEY)); + } + + /** + * 设置原始应用版本id. + * + * @param originAppId 原始应用版本id. + */ + public void setOriginAppId(String originAppId) { + this.businessData.put(AippConst.BS_ORIGIN_APP_ID, originAppId); + } + + /** + * 获取原始的appId. + * + * @return 原始appId. + */ + public String getOriginAppId() { + return ObjectUtils.cast(this.businessData.get(AippConst.BS_ORIGIN_APP_ID)); + } + + /** + * 设置原始应用会话id. + * + * @param originChatId 原始应用会话id. + */ + public void setOriginChatId(String originChatId) { + this.businessData.put(AippConst.BS_ORIGIN_CHAT_ID, originChatId); + } + + /** + * 设置流程id. + * + * @param flowTraceId 流程id.. + */ + public void setFlowTraceId(String flowTraceId) { + this.businessData.put(AippConst.INST_FLOW_INST_ID_KEY, flowTraceId); + } + + /** + * 获取流程id. + * + * @return {@link String} 流程id. + */ + public String getFlowTraceId() { + return ObjectUtils.cast(this.businessData.get(AippConst.INST_FLOW_INST_ID_KEY)); + } + + /** + * 获取原始应用会话id. + * + * @return 原始应用会话id. + */ + public String getOriginChatId() { + return Optional.ofNullable(this.businessData.get(AippConst.BS_ORIGIN_CHAT_ID)) + .map(ObjectUtils::cast) + .orElseGet(this::getChatId); + } + + /** + * 设置产品线的id信息 + * + * @param dimensionId 产品线的id信息 + */ + public void setDimensionId(String dimensionId) { + this.businessData.put(AippConst.BS_DIMENSION_ID_KEY, dimensionId); + } + + /** + * 获取产品线的id信息 + * + * @return {@link String} 产品线的id信息. + */ + public String getDimensionId() { + return ObjectUtils.cast(this.businessData.get(AippConst.BS_DIMENSION_ID_KEY)); + } + + /** + * 获取文件描述信息列表. + * + * @return {@link String} 对象 + */ + public List> getFileDescriptions() { + return Optional.ofNullable(this.businessData.get(BS_AIPP_FILE_DESC_KEY)) + .map(ObjectUtils::>>cast) + .orElse(Collections.emptyList()); + } + + /** + * 判断restart模式是否是INCREMENT模式. + * + * @return {@link String} 对象 + */ + public boolean isIncrementMode() { + return StringUtils.equals(RestartModeEnum.INCREMENT.getMode(), + ObjectUtils.cast(this.businessData.get(RESTART_MODE))); + } + + /** + * 设置 resume duration. + * + * @param duration 间隔. + */ + public void setResumeDuration(long duration) { + this.businessData.put(AippConst.INST_RESUME_DURATION_KEY, String.valueOf(duration)); + } + + /** + * 设置上下文的任务实例id. + * + * @param taskInstanceId {@link String} 对象 + */ + public void setContextTaskInstanceId(String taskInstanceId) { + this.businessData.put(AippConst.CONTEXT_INSTANCE_ID, taskInstanceId); + } + + /** + * 获取上下文的任务实例id. + * + * @return 表示上下文的任务实例id的 {@link String} 对象 + */ + public String getContextTaskInstanceId() { + return ObjectUtils.cast(this.businessData.get(AippConst.CONTEXT_INSTANCE_ID)); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/AppChatRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/AppChatRepository.java new file mode 100644 index 0000000000..3ed1e87ae6 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/AppChatRepository.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.chat.repository; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; + +import java.util.Optional; + +/** + * 会话仓库接口. + * + * @author 张越 + * @since 2025-02-08 + */ +public interface AppChatRepository { + /** + * 保存chat. + * + * @param chatCreateEntity 创建数据. + * @param context 操作人上下文信息. + */ + void saveChat(ChatCreateEntity chatCreateEntity, OperationContext context); + + /** + * 通过chatId获取chat数据. + * + * @param chatId 会话id. + * @param user 创建人. + * @return {@link Optional}{@code <}{@link QueryChatRsp}{@code >} 对象. + */ + Optional getChatById(String chatId, String user); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/impl/AppChatRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/impl/AppChatRepositoryImpl.java new file mode 100644 index 0000000000..6d23d67050 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/chat/repository/impl/AppChatRepositoryImpl.java @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.chat.repository.impl; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; +import modelengine.fit.jober.aipp.entity.ChatInfo; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.aipp.util.UUIDUtil; + +import lombok.AllArgsConstructor; +import modelengine.fitframework.annotation.Component; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * 会话仓库实现类 + * + * @author 张越 + * @since 2025-02-08 + */ +@Component +@AllArgsConstructor +public class AppChatRepositoryImpl implements AppChatRepository { + private static final String DEFAULT_CHAT_NAME_PREFIX = "@appBuilderDebug-"; + + private final AippChatMapper aippChatMapper; + + @Override + public void saveChat(ChatCreateEntity chatCreateEntity, OperationContext context) { + String cutChatName = this.generateChatName(chatCreateEntity.getChatName()); + LocalDateTime operateTime = LocalDateTime.now(); + ChatInfo chatInfo = ChatInfo.builder() + .appId(chatCreateEntity.getAppId()) + .version(chatCreateEntity.getAppVersion()) + .attributes(JsonUtils.toJsonString(chatCreateEntity.getAttributes())) + .chatId(chatCreateEntity.getChatId()) + .chatName(cutChatName) + .status(AippConst.CHAT_STATUS) + .updater(context.getOperator()) + .createTime(operateTime) + .updateTime(operateTime) + .creator(context.getOperator()) + .build(); + this.aippChatMapper.insertChat(chatInfo); + ChatAndInstanceMap wideRelationInfo = ChatAndInstanceMap.builder() + .msgId(UUIDUtil.uuid()) + .instanceId(chatCreateEntity.getTaskInstanceId()) + .chatId(chatCreateEntity.getChatId()) + .createTime(operateTime) + .updateTime(operateTime) + .build(); + this.aippChatMapper.insertWideRelationship(wideRelationInfo); + } + + private String generateChatName(String chatName) { + if (chatName == null) { + return DEFAULT_CHAT_NAME_PREFIX + UUIDUtil.uuid().substring(0, 6); + } + return chatName.length() > 64 ? chatName.substring(0, 32) : chatName; + } + + @Override + public Optional getChatById(String chatId, String user) { + List chats = this.aippChatMapper.selectChatList(null, chatId, user); + return chats.stream().findFirst(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/AppDefinitionService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/AppDefinitionService.java new file mode 100644 index 0000000000..d08dfbcd35 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/AppDefinitionService.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.definition.service; + +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; + +/** + * 定义服务接口. + * + * @author 张越 + * @since 2025-02-08 + */ +public interface AppDefinitionService { + /** + * 获取相同的定义. + * + * @param aippDto 参数. + * @return {@link FlowDefinitionResult} 对象. + */ + FlowDefinitionResult getSameFlowDefinition(AippDto aippDto); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/impl/AppDefinitionServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/impl/AppDefinitionServiceImpl.java new file mode 100644 index 0000000000..a711cd9b92 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/definition/service/impl/AppDefinitionServiceImpl.java @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.definition.service.impl; + +import lombok.AllArgsConstructor; +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 定义服务实现类 + * + * @author 张越 + * @since 2025-02-08 + */ +@Component +@AllArgsConstructor +public class AppDefinitionServiceImpl implements AppDefinitionService { + private final AippFlowDefinitionService flowDefinitionService; + + @Override + public FlowDefinitionResult getSameFlowDefinition(AippDto aippDto) { + String metaId = aippDto.getMetaId(); + String version = aippDto.getVersion(); + List flowDefinitions = this.flowDefinitionService.getFlowDefinitionByMetaIdAndPartVersion( + metaId, version + "-", null); + String parsedGraphData = this.flowDefinitionService.getParsedGraphData( + JsonUtils.toJsonString(aippDto.getFlowViewData()), version); + Map aippFlowDefinitionMapping = this.buildFlowDefinition(parsedGraphData); + return flowDefinitions.stream().limit(1).filter(definition -> { + Map map = this.buildFlowDefinition(definition.getGraph()); + return this.compareMaps(map, aippFlowDefinitionMapping); + }).findAny().orElse(null); + } + + private Map buildFlowDefinition(String flowDefinition) { + Map parsedFlowDefinitionMapping = JsonUtils.parseObject(flowDefinition); + + // 这边 name 和 version 不需要比较 + parsedFlowDefinitionMapping.remove(AippConst.FLOW_CONFIG_NAME); + parsedFlowDefinitionMapping.remove(AippConst.FLOW_CONFIG_VERSION_KEY); + return parsedFlowDefinitionMapping; + } + + private boolean compareMaps(Map map1, Map map2) { + if (map1 == map2) { + return true; + } + if (map1 == null || map2 == null) { + return false; + } + if (map1.size() != map2.size()) { + return false; + } + for (Map.Entry entry : map1.entrySet()) { + String key = entry.getKey(); + Object value1 = entry.getValue(); + Object value2 = map2.get(key); + if (!map2.containsKey(key) || !this.isSameObject(value1, value2)) { + return false; + } + } + return true; + } + + private boolean isSameObject(Object obj1, Object obj2) { + if (obj1 == obj2) { + return true; + } + if (obj1 == null || obj2 == null) { + return false; + } + if (obj1 instanceof Map && obj2 instanceof Map) { + return compareMaps(ObjectUtils.cast(obj1), ObjectUtils.cast(obj2)); + } + return Objects.equals(obj1, obj2); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfig.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfig.java new file mode 100644 index 0000000000..612eec6477 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfig.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Jade流程配置数据. + * + * @author 张越 + * @since 2025-01-14 + */ +public class JadeConfig { + private final List pages = new ArrayList<>(); + + public JadeConfig(String appearance) { + JSONArray pageArray = JSONObject.parseObject(appearance).getJSONArray("pages"); + for (int j = 0; j < pageArray.size(); j++) { + JSONObject node = pageArray.getJSONObject(j); + this.pages.add(new JadePage(node)); + } + } + + /** + * 通过图形id获取图形数据. + * + * @param shapeId 图形id. + * @return {@link Optional}{@code <}{@link JadeShape}{@code >} 对象. + */ + public Optional getShapeById(String shapeId) { + for (JadePage page : this.pages) { + Optional shapeOptional = page.getShapeById(shapeId); + if (shapeOptional.isPresent()) { + return shapeOptional; + } + } + return Optional.empty(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadePage.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadePage.java new file mode 100644 index 0000000000..624f0d1d68 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadePage.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import modelengine.fitframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * 页面数据. + * + * @author 张越 + * @since 2025-01-14 + */ +public class JadePage { + private final List shapes; + + public JadePage(JSONObject page) { + this.shapes = new ArrayList<>(); + JSONArray shapeArray = page.getJSONArray("shapes"); + for (int j = 0; j < shapeArray.size(); j++) { + JSONObject node = shapeArray.getJSONObject(j); + this.shapes.add(new JadeShape(node)); + } + } + + /** + * 通过图形id获取图形数据. + * + * @param shapeId 图形id. + * @return {@link Optional}{@code <}{@link JadeShape}{@code >} 对象. + */ + public Optional getShapeById(String shapeId) { + return this.shapes.stream().filter(s -> StringUtils.equals(shapeId, s.getId())).findFirst(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeShape.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeShape.java new file mode 100644 index 0000000000..ae8baaa2b3 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeShape.java @@ -0,0 +1,158 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig; + +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.EvaluationStartNodeInputParamsExtractor; +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.InputParamsExtractor; +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.NullInputParamsExtractor; +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.StartNodeInputParamsExtractor; +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.StateNodeInputParamsExtractor; +import modelengine.fit.jober.aipp.domains.jadeconfig.extractors.TaskNodeInputParamsExtractor; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.MapUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 图形数据. + * + * @author 张越 + * @since 2025-01-14 + */ +public class JadeShape { + private static final Map EXTRACTORS = MapBuilder.get() + .put("startNodeStart", new StartNodeInputParamsExtractor()) + .put("evaluationStartNodeStart", new EvaluationStartNodeInputParamsExtractor()) + .put("endNodeEnd", new NullInputParamsExtractor()) + .put("evaluationEndNodeEnd", new NullInputParamsExtractor()) + .put("jadeEvent", new NullInputParamsExtractor()) + .put("conditionNodeCondition", new NullInputParamsExtractor()) + .put("manualCheckNodeState", new TaskNodeInputParamsExtractor()) + .put("intelligentFormNodeState", new TaskNodeInputParamsExtractor()) + .build(); + + private final JSONObject shape; + private Map params; + + public JadeShape(JSONObject shape) { + this.shape = shape; + JSONArray inputParams = this.getInputParam(); + if (inputParams != null) { + this.params = this.extractingExpandObject(this.getInputParam()); + } + } + + private List extractingExpandArray(JSONArray value) { + List result = new ArrayList<>(); + for (int index = 0; index < value.size(); index++) { + JSONObject jsonObject = value.getJSONObject(index); + if (this.isFromInput(jsonObject)) { + result.add(jsonObject.get("value")); + continue; + } + if (this.isFromExpand(jsonObject)) { + this.handleExpandType(jsonObject, result); + } + } + return result; + } + + private void handleExpandType(JSONObject jsonObject, List result) { + if (this.isArray(jsonObject)) { + List array = this.extractingExpandArray(jsonObject.getJSONArray("value")); + result.add(array); + return; + } + if (this.isObject(jsonObject)) { + Map map = this.extractingExpandObject(jsonObject.getJSONArray("value")); + if (MapUtils.isNotEmpty(map)) { + result.add(map); + } + } + } + + // 如果type是Object,那么调用这个方法获取一个Map + private Map extractingExpandObject(JSONArray value) { + Map result = new HashMap<>(); + for (int index = 0; index < value.size(); index++) { + JSONObject jsonObject = value.getJSONObject(index); + if (this.isFromInput(jsonObject)) { + result.put(jsonObject.getString("name"), jsonObject.get("value")); + continue; + } + if (this.isFromExpand(jsonObject)) { + this.handleExpandType(jsonObject, result); + } + } + return result; + } + + private void handleExpandType(JSONObject jsonObject, Map result) { + if (this.isArray(jsonObject)) { + List array = this.extractingExpandArray(jsonObject.getJSONArray("value")); + result.put(jsonObject.getString("name"), array); + return; + } + if (this.isObject(jsonObject)) { + Map map = this.extractingExpandObject(jsonObject.getJSONArray("value")); + if (MapUtils.isNotEmpty(map)) { + result.put(jsonObject.getString("name"), map); + } + } + } + + private boolean isFromExpand(JSONObject jsonObject) { + return StringUtils.equalsIgnoreCase("Expand", jsonObject.getString("from")); + } + + private boolean isFromInput(JSONObject jsonObject) { + return StringUtils.equalsIgnoreCase("Input", jsonObject.getString("from")); + } + + private boolean isObject(JSONObject jsonObject) { + return StringUtils.equalsIgnoreCase("Object", jsonObject.getString("type")); + } + + private boolean isArray(JSONObject jsonObject) { + return StringUtils.equalsIgnoreCase("Array", jsonObject.getString("type")); + } + + /** + * 获取id. + * + * @return {@link String} 唯一标识. + */ + public String getId() { + return this.shape.getString("id"); + } + + /** + * 获取值. + * + * @param key 键值. + * @return 值. + */ + public Optional getValue(String key) { + return Optional.ofNullable(this.params.get(key)); + } + + private JSONArray getInputParam() { + String nodeType = this.shape.getString("type"); + InputParamsExtractor extractor = Optional.ofNullable(EXTRACTORS.get(nodeType)) + .orElseGet(StateNodeInputParamsExtractor::new); + return extractor.extract(this.shape); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/EvaluationStartNodeInputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/EvaluationStartNodeInputParamsExtractor.java new file mode 100644 index 0000000000..48bc4d4e39 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/EvaluationStartNodeInputParamsExtractor.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 评估开始节点入参提取器. + * + * @author 张越 + * @since 2025-01-14 + */ +public class EvaluationStartNodeInputParamsExtractor implements InputParamsExtractor { + @Override + public JSONArray extract(JSONObject shape) { + return new JSONArray(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/InputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/InputParamsExtractor.java new file mode 100644 index 0000000000..d2b1f50244 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/InputParamsExtractor.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 输入参数提取器. + * + * @author 张越 + * @since 2025-01-14 + */ +public interface InputParamsExtractor { + /** + * 提取. + * + * @param shape 图形数据. + * @return {@link JSONArray} 对象. + */ + JSONArray extract(JSONObject shape); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/NullInputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/NullInputParamsExtractor.java new file mode 100644 index 0000000000..6b1d93b8c7 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/NullInputParamsExtractor.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 统一返回null的入参提取器. + * + * @author 张越 + * @since 2025-01-14 + */ +public class NullInputParamsExtractor implements InputParamsExtractor { + @Override + public JSONArray extract(JSONObject shape) { + return null; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StartNodeInputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StartNodeInputParamsExtractor.java new file mode 100644 index 0000000000..d1767a48da --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StartNodeInputParamsExtractor.java @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 开始节点入参提取器. + * + * @author 张越 + * @since 2025-01-14 + */ +public class StartNodeInputParamsExtractor implements InputParamsExtractor { + @Override + public JSONArray extract(JSONObject shape) { + return shape.getJSONObject("flowMeta").getJSONArray("inputParams"); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StateNodeInputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StateNodeInputParamsExtractor.java new file mode 100644 index 0000000000..89393578e3 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/StateNodeInputParamsExtractor.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 状态节点入参提取器. + * + * @author 张越 + * @since 2025-01-14 + */ +public class StateNodeInputParamsExtractor implements InputParamsExtractor { + @Override + public JSONArray extract(JSONObject shape) { + return shape.getJSONObject("flowMeta") + .getJSONObject("jober") + .getJSONObject("converter") + .getJSONObject("entity") + .getJSONArray("inputParams"); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/TaskNodeInputParamsExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/TaskNodeInputParamsExtractor.java new file mode 100644 index 0000000000..f98159f9aa --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/jadeconfig/extractors/TaskNodeInputParamsExtractor.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig.extractors; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * 任务型节点入参提取器. + * + * @author 张越 + * @since 2025-02-12 + */ +public class TaskNodeInputParamsExtractor implements InputParamsExtractor { + @Override + public JSONArray extract(JSONObject shape) { + return shape.getJSONObject("flowMeta") + .getJSONObject("task") + .getJSONObject("converter") + .getJSONObject("entity") + .getJSONArray("inputParams"); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLog.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLog.java new file mode 100644 index 0000000000..4e9881109c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLog.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.log; + +import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INFOS_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INPUT_KEY; + +import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import lombok.Getter; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +/** + * 应用日志对象. + * + * @author 张越 + * @since 2025-02-07 + */ +public class AppLog { + private static final HashSet QUESTION_TYPE = new HashSet<>(Arrays.asList(AippInstLogType.QUESTION.name(), + AippInstLogType.HIDDEN_QUESTION.name(), + AippInstLogType.QUESTION_WITH_FILE.name())); + + @Getter + private final AippInstLog logData; + + AppLog(AippInstLog logData) { + this.logData = logData; + } + + /** + * 是否是问题类型的日志. + * + * @return true/false. + */ + public boolean isQuestionType() { + return QUESTION_TYPE.contains(this.logData.getLogType()); + } + + /** + * 将 {@link AppLog} 转换为 {@link AippInstLogDataDto.AippInstanceLogBody}. + * + * @return {@link AippInstLogDataDto.AippInstanceLogBody} 对象. + */ + public AippInstLogDataDto.AippInstanceLogBody toBody() { + return new AippInstLogDataDto.AippInstanceLogBody(this.logData.getLogId(), this.logData.getLogData(), + this.logData.getLogType(), this.logData.getCreateAt(), this.logData.getCreateUserAccount()); + } + + /** + * 判断是否是某个类型. + * + * @param types 类型列表. + * @return true/false. + */ + public boolean is(AippInstLogType... types) { + String logType = this.logData.getLogType(); + return Arrays.stream(types).anyMatch(e -> StringUtils.equals(logType, e.name())); + } + + /** + * 获取输入信息. + * + * @return 输入信息的 {@link Optional} 对象. + */ + public Optional> getInput() { + Map data = JsonUtils.parseObject(this.logData.getLogData()); + if (data.containsKey(BUSINESS_INFOS_KEY)) { + Map infos = ObjectUtils.cast(data.get(BUSINESS_INFOS_KEY)); + if (infos.containsKey(BUSINESS_INPUT_KEY)) { + return Optional.of(ObjectUtils.cast(infos.get(BUSINESS_INPUT_KEY))); + } + } + return Optional.empty(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLogFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLogFactory.java new file mode 100644 index 0000000000..c9be027b57 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/AppLogFactory.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.log; + +import static modelengine.fit.jober.aipp.enums.AippInstLogType.FORM; + +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.entity.AippLogData; +import modelengine.fit.jober.aipp.util.JsonUtils; + +import lombok.RequiredArgsConstructor; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.StringUtils; + +import java.util.Map; + +/** + * {@link AppLog} 的工厂类 + * + * @author 张越 + * @since 2025-02-07 + */ +@Component +@RequiredArgsConstructor +public class AppLogFactory { + private static final String FORM_DATA = "formData"; + private static final String FORM_APPEARANCE = "formAppearance"; + + /** + * 通过 {@link AippInstLog} 和任务id创建一个实例对象. + * + * @param logData 日志对象. + * @return {@link AppLog} 对象. + */ + public AppLog create(AippInstLog logData) { + if (StringUtils.equals(FORM.name(), logData.getLogType())) { + AippLogData form = JsonUtils.parseObject(logData.getLogData(), AippLogData.class); + if (form != null) { + Map newLogData = MapBuilder.get() + .put(FORM_DATA, form.getFormData()) + .put(FORM_APPEARANCE, form.getFormAppearance()) + .build(); + logData.setLogData(JsonUtils.toJsonString(newLogData)); + } + } + return new AppLog(logData); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/AippLogRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/AippLogRepository.java new file mode 100644 index 0000000000..7384fd7b80 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/AippLogRepository.java @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.log.repository; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.log.AppLog; + +import java.util.List; + +/** + * 日志仓库接口. + * + * @author 张越 + * @since 2025-02-07 + */ +public interface AippLogRepository { + /** + * 查询任务实例的所有日志,包含子实例的日志. + * + * @param instanceId 实例id. + * @return 日志列表. + */ + List selectAllLogsByInstanceId(String instanceId); + + /** + * 根据父Instance的id获取其路径。 + * + * @param parentInstId 表示父instance的id的 {@link String}。 + * @return 表示父instId的路径的 {@link String}。 + */ + String getParentPath(String parentInstId); + + /** + * 查询指定实例且指定类型的的日志。 + * + * @param instanceId 表示指定实例 id 的 {@link String}。 + * @param logTypes 表示指定日志类型列表的 {@link List}{@code <}{@link String}{@code >}。 + * @return 表示查询到的日志列表的 {@link List}{@code <}{@link AppLog}{@code >}。 + */ + List selectByInstanceIdAndLogTypes(String instanceId, List logTypes); + + /** + * 删除指定实例的历史记录。 + * + * @param instanceId 指定实例的 id。 + */ + void deleteByInstanceId(String instanceId); + + /** + * 删除指定aipp预览的历史记录 + * + * @param previewAippId 指定aipp的id + * @param context 登录信息 + */ + void deleteAippPreviewLog(String previewAippId, OperationContext context); + + /** + * 修改数据和类型. + * + * @param logId 日志id. + * @param newLogType 新的日志类型. + * @param newLogData 新的日志数据. + */ + void updateDataAndType(Long logId, String newLogType, String newLogData); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/impl/AippLogRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/impl/AippLogRepositoryImpl.java new file mode 100644 index 0000000000..90245182ad --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/log/repository/impl/AippLogRepositoryImpl.java @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.log.repository.impl; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.log.AppLogFactory; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; + +import lombok.AllArgsConstructor; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.StringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * 日志仓库实现类 + * + * @author 张越 + * @since 2025-02-07 + */ +@Component +@AllArgsConstructor +public class AippLogRepositoryImpl implements AippLogRepository { + private static final Logger log = Logger.get(AippLogRepositoryImpl.class); + + private final AippLogMapper aippLogMapper; + private final AppLogFactory appLogFactory; + + @Override + public List selectAllLogsByInstanceId(String instanceId) { + return this.aippLogMapper.getFullLogsByInstanceIds(Collections.singletonList(instanceId)) + .stream() + .map(this.appLogFactory::create) + .toList(); + } + + @Override + public String getParentPath(String parentInstId) { + return Optional.ofNullable(parentInstId).map(this.aippLogMapper::getParentPath).orElse(StringUtils.EMPTY); + } + + @Override + public List selectByInstanceIdAndLogTypes(String instanceId, List logTypes) { + if (StringUtils.isEmpty(instanceId)) { + log.error("When queryLogsByInstanceIdAndLogTypes input instance id is empty."); + throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); + } + return this.aippLogMapper.getLogsByInstanceIdAndLogTypes(instanceId, logTypes) + .stream() + .map(this.appLogFactory::create) + .toList(); + } + + @Override + public void deleteByInstanceId(String instanceId) { + if (StringUtils.isEmpty(instanceId)) { + log.error("Instance id is null or empty."); + throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); + } + this.aippLogMapper.deleteInstanceLog(instanceId); + } + + @Override + public void deleteAippPreviewLog(String previewAippId, OperationContext context) { + this.aippLogMapper.deleteByType(previewAippId, AippTypeEnum.PREVIEW.name(), context.getAccount(), null); + } + + @Override + public void updateDataAndType(Long logId, String newLogType, String newLogData) { + this.aippLogMapper.updateDataAndType(logId, newLogType, newLogData); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTask.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTask.java new file mode 100644 index 0000000000..67d6ac2b53 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTask.java @@ -0,0 +1,561 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INPUT_KEY; +import static modelengine.fit.jober.aipp.enums.AippInstLogType.HIDDEN_QUESTION; +import static modelengine.fit.jober.aipp.enums.AippInstLogType.QUESTION; +import static modelengine.fit.jober.aipp.enums.AippInstLogType.QUESTION_WITH_FILE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotBlank; + +import com.alibaba.fastjson.JSONObject; + +import lombok.Getter; +import lombok.Setter; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.domain.type.DateTimeConverter; +import modelengine.fit.jober.aipp.common.AppTaskRunnable; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.convertor.FormMetaConvertor; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.TaskInstanceDecorator; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.AppInputParam; +import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; +import modelengine.fit.jober.aipp.entity.AippLogData; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.enums.JaneCategory; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.service.AopAippLogService; +import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.jober.aipp.service.AppChatSseService; +import modelengine.fit.jober.aipp.util.AippLogUtils; +import modelengine.fit.jober.aipp.util.DataUtils; +import modelengine.fit.jober.aipp.util.FormUtils; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.aipp.util.UsefulUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 应用任务对象. + * + * @author 张越 + * @since 2025-01-03 + */ +public class AppTask implements AppTaskRunnable { + private static final Logger log = Logger.get(AppTask.class); + + private final TaskEntity entity; + + private AppTaskInstanceService appTaskInstanceService; + private AippLogRepository aippLogRepository; + private FlowsService flowsService; + private AppChatSessionService appChatSessionService; + private FlowInstanceService flowInstanceService; + private AppTaskService appTaskService; + private AppBuilderFormPropertyRepository formPropertyRepository; + private AopAippLogService aopAippLogService; + private AppChatSseService appChatSseService; + + @Getter + @Setter + private AppVersion appVersion; + + private List instances; + private List formProperties; + + AppTask(TaskEntity entity) { + this.entity = entity; + } + + AppTask(AippLogRepository aippLogRepository, AppTaskInstanceService appTaskInstanceService, + FlowsService flowsService, AppChatSessionService appChatSessionService, + FlowInstanceService flowInstanceService, AppTaskService appTaskService, + AppBuilderFormPropertyRepository formPropertyRepository, AopAippLogService aopAippLogService, + AppChatSseService appChatSseService) { + this.entity = new TaskDomainEntity(); + this.aippLogRepository = aippLogRepository; + this.appTaskInstanceService = appTaskInstanceService; + this.flowsService = flowsService; + this.appChatSessionService = appChatSessionService; + this.flowInstanceService = flowInstanceService; + this.appTaskService = appTaskService; + this.formPropertyRepository = formPropertyRepository; + this.aopAippLogService = aopAippLogService; + this.appChatSseService = appChatSseService; + } + + /** + * 作为实体. + * + * @return {@link TaskDomainEntity} 对象. + */ + public static TaskDomainEntity asEntity() { + return new TaskDomainEntity(); + } + + /** + * 作为创建参数. + * + * @return {@link TaskDomainEntity} 对象 + */ + public static TaskDomainEntity asCreateEntity() { + TaskDomainEntity entity = new TaskDomainEntity(); + entity.setCategory(JaneCategory.AIPP.name()); + entity.setStatus(AippMetaStatusEnum.INACTIVE.getCode()); + entity.setProperties(AippConst.STATIC_META_ITEMS.stream() + .map(FormMetaConvertor.INSTANCE::toTaskProperty) + .collect(Collectors.toList())); + return entity; + } + + /** + * 作为修改参数. + * + * @param taskId 任务唯一标识. + * @return {@link TaskDomainEntity} 对象. + */ + public static TaskDomainEntity asUpdateEntity(String taskId) { + TaskDomainEntity entity = new TaskDomainEntity(); + entity.setTaskId(taskId); + return entity; + } + + /** + * 作为查询参数. + * + * @param offset 偏移量. + * @param limit 限制. + * @return {@link TaskQueryEntity} 对象. + */ + public static TaskQueryEntity asQueryEntity(long offset, int limit) { + return new TaskQueryEntity(offset, limit); + } + + /** + * 将entity转换为特定的Entity类型对象. + * + * @param 代表Entity的类型. + * @return 特定的 {@link TaskEntity} 类型. + */ + public > T getEntity() { + return ObjectUtils.cast(this.entity); + } + + @Override + public void run(RunContext runContext) { + this.run(runContext, null); + } + + @Override + public void run(RunContext ctx, ChatSession chatSession) { + OperationContext context = ctx.getOperationContext(); + + // 创建实例 + String taskId = this.entity.getTaskId(); + String name = ctx.getInstanceName(); + AppTaskInstance taskInstance = this.appTaskInstanceService.createInstance( + AppTaskInstance.asCreate(taskId, context.getOperator(), name) + .setStatus(MetaInstStatusEnum.RUNNING.name()) + .build(), context); + + // 设置上下文属性. + ctx.setAppSuiteId(this.entity.getAppSuiteId()); + ctx.setAppVersion(this.entity.getVersion()); + ctx.setTaskId(this.entity.getTaskId()); + ctx.setAippType(Optional.ofNullable(this.entity.getAippType()).orElse(NORMAL.name())); + ctx.setTaskInstanceId(taskInstance.getId()); + ctx.setHttpContext(JsonUtils.toJsonString(context)); + + log.info("[perf] [{}] startChat persistAippLog start, metaInstId={}", System.currentTimeMillis(), + taskInstance.getId()); + this.persistAippLog(ctx, taskInstance); + + log.info("[perf] [{}] startChat persistAippLog end, metaInstId={}", System.currentTimeMillis(), + taskInstance.getId()); + // 持久化aipp实例表单记录 + this.persistAippFormLog(ctx, taskInstance); + log.info("[perf] [{}] startChat persistAippFormLog end, metaInstId={}", System.currentTimeMillis(), + taskInstance.getId()); + + // 记录上下文 + this.recordContext(ctx, taskInstance); + log.info("[perf] [{}] startChat recordContext end, metaInstId={}", System.currentTimeMillis(), + taskInstance.getId()); + + // 启动实例. + ctx.setMemoryConfig(this.getMemoryConfigs(this.entity.getFlowDefinitionId(), context)); + ctx.setAppTask(this); + TaskInstanceDecorator.create(taskInstance) + .chat(this.appChatSessionService, this.appChatSseService) + .run(ctx, chatSession); + + ctx.setFlowTraceId(taskInstance.getEntity().getFlowTranceId()); + } + + private void persistAippLog(RunContext ctx, AppTaskInstance instance) { + String question = ctx.getQuestion(); + List> fileDescList = ctx.getFileDescriptions(); + + // 持久化日志 + if (CollectionUtils.isEmpty(fileDescList)) { + if (ctx.isIncrementMode()) { + // 如果是处于增长式的重新对话中,插入 hidden_question + this.insertLog(HIDDEN_QUESTION.name(), AippLogData.builder().msg(question).build(), ctx, instance); + } else { + // 插入question日志 + Map infos = this.buildLogInfos(ctx); + this.insertLog(QUESTION.name(), AippLogData.builder().msg(question).infos(infos).build(), ctx, + instance); + } + } else { + JSONObject msgJsonObj = new JSONObject(); + msgJsonObj.put("question", question); + msgJsonObj.put("files", fileDescList); + this.insertLog(QUESTION_WITH_FILE.name(), AippLogData.builder().msg(msgJsonObj.toJSONString()).build(), ctx, + instance); + } + } + + private Map buildLogInfos(RunContext runContext) { + FlowInfo flowInfo = this.flowsService.getFlows(this.entity.getFlowDefinitionId(), + runContext.getOperationContext()); + List names = flowInfo.getInputParamsByName("input") + .stream() + .map(AppInputParam::from) + .map(AppInputParam::getName) + .toList(); + if (CollectionUtils.isEmpty(names)) { + return new HashMap<>(); + } + Map inputParams = new HashMap<>(); + runContext.getBusinessData() + .entrySet() + .stream() + .filter(data -> names.contains(data.getKey())) + .forEach(data -> inputParams.put(data.getKey(), data.getValue())); + Map infos = new HashMap<>(); + infos.put(BUSINESS_INPUT_KEY, inputParams); + return infos; + } + + private void persistAippFormLog(RunContext context, AppTaskInstance instance) { + String formId = this.entity.getStartFormId(); + String formVersion = this.entity.getStartFormVersion(); + if (StringUtils.isNotEmpty(formId) && StringUtils.isNotEmpty(formVersion)) { + AippLogData logData = FormUtils.buildLogDataWithFormData(this.getFormProperties(), formId, formVersion, + context.getBusinessData()); + this.insertLog(AippInstLogType.FORM.name(), logData, context, instance); + } + } + + private void insertLog(String logType, AippLogData logData, RunContext runContext, AppTaskInstance instance) { + String aippId = runContext.getAppSuiteId(); + String instId = runContext.getTaskInstanceId(); + String version = runContext.getAppVersion(); + String aippType = runContext.getAippType(); + + String account = DataUtils.getOpContext(runContext.getBusinessData()).getAccount(); + if (!AippLogUtils.validFormMsg(logData, logType)) { + return; + } + String path = instance.getPath(runContext.getOperationContext()); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); + AippLogCreateDto createDto = AippLogCreateDto.builder() + .aippId(aippId) + .version(version) + .aippType(aippType) + .instanceId(instId) + .logType(logType) + .logData(JsonUtils.toJsonString(logData)) + .createUserAccount(account) + .path(path) + .chatId(chatId) + .atChatId(atChatId) + .build(); + this.aopAippLogService.insertLog(createDto); + } + + /** + * 获取表单配置项集合. + * + * @return {@link List}{@code <}{@link AppBuilderFormProperty}{@code >} 集合. + */ + public List getFormProperties() { + return UsefulUtils.lazyGet(this.formProperties, + () -> this.formPropertyRepository.selectWithAppId(this.getEntity().getAppId()), + ps -> this.formProperties = ps); + } + + private void recordContext(RunContext context, AppTaskInstance taskInstance) { + context.setAppId(this.entity.getAppId()); + context.setUserId(context.getOperationContext().getOperator()); + context.setContextTaskInstanceId(taskInstance.getId()); + List fileUrls = context.getFileDescriptions() + .stream() + .map(fileDesc -> fileDesc.get("file_url")) + .toList(); + if (CollectionUtils.isNotEmpty(fileUrls)) { + context.setFileUrls(fileUrls); + } + } + + private List> getMemoryConfigs(String flowDefinitionId, OperationContext context) { + try { + FlowInfo flowInfo = this.flowsService.getFlows(flowDefinitionId, context); + return flowInfo.getInputParamsByName(AippConst.MEMORY_CONFIG_KEY); + } catch (JobberException e) { + log.error("get flow failed, flowDefinitionId {}", flowDefinitionId); + throw new AippException(context, AippErrCode.OBTAIN_APP_ORCHESTRATION_INFO_FAILED); + } + } + + /** + * 是否处于草稿态. + * + * @return true/false. + */ + public boolean isDraft() { + String baseLineVersion = this.entity.getBaseLineVersion(); + String status = this.entity.getStatus(); + return baseLineVersion != null && AippMetaStatusEnum.getAippMetaStatus(status) != AippMetaStatusEnum.ACTIVE; + } + + /** + * 是否是正常类型任务. + * + * @return true/false. + */ + public boolean isNormal() { + return StringUtils.equalsIgnoreCase(this.entity.getAippType(), NORMAL.name()); + } + + /** + * 是否处于active状态. + * + * @return true/false. + */ + public boolean isActive() { + return StringUtils.equals(AippMetaStatusEnum.ACTIVE.getCode(), this.entity.getStatus()); + } + + /** + * 通过最新的版本判断是否是升级. + * + * @param newVersion 最新的版本号. + * @return true/false. + */ + public boolean isUpgrade(String newVersion) { + return this.isActive() || !StringUtils.equals(newVersion, this.entity.getVersion()); + } + + /** + * 判断任务是否属于某个app版本. + * + * @param appId 应用版本id. + * @return true/false. + */ + public boolean isBelongApp(String appId) { + return StringUtils.equals(appId, this.entity.getAppId()); + } + + /** + * 是否已发布. + * + * @return true/false. + */ + public boolean isPublished() { + if (StringUtils.isBlank(this.entity.getAippType()) || StringUtils.isBlank(this.entity.getStatus())) { + return false; + } + return StringUtils.equals(this.entity.getAippType(), AippTypeEnum.NORMAL.name()) + && StringUtils.equals(this.entity.getStatus(), AippMetaStatusEnum.ACTIVE.getCode()); + } + + /** + * 停止所有运行中的实例. + * + * @param context 操作人上下文信息. + */ + public void terminateAllInstances(OperationContext context) { + String taskId = this.entity.getTaskId(); + Stream instanceStream = this.appTaskInstanceService.getInstanceStreamByTaskId( + taskId, 15, context); + + // 只停止正在运行的 + instanceStream.filter(AppTaskInstance::isRunning).forEach(instance -> { + String flowTraceId = instance.getEntity().getFlowTranceId(); + Validation.notNull(flowTraceId, "flowTraceId can not be null"); + this.flowInstanceService.terminateFlows(null, flowTraceId, Collections.emptyMap(), context); + + // 更新实例状态. + // 修改taskInstance. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(taskId, instance.getId()) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.TERMINATED.name()) + .build(); + this.appTaskInstanceService.update(updateEntity, context); + }); + } + + /** + * 清理资源. + * + * @param context 操作人上下文信息. + */ + public void cleanResource(OperationContext context) { + String previewVersion = this.getEntity().getVersion(); + if (this.isActive()) { + this.terminateAllInstances(context); + this.aippLogRepository.deleteAippPreviewLog(this.getEntity().getAppSuiteId(), context); + } + String flowId = this.getEntity().getFlowConfigId(); + if (!StringUtils.isBlank(flowId)) { + try { + this.flowsService.deleteFlowsWithoutElsa(flowId, previewVersion, context); + } catch (JobberException e) { + log.error("delete flow failed, flowId: {} previewVersion: {}", flowId, previewVersion); + throw new AippException(context, AippErrCode.APP_PUBLISH_FAILED); + } + } + this.appTaskService.deleteTaskById(this.getEntity().getTaskId(), context); + } + + /** + * 获取所有实例. + * + * @param context 操作人上下文信息. + * @return {@link AppTaskInstance} 列表. + */ + public List getInstances(OperationContext context) { + return UsefulUtils.lazyGet(this.instances, + () -> this.appTaskInstanceService.getInstancesByTaskId(this.entity.getTaskId(), 10, context), + r -> this.instances = r); + } + + /** + * 删除task,同时删除相关数据. + * + * @param context 操作人上下文信息. + */ + public void delete(OperationContext context) { + // 需要先删除instance,再删除task + this.getInstances(context).forEach( + instance -> this.appTaskInstanceService.delete(instance.getTaskId(), instance.getId(), context)); + this.appTaskService.deleteTaskById(this.getEntity().getTaskId(), context); + this.flowsService.deleteFlowsWithoutElsa(this.getEntity().getAppSuiteId(), this.getEntity().getVersion(), + context); + } + + /** + * 恢复执行. + * + * @param instanceId 实例id. + * @param logId 日志id. + * @param formArgs 表单参数. + * @param context 操作人上下文. + */ + public void resume(String instanceId, Long logId, Map formArgs, OperationContext context) { + AppTaskInstance instance = this.appTaskInstanceService.getInstanceById(instanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + + // 更新表单数据 + RunContext runContext = new RunContext(ObjectUtils.cast(formArgs.get(AippConst.BS_DATA_KEY)), context); + runContext.setAppSuiteId(this.getEntity().getAppSuiteId()); + runContext.setAppVersion(this.getEntity().getVersion()); + runContext.setTaskId(this.getEntity().getTaskId()); + runContext.setAippType(Optional.ofNullable(this.getEntity().getAippType()).orElse(NORMAL.name())); + runContext.setTaskInstanceId(instanceId); + runContext.setHttpContext(JsonUtils.toJsonString(context)); + + // 获取人工节点开始时间戳 [记录人工节点时延] + runContext.setResumeDuration( + instance.getEntity().getResumeDuration() + instance.getEntity().getDuration().toMillis()); + doIfNotBlank(instance.getEntity().getCreateTime(), + (ct) -> runContext.setStartTime(DateTimeConverter.INSTANCE.fromExternal(ct))); + + this.updateLog(logId, formArgs, instance, runContext); + this.updateInstance(instanceId, context, runContext); + + try { + instance.resume(this.getEntity().getFlowDefinitionId(), formArgs, context); + } catch (JobberException e) { + log.error("resume flow failed, flowDefinitionId:{}, flowTraceId:{}, formArgs:{}", + this.getEntity().getFlowDefinitionId(), instance.getEntity().getFlowTranceId(), formArgs); + throw new AippException(context, AippErrCode.RESUME_CHAT_FAILED); + } catch (AippException e) { + this.updateInstanceStatusError(this.entity.getTaskId(), instanceId, context); + this.insertLog(AippInstLogType.ERROR.name(), AippLogData.builder().msg(e.getMessage()).build(), runContext, + instance); + } + } + + private void updateInstance(String instanceId, OperationContext context, RunContext runContext) { + // 更新实例并清空当前表单数据 + // 修改taskInstance. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(this.getEntity().getTaskId(), instanceId) + .fetch(runContext.getBusinessData(), this.getEntity().getProperties()) + .setFormId(AippConst.INVALID_FORM_ID) + .setFormVersion(AippConst.INVALID_FORM_VERSION_ID) + .build(); + this.appTaskInstanceService.update(updateEntity, context); + } + + private void updateLog(Long logId, Map formArgs, AppTaskInstance instance, RunContext runContext) { + // 持久化aipp实例表单记录 + String formId = instance.getEntity().getFormId(); + String formVersion = instance.getEntity().getFormVersion(); + AippLogData logData = FormUtils.buildLogDataWithFormData(this.getFormProperties(), formId, formVersion, + runContext.getBusinessData()); + + // 设置表单的渲染数据和填充数据 + logData.setFormAppearance(ObjectUtils.cast(formArgs.get(AippConst.FORM_APPEARANCE_KEY))); + logData.setFormData(ObjectUtils.cast(formArgs.get(AippConst.FORM_DATA_KEY))); + this.aippLogRepository.updateDataAndType(logId, AippInstLogType.HIDDEN_FORM.name(), + JsonUtils.toJsonString(logData)); + } + + private void updateInstanceStatusError(String versionId, String instanceId, OperationContext context) { + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(versionId, instanceId) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.ERROR.name()) + .build(); + this.appTaskInstanceService.update(updateEntity, context); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskFactory.java new file mode 100644 index 0000000000..93899471f2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskFactory.java @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jane.Undefinable.defined; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotNull; + +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jane.meta.multiversion.definition.MetaDeclarationInfo; +import modelengine.fit.jane.meta.property.MetaPropertyDeclarationInfo; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.service.AopAippLogService; +import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.jober.aipp.service.AppChatSseService; +import modelengine.fit.jober.entity.task.TaskProperty; + +import lombok.RequiredArgsConstructor; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.CollectionUtils; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * {@link AppTask} 的工厂类 + * + * @author 张越 + * @since 2025-01-03 + */ +@Component +@RequiredArgsConstructor +public class AppTaskFactory { + private final AippLogRepository aippLogRepository; + private final AppTaskInstanceService appTaskInstanceService; + private final FlowsService flowsService; + private final AppChatSessionService appChatSessionService; + private final FlowInstanceService flowInstanceService; + private final AppBuilderFormPropertyRepository appBuilderFormPropertyRepository; + private final AopAippLogService aopAippLogService; + private final AppChatSseService appChatSseService; + + /** + * 将 {@link AppTask} 转换为 {@link MetaDeclarationInfo} 对象. + * + * @param task {@link AppTask} 对象. + * @return {@link MetaDeclarationInfo} 对象. + */ + public MetaDeclarationInfo toMetaDeclaration(AppTask task) { + MetaDeclarationInfo info = new MetaDeclarationInfo(); + doIfNotNull(task.getEntity().getAppSuiteId(), v -> info.setBasicMetaTemplateId(defined(v))); + doIfNotNull(task.getEntity().getName(), v -> info.setName(defined(v))); + doIfNotNull(task.getEntity().getVersion(), v -> info.setVersion(defined(v))); + doIfNotNull(task.getEntity().getCategory(), v -> info.setCategory(defined(v))); + + // 设置attributes. + task.getEntity().visitAttributes(info::putAttribute); + + // 设置properties. + List metaProperties = task.getEntity() + .getProperties() + .stream() + .map(this::toMetaProperty) + .collect(Collectors.toList()); + if (CollectionUtils.isNotEmpty(metaProperties)) { + info.setProperties(defined(metaProperties)); + } + return info; + } + + private MetaPropertyDeclarationInfo toMetaProperty(TaskProperty property) { + MetaPropertyDeclarationInfo metaProperty = new MetaPropertyDeclarationInfo(); + metaProperty.setName(defined(property.getName())); + metaProperty.setDataType(defined(property.getDataType())); + metaProperty.setDescription(defined(property.getDescription())); + return metaProperty; + } + + /** + * 通过 {@link Meta} 和任务id创建一个实例对象. + * + * @param meta 任务对象. + * @param appTaskService 任务服务对象. + * @return {@link AppTask} 对象. + */ + public AppTask create(Meta meta, AppTaskService appTaskService) { + AppTask appTask = new AppTask(this.aippLogRepository, this.appTaskInstanceService, this.flowsService, + this.appChatSessionService, this.flowInstanceService, appTaskService, + this.appBuilderFormPropertyRepository, this.aopAippLogService, this.appChatSseService); + appTask.getEntity().loadFrom(meta); + return appTask; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskUtils.java new file mode 100644 index 0000000000..b4d17591c2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/AppTaskUtils.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; + +/** + * {@link AppTask} 的工厂类 + * + * @author 张越 + * @since 2025-02-06 + */ +public class AppTaskUtils { + /** + * 转换为发布数据对象. + * + * @param appTask 任务领域对象. + * @param appVersion 应用版本. + * @param converterFactory 转化器工厂。 + * @return {@link AppBuilderAppDto} 对象. + */ + public static AppBuilderAppDto toPublishedAppBuilderAppDto(AppTask appTask, AppVersion appVersion, + ConverterFactory converterFactory) { + AppBuilderAppDto appDto = converterFactory.convert(appVersion, AppBuilderAppDto.class); + appDto.setPublishedDescription(appTask.getEntity().getPublishDescription()); + appDto.setPublishedUpdateLog(appTask.getEntity().getPublishLog()); + return appDto; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDecorator.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDecorator.java new file mode 100644 index 0000000000..8788c04836 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDecorator.java @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import lombok.Getter; +import modelengine.fit.jober.aipp.common.AppTaskRunnable; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.entity.AippLogData; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.jade.common.globalization.LocaleService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * 应用任务的装饰类. + * + * @author 张越 + * @since 2025-01-13 + */ +@Getter +public class TaskDecorator implements AppTaskRunnable { + private static final String UI_WORD_KEY = "aipp.service.impl.AippRunTimeServiceImpl"; + private static final Logger log = LoggerFactory.getLogger(TaskDecorator.class); + private static final Set PASS_THROUGH_ERROR = + new HashSet<>(List.of(AippErrCode.CHAT_QUEUE_TOO_LONG.getCode())); + + private final AppTask task; + private final AippLogService aippLogService; + private final AppTaskInstanceService appTaskInstanceService; + private final LocaleService localeService; + + private AppTaskRunnable proxy; + + private TaskDecorator(AppTask task, AippLogService aippLogService, AppTaskInstanceService appTaskInstanceService, + LocaleService localeService) { + this.task = task; + this.proxy = task; + this.aippLogService = aippLogService; + this.appTaskInstanceService = appTaskInstanceService; + this.localeService = localeService; + } + + /** + * 创建装饰器. + * + * @param task 任务对象. + * @param aippLogService {@link AippLogService} 对象. + * @param appTaskInstanceService {@link AppTaskInstanceService} 对象. + * @param localeService {@link LocaleService} 对象. + * @return {@link TaskDecorator} 装饰器对象. + */ + public static TaskDecorator create(AppTask task, AippLogService aippLogService, + AppTaskInstanceService appTaskInstanceService, LocaleService localeService) { + return new TaskDecorator(task, aippLogService, appTaskInstanceService, localeService); + } + + /** + * 对instance的run接口进行装饰,为其添加chat相关能力. + * + * @return {@link TaskDecorator} 装饰器对象. + */ + public TaskDecorator exceptionLog() { + Object current = this.proxy; + Object newProxy = Proxy.newProxyInstance(current.getClass().getClassLoader(), + current.getClass().getInterfaces(), + (p, method, args) -> this.invokeMethod(method, args, current)); + this.proxy = ObjectUtils.cast(newProxy); + return this; + } + + private Object invokeMethod(Method method, Object[] args, Object current) + throws IllegalAccessException, InvocationTargetException { + if (method.getName().startsWith("run")) { + RunContext ctx = ObjectUtils.cast(args[0]); + try { + return method.invoke(current, args); + } catch (InvocationTargetException exception) { + log.error("Error occurs when run a task:", exception); + appTaskInstanceService.update( + AppTaskInstance.asUpdate(this.task.getEntity().getTaskId(), ctx.getTaskInstanceId()) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.ERROR.name()) + .build(), ctx.getOperationContext()); + String msg; + if (this.isPassThroughError(exception)) { + // 这边的 code 对应的报错信息没有 params 参数 + msg = localeService.localize(this.getPassThroughCode(exception)); + } + else { + msg = localeService.localize(UI_WORD_KEY); + } + this.aippLogService.insertLog(AippInstLogType.ERROR.name(), + AippLogData.builder().msg(msg).build(), ctx.getBusinessData()); + return null; + } + } + return method.invoke(current, args); + } + + private String getPassThroughCode(InvocationTargetException exception) { + return String.valueOf(ObjectUtils.cast(exception.getTargetException()).getCode()); + } + + private boolean isPassThroughError(InvocationTargetException exception) { + if (exception.getTargetException() == null || !(exception.getTargetException() instanceof AippException)) { + return false; + } + AippException aippException = ObjectUtils.cast(exception.getTargetException()); + return PASS_THROUGH_ERROR.contains(aippException.getCode()); + } + + @Override + public void run(RunContext context) { + Optional.ofNullable(this.proxy).orElse(this.task).run(context); + } + + @Override + public void run(RunContext context, ChatSession chatSession) { + Optional.ofNullable(this.proxy).orElse(this.task).run(context, chatSession); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDomainEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDomainEntity.java new file mode 100644 index 0000000000..671c705581 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskDomainEntity.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +/** + * 普通数据类,当Task作为领域对象时使用的entity类. + * + * @author 张越 + * @since 2025-01-14 + */ +public class TaskDomainEntity extends TaskEntity { + TaskDomainEntity() {} + + @Override + public TaskDomainEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskEntity.java new file mode 100644 index 0000000000..f4ead8cb08 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskEntity.java @@ -0,0 +1,614 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotBlank; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotNull; + +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.dto.AippCreateDto; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.dto.AippNodeForms; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.entity.consts.NodeTypes; +import modelengine.fit.jober.entity.task.TaskProperty; + +import lombok.Getter; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +/** + * 应用任务的数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public abstract class TaskEntity> { + private static final String DEFAULT_DESCRIPTION = "aipp 编排应用"; + + // 对应数据库表中的templateId,对应Meta中的id. + @Getter + private String appSuiteId; + + // 对应数据库表中的id,对应Meta中的versionId. + @Getter + private String taskId; + + @Getter + private String name; + + @Getter + private String category; + + @Getter + private String creator; + + @Getter + private String lastModifier; + + @Getter + private String tenant; + + @Getter + private String version; + + @Getter + private LocalDateTime creationTime; + + @Getter + private LocalDateTime lastModificationTime; + + private List properties; + + private Map attributes; + + TaskEntity() { + this.properties = new ArrayList<>(); + this.attributes = new HashMap<>(); + } + + /** + * 设置名称. + * + * @param name 名称. + * @return {@link TaskEntity} 对象. + */ + public T setName(String name) { + doIfNotBlank(name, v -> this.name = v); + return this.self(); + } + + /** + * 设置版本. + * + * @param version 版本. + * @return {@link TaskEntity} 对象. + */ + public T setVersion(String version) { + doIfNotBlank(version, v -> this.version = v); + return this.self(); + } + + /** + * 设置app的唯一标识. + * + * @param appSuiteId app的唯一标识. + * @return {@link TaskEntity} 对象. + */ + public T setAppSuiteId(String appSuiteId) { + this.appSuiteId = appSuiteId; + return this.self(); + } + + /** + * 设置分类. + * + * @param category 分类. + * @return {@link TaskEntity} 对象. + */ + public T setCategory(String category) { + this.category = category; + return this.self(); + } + + /** + * 设置aipp类型. + * + * @param aippType aipp类型. + * @return {@link TaskEntity} 对象. + */ + public T setAippType(String aippType) { + this.attributes.put(AippConst.ATTR_AIPP_TYPE_KEY, aippType); + return this.self(); + } + + /** + * 设置状态. + * + * @param status 状态码. + * @return {@link TaskEntity} 对象. + */ + public T setStatus(String status) { + this.attributes.put(AippConst.ATTR_META_STATUS_KEY, status); + return this.self(); + } + + /** + * 设置任务id. + * + * @param taskId 任务id. + * @return {@link TaskEntity} 对象. + */ + public T setTaskId(String taskId) { + doIfNotBlank(taskId, v -> this.taskId = v); + return this.self(); + } + + /** + * 设置应用版本id. + * + * @param appId 应用版本id. + * @return {@link TaskEntity} 对象. + */ + public T setAppId(String appId) { + doIfNotBlank(appId, v -> this.attributes.put(AippConst.ATTR_APP_ID_KEY, v)); + return self(); + } + + /** + * 设置任务创建时间. + * + * @param creationTime 创建时间. + * @return {@link TaskEntity} 对象. + */ + public T setCreationTime(LocalDateTime creationTime) { + doIfNotNull(creationTime, v -> this.creationTime = v); + return this.self(); + } + + /** + * 设置任务最后一次修改时间. + * + * @param lastModificationTime 最近一次修改时间. + * @return {@link TaskEntity} 对象. + */ + public T setLastModificationTime(LocalDateTime lastModificationTime) { + doIfNotNull(lastModificationTime, v -> this.lastModificationTime = v); + return this.self(); + } + + /** + * 设置创建人. + * + * @param creator 创建人. + * @return {@link TaskEntity} 对象. + */ + public T setCreator(String creator) { + doIfNotBlank(creator, v -> this.creator = v); + return this.self(); + } + + /** + * 设置描述. + * + * @param description 描述. + * @return {@link TaskEntity} 对象. + */ + public T setDescription(String description) { + if (StringUtils.isNotBlank(description)) { + this.attributes.put(AippConst.ATTR_DESCRIPTION_KEY, + Optional.ofNullable(description).orElse(DEFAULT_DESCRIPTION)); + } + return this.self(); + } + + /** + * 设置图标. + * + * @param icon 图标. + * @return {@link TaskEntity} 对象. + */ + public T setIcon(String icon) { + if (StringUtils.isNotBlank(icon)) { + this.attributes.put(AippConst.ATTR_META_ICON_KEY, icon); + } + return this.self(); + } + + /** + * 设置流程id. + * + * @param flowId 流程id. + * @return {@link TaskEntity} 对象. + */ + public T setFlowConfigId(String flowId) { + doIfNotNull(flowId, v -> this.attributes.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, v)); + return this.self(); + } + + /** + * 设置发布描述. + * + * @param publishDescription 发布描述. + * @return {@link TaskEntity} 对象. + */ + public T setPublishDescription(String publishDescription) { + doIfNotNull(publishDescription, v -> this.attributes.put(AippConst.ATTR_PUBLISH_DESCRIPTION, v)); + return this.self(); + } + + /** + * 设置发布日志. + * + * @param publishLog 发布日志. + * @return {@link TaskEntity} 对象. + */ + public T setPublishLog(String publishLog) { + doIfNotNull(publishLog, v -> this.attributes.put(AippConst.ATTR_PUBLISH_UPDATE_LOG, v)); + return this.self(); + } + + /** + * 设置工具store中的唯一标志. + * + * @param uniqueName 在工具中的唯一标识. + * @return {@link TaskEntity} 对象. + */ + public T setUniqueName(String uniqueName) { + doIfNotBlank(uniqueName, v -> this.attributes.put(AippConst.ATTR_UNIQUE_NAME, v)); + return this.self(); + } + + /** + * 设置属性中的版本号. + * + * @param version 版本号. + * @return {@link TaskEntity} 对象. + */ + public T setAttributeVersion(String version) { + doIfNotNull(version, v -> this.attributes.put(AippConst.ATTR_VERSION_KEY, v)); + return this.self(); + } + + /** + * 设置流程定义id. + * + * @param definitionId 流程定义id. + * @return {@link TaskEntity} 对象. + */ + public T setFlowDefinitionId(String definitionId) { + this.attributes.put(AippConst.ATTR_FLOW_DEF_ID_KEY, definitionId); + return this.self(); + } + + /** + * 设置发布时间. + * + * @param publishTime 发布时间. + * @return {@link TaskEntity} 对象. + */ + public T setPublishTime(String publishTime) { + this.attributes.put(AippConst.ATTR_PUBLISH_TIME_KEY, publishTime); + return this.self(); + } + + /** + * 设置基线版本. + * + * @param baseLineVersion 基线版本. + * @return {@link TaskEntity} 对象. + */ + public T setBaseLineVersion(String baseLineVersion) { + this.attributes.put(AippConst.ATTR_BASELINE_VERSION_KEY, baseLineVersion); + return this.self(); + } + + /** + * 从 {@link AippDto} 中提取数据. + * + * @param args {@link AippDto} 对象. + * @return {@link TaskEntity} 对象. + */ + public T fetch(AippDto args) { + Optional.ofNullable(args).ifPresent(a -> { + this.name = a.getName(); + this.version = a.getVersion(); + this.setDescription(a.getDescription()); + this.attributes.put(AippConst.ATTR_META_ICON_KEY, a.getIcon()); + this.attributes.put(AippConst.ATTR_APP_ID_KEY, a.getAppId()); + }); + return this.self(); + } + + /** + * 从 {@link AippCreateDto} 中提取数据. + * + * @param baseline {@link AippCreateDto} 对象. + * @return {@link TaskEntity} 对象. + */ + public T fetch(AippCreateDto baseline) { + Optional.ofNullable(baseline) + .ifPresent(b -> this.setBaseLineVersion(b.getVersion()).setAppSuiteId(b.getAippId())); + return this.self(); + } + + /** + * 从流程视图中提取数据. + * + * @param flowView 流程视图. + * @return {@link TaskEntity} 对象. + */ + public T fetch(Map flowView) { + Optional.ofNullable(flowView).ifPresent(fv -> { + this.setFlowConfigId(ObjectUtils.cast(fv.get(AippConst.FLOW_CONFIG_ID_KEY))); + this.setAttributeVersion(ObjectUtils.cast(fv.get(AippConst.FLOW_CONFIG_VERSION_KEY))); + }); + return this.self(); + } + + /** + * 从 nodeForms 中提取数据. + * + * @param nodeForms 节点表单数据. + * @return {@link TaskEntity} 对象. + */ + public T fetch(List nodeForms) { + for (AippNodeForms node : nodeForms) { + if (node.getMetaInfo().isEmpty()) { + continue; + } + if (NodeTypes.START.getType().equalsIgnoreCase(node.getType())) { + this.attributes.put(AippConst.ATTR_START_FORM_ID_KEY, node.getMetaInfo().get(0).getFormId()); + this.attributes.put(AippConst.ATTR_START_FORM_VERSION_KEY, node.getMetaInfo().get(0).getVersion()); + } + if (NodeTypes.END.getType().equalsIgnoreCase(node.getType())) { + this.attributes.put(AippConst.ATTR_END_FORM_ID_KEY, node.getMetaInfo().get(0).getFormId()); + this.attributes.put(AippConst.ATTR_END_FORM_VERSION_KEY, node.getMetaInfo().get(0).getVersion()); + } + } + return this.self(); + } + + /** + * 访问所有的attributes数据. + * + * @param consumer 消费者. + */ + public void visitAttributes(BiConsumer consumer) { + this.attributes.forEach(consumer); + } + + /** + * 设置属性列表. + * + * @param properties 属性列表. + * @return {@link TaskEntity} 对象. + */ + public T setProperties(List properties) { + this.properties = properties; + return this.self(); + } + + /** + * 从 {@link Meta} 中加载数据. + * + * @param meta {@link Meta} 对象. + */ + public void loadFrom(Meta meta) { + this.appSuiteId = meta.getId(); + this.taskId = meta.getVersionId(); + this.name = meta.getName(); + this.category = meta.getCategory(); + this.creator = meta.getCreator(); + this.lastModifier = meta.getLastModifier(); + this.tenant = meta.getTenant(); + this.version = meta.getVersion(); + this.creationTime = meta.getCreationTime(); + this.lastModificationTime = meta.getLastModificationTime(); + this.properties = meta.getProperties(); + this.attributes = meta.getAttributes(); + } + + @Override + public TaskEntity clone() throws CloneNotSupportedException { + return ObjectUtils.cast(super.clone()); + } + + /** + * 构建 {@link AppTask} 对象. + * + * @return {@link AppTask} 对象. + */ + public AppTask build() { + return new AppTask(this); + } + + /** + * 获取发布日志. + * + * @return {@link String} 发布日志. + */ + public String getPublishLog() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_PUBLISH_UPDATE_LOG)); + } + + /** + * 获取发布描述信息. + * + * @return {@link String} 发布描述. + */ + public String getPublishDescription() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_PUBLISH_DESCRIPTION)); + } + + /** + * 获取图标. + * + * @return 图标. + */ + public String getIcon() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_META_ICON_KEY)); + } + + /** + * 获取描述. + * + * @return 描述. + */ + public String getDescription() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_DESCRIPTION_KEY)); + } + + /** + * 获取状态. + * + * @return 状态. + */ + public String getStatus() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_META_STATUS_KEY)); + } + + /** + * 获取发布时间. + * + * @return 发布时间. + */ + public String getPublishTime() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_PUBLISH_TIME_KEY)); + } + + /** + * 获取流程定义id. + * + * @return 流程定义id. + */ + public String getFlowDefinitionId() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_FLOW_DEF_ID_KEY)); + } + + /** + * 获取流程配置id. + * + * @return 流程配置id. + */ + public String getFlowConfigId() { + // flow_graph的id. + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)); + } + + /** + * 获取属性中的版本. + * + * @return 版本. + */ + public String getAttributeVersion() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_VERSION_KEY)); + } + + /** + * 获取store中对应的唯一标识. + * + * @return store中的唯一标识. + */ + public String getUniqueName() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_UNIQUE_NAME)); + } + + /** + * 获取应用id. + * + * @return 应用id. + */ + public String getAppId() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_APP_ID_KEY)); + } + + /** + * 获取应用类型. + * + * @return 应用类型. + */ + public String getAippType() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_AIPP_TYPE_KEY)); + } + + /** + * 获取基线版本. + * + * @return 基线版本. + */ + public String getBaseLineVersion() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_BASELINE_VERSION_KEY)); + } + + /** + * 获取开始表单唯一标识. + * + * @return {@link String} 对象. + */ + public String getStartFormId() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_START_FORM_ID_KEY)); + } + + /** + * 获取开始表单版本. + * + * @return {@link String} 对象. + */ + public String getStartFormVersion() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_START_FORM_VERSION_KEY)); + } + + /** + * 获取结束表单唯一标识. + * + * @return {@link String} 对象. + */ + public String getEndFormId() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_END_FORM_ID_KEY)); + } + + /** + * 获取结束表单版本. + * + * @return {@link String} 对象. + */ + public String getEndFormVersion() { + return ObjectUtils.cast(this.attributes.get(AippConst.ATTR_END_FORM_VERSION_KEY)); + } + + /** + * 获取属性. + * + * @return {@link List}{@code <}{@link TaskProperty}{@code >} 列表. + */ + public List getProperties() { + return Collections.unmodifiableList(this.properties); + } + + @Override + public String toString() { + return JsonUtils.toJsonString(this.self()); + } + + /** + * 返回自身. + * + * @return 当前的对象. + */ + public abstract T self(); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskQueryEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskQueryEntity.java new file mode 100644 index 0000000000..843efa9b9e --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/TaskQueryEntity.java @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNotBlank; +import static modelengine.fitframework.util.ObjectUtils.nullIf; + +import modelengine.fit.jane.common.enums.DirectionEnum; +import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; +import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; + +import lombok.Getter; +import modelengine.fitframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * 应用任务的查询数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public class TaskQueryEntity extends TaskEntity { + private final List appSuiteIds; + private final List taskIds; + private final List names; + private final List categories; + private final List creators; + private final List orderBys; + private final List versions; + private final Map> queryAttributes; + + @Getter + private final long offset; + + @Getter + private final int limit; + + @Getter + private boolean isLatest; + + TaskQueryEntity(long offset, int limit) { + this.offset = offset; + this.limit = limit; + this.appSuiteIds = new ArrayList<>(); + this.taskIds = new ArrayList<>(); + this.names = new ArrayList<>(); + this.categories = new ArrayList<>(); + this.creators = new ArrayList<>(); + this.orderBys = new ArrayList<>(); + this.versions = new ArrayList<>(); + this.queryAttributes = new HashMap<>(); + this.isLatest = false; + } + + /** + * 添加应用唯一标识. + * + * @param appSuiteId 应用唯一标识. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addAppSuiteId(String appSuiteId) { + doIfNotBlank(appSuiteId, this.appSuiteIds::add); + return this.self(); + } + + /** + * 批量添加应用唯一标识. + * + * @param appSuiteIds 应用唯一标识集合. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addAppSuiteIds(List appSuiteIds) { + if (CollectionUtils.isNotEmpty(appSuiteIds)) { + this.appSuiteIds.addAll(appSuiteIds); + } + return this.self(); + } + + /** + * 添加名称. + * + * @param name 名称. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addName(String name) { + doIfNotBlank(name, this.names::add); + return this.self(); + } + + /** + * 添加分类. + * + * @param category 分类. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addCategory(String category) { + doIfNotBlank(category, this.categories::add); + return this.self(); + } + + /** + * 添加创建者. + * + * @param creator 创建者. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addCreator(String creator) { + doIfNotBlank(creator, this.creators::add); + return this.self(); + } + + /** + * 添加版本号. + * + * @param version 版本号. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addVersion(String version) { + doIfNotBlank(version, this.versions::add); + return this.self(); + } + + /** + * 设置属性. + * + * @param key 属性的键. + * @param value 属性的值. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity putQueryAttribute(String key, String value) { + doIfNotBlank(key, k -> { + List values = this.queryAttributes.computeIfAbsent(key, (kk) -> new ArrayList<>()); + doIfNotBlank(value, values::add); + }); + return this.self(); + } + + /** + * 是否查询最新数据. + * + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity latest() { + this.isLatest = true; + return this.self(); + } + + /** + * 转换为 {@link MetaFilter} 对象. + * + * @return {@link MetaFilter} 对象. + */ + public MetaFilter toMetaFilter() { + return new MetaFilter(this.appSuiteIds, this.taskIds, this.names, this.categories, this.creators, + this.orderBys, this.versions, this.queryAttributes); + } + + /** + * 添加默认排序规则. + * + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addOrderBy() { + return this.addOrderBy(null, null); + } + + /** + * 添加排序规则. + * + * @param sort 排序字段. + * @param order 顺序. + * @return {@link TaskQueryEntity} 对象. + */ + public TaskQueryEntity addOrderBy(String sort, String order) { + String orderBy = String.format(Locale.ROOT, "%s(%s)", + DirectionEnum.getDirection(nullIf(order, DirectionEnum.DESCEND.name())).getValue(), + AippSortKeyEnum.getSortKey(nullIf(sort, AippSortKeyEnum.UPDATE_AT.name())).getKey()); + this.orderBys.add(orderBy); + return this.self(); + } + + @Override + public TaskQueryEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/AppTaskService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/AppTaskService.java new file mode 100644 index 0000000000..ccb32429e7 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/AppTaskService.java @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task.service; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.common.RangedResultSet; + +import java.util.List; +import java.util.Optional; + +/** + * 应用任务服务. + * + * @author 张越 + * @since 2025-01-03 + */ +public interface AppTaskService { + /** + * 创建应用任务. + * + * @param task 任务创建参数. + * @param context 操作人上下文信息. + * @return {@link AppTask} 对象. + */ + AppTask createTask(AppTask task, OperationContext context); + + /** + * 修改应用任务数据. + * + * @param task 任务对象. + * @param context 操作人上下文信息. + */ + void updateTask(AppTask task, OperationContext context); + + /** + * 通过id删除应用任务. + * + * @param taskId 应用任务id. + * @param context 操作人上下文信息. + */ + void deleteTaskById(String taskId, OperationContext context); + + /** + * 获分页查询指定应用的已发布元数据列表,按更新时间倒序。 + * + * @param appSuiteId 应用的唯一标识的 {@link String} + * @param offset 表示偏移量的 {@code long}。 + * @param limit 表示单页最大数量的 {@code int}。 + * @param context 表示操作人上下文的 {@link OperationContext}。 + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getPublishedByPage(String appSuiteId, long offset, int limit, OperationContext context); + + /** + * 按条件获取最新的被创建出来的应用任务. + * + * @param appSuiteId 应用的唯一标识. + * @param aippType aipp类型. + * @param status 状态. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getLatestCreate(String appSuiteId, String aippType, String status, OperationContext context); + + /** + * 按条件获取最新的被创建出来的应用任务. + * + * @param appSuiteId 应用的唯一标识. + * @param aippType aipp类型. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getLatestCreate(String appSuiteId, String aippType, OperationContext context); + + /** + * 通过uniqueName获取应用任务. + * + * @param uniqueName 任务在store中的唯一标识. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getLatest(String uniqueName, OperationContext context); + + /** + * 根据应用id,版本号获取最新被修改的第一个数据. + * + * @param appSuiteId 应用的唯一标识. + * @param version 版本号. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getLatest(String appSuiteId, String version, OperationContext context); + + /** + * 根据条件,查询最新的第一个数据. + * + * @param query 查询参数. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getLatest(AppTask query, OperationContext context); + + /** + * 获取最新的任务. + * + * @param query 查询参数. + * @param context 操作人上下文信息. + * @return {@link RangedResultSet}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + RangedResultSet getTasks(AppTask query, OperationContext context); + + /** + * 按查询条件获取最新创建的所有任务. + * + * @param appSuiteId 应用的唯一标识. + * @param aippType aipp类型. + * @param status 状态. + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getTaskList(String appSuiteId, String aippType, String status, OperationContext ctx); + + /** + * 按查询条件获取最新创建的所有任务. + * + * @param query 查询参数. + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getTaskList(AppTask query, OperationContext ctx); + + /** + * 获取preview类型的任务. + * + * @param appSuiteId 应用的唯一标识. + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getPreviewTasks(String appSuiteId, OperationContext ctx); + + /** + * 按查询条件获取最新创建的所有任务,并按创建时间排序 + * + * @param appId 应用版本的唯一标识. + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getTasksByAppId(String appId, OperationContext ctx); + + /** + * 按查询条件获取最新创建的所有任务. + * + * @param appId 应用版本的唯一标识. + * @param aippType aipp类型. + * @param ctx 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTask}{@code >} 应用任务列表. + */ + List getTasksByAppId(String appId, String aippType, OperationContext ctx); + + /** + * 通过任务id获取任务. + * + * @param taskId 任务id. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTask}{@code >} 应用任务. + */ + Optional getTaskById(String taskId, OperationContext context); + + /** + * 获取任务,如果不存在,则抛出TASK_NOT_FOUND异常. + * + * @param taskId 任务id. + * @param context 操作人上下文. + * @return {@link AppTask} 任务. + */ + AppTask retrieveById(String taskId, OperationContext context); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/impl/AppTaskServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/impl/AppTaskServiceImpl.java new file mode 100644 index 0000000000..34ebbd5375 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/task/service/impl/AppTaskServiceImpl.java @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task.service.impl; + +import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_UNIQUE_NAME; +import static modelengine.fit.jober.aipp.util.MetaUtils.getAllFromRangedResult; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.common.enums.DirectionEnum; +import modelengine.fit.jane.meta.multiversion.MetaService; +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jane.meta.multiversion.definition.MetaDeclarationInfo; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.TaskQueryEntity; +import modelengine.fit.jober.aipp.domains.task.AppTaskFactory; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.enums.JaneCategory; +import modelengine.fit.jober.common.RangeResult; +import modelengine.fit.jober.common.RangedResultSet; + +import lombok.RequiredArgsConstructor; +import modelengine.fitframework.annotation.Component; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 应用任务服务实现类. + * + * @author 张越 + * @since 2025-01-03 + */ +@Component +@RequiredArgsConstructor +public class AppTaskServiceImpl implements AppTaskService { + private static final int DEFAULT_LIMIT = 10; + + private final MetaService metaService; + private final AppTaskFactory factory; + + @Override + public AppTask createTask(AppTask task, OperationContext context) { + MetaDeclarationInfo declaration = this.factory.toMetaDeclaration(task); + Meta meta = this.metaService.create(declaration, context); + return this.factory.create(meta, this); + } + + @Override + public void updateTask(AppTask task, OperationContext context) { + this.metaService.patch(task.getEntity().getTaskId(), this.factory.toMetaDeclaration(task), context); + } + + @Override + public void deleteTaskById(String taskId, OperationContext context) { + this.metaService.delete(taskId, context); + } + + @Override + public List getPublishedByPage(String appSuiteId, long offset, int limit, OperationContext context) { + AppTask task = AppTask.asQueryEntity(offset, limit) + .addAppSuiteId(appSuiteId) + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.NORMAL.type()) + .putQueryAttribute(AippConst.ATTR_META_STATUS_KEY, AppState.PUBLISHED.getName()) + .addOrderBy(AippSortKeyEnum.UPDATE_AT.name(), DirectionEnum.DESCEND.name()) + .build(); + return this.list(task, context) + .getResults() + .stream() + .map(r -> this.factory.create(r, this)) + .collect(Collectors.toList()); + } + + @Override + public Optional getLatestCreate(String appSuiteId, String aippType, String status, OperationContext ctx) { + return this.getLatest(AppTask.asQueryEntity(0, 1) + .latest() + .addAppSuiteId(appSuiteId) + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, aippType) + .putQueryAttribute(AippConst.ATTR_META_STATUS_KEY, status) + .addOrderBy(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()) + .addCategory(JaneCategory.AIPP.name()) + .build(), ctx); + } + + @Override + public Optional getLatestCreate(String appSuiteId, String aippType, OperationContext ctx) { + return this.getLatestCreate(appSuiteId, aippType, null, ctx); + } + + @Override + public Optional getLatest(String uniqueName, OperationContext context) { + RangedResultSet resultSet = this.getTasks( + AppTask.asQueryEntity(0, 1).latest().putQueryAttribute(ATTR_UNIQUE_NAME, uniqueName).build(), + context); + return resultSet.getFirst(); + } + + @Override + public Optional getLatest(String appSuiteId, String version, OperationContext context) { + return this.getLatest(AppTask.asQueryEntity(0, 1) + .latest() + .addAppSuiteId(appSuiteId) + .addVersion(version) + .addOrderBy() + .addCategory(JaneCategory.AIPP.name()) + .build(), context); + } + + @Override + public Optional getLatest(AppTask query, OperationContext context) { + RangedResultSet resultSet = this.getTasks(query, context); + return resultSet.getFirst(); + } + + @Override + public RangedResultSet getTasks(AppTask query, OperationContext context) { + RangedResultSet resultSet = this.list(query, context); + List tasks = resultSet.getResults().stream().map(r -> this.factory.create(r, this)).toList(); + RangeResult range = resultSet.getRange(); + return RangedResultSet.create(tasks, range.getOffset(), range.getLimit(), range.getTotal()); + } + + @Override + public List getTaskList(String appSuiteId, String aippType, String status, OperationContext ctx) { + return getAllFromRangedResult(DEFAULT_LIMIT, (offset) -> { + AppTask task = AppTask.asQueryEntity(offset, DEFAULT_LIMIT) + .addAppSuiteId(appSuiteId) + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, aippType) + .putQueryAttribute(AippConst.ATTR_META_STATUS_KEY, status) + .addOrderBy(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()) + .addCategory(JaneCategory.AIPP.name()) + .build(); + return this.list(task, ctx); + }).map(r -> this.factory.create(r, this)).collect(Collectors.toList()); + } + + @Override + public List getTaskList(AppTask query, OperationContext ctx) { + TaskQueryEntity entity = query.getEntity(); + Function> function = (offset) -> this.metaService.list(entity.toMetaFilter(), + entity.isLatest(), offset, DEFAULT_LIMIT, ctx); + return getAllFromRangedResult(DEFAULT_LIMIT, function).map(r -> this.factory.create(r, this)) + .collect(Collectors.toList()); + } + + @Override + public List getPreviewTasks(String appSuiteId, OperationContext ctx) { + return getAllFromRangedResult(DEFAULT_LIMIT, (offset) -> { + AppTask task = AppTask.asQueryEntity(offset, DEFAULT_LIMIT) + .addAppSuiteId(appSuiteId) + .addOrderBy() + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.PREVIEW.name()) + .addCategory(JaneCategory.AIPP.name()) + .build(); + return this.list(task, ctx); + }).map(r -> this.factory.create(r, this)).collect(Collectors.toList()); + } + + @Override + public List getTasksByAppId(String appId, OperationContext ctx) { + return getAllFromRangedResult(DEFAULT_LIMIT, (offset) -> { + AppTask task = AppTask.asQueryEntity(offset, DEFAULT_LIMIT) + .putQueryAttribute(AippConst.ATTR_APP_ID_KEY, appId) + .addOrderBy(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()) + .build(); + return this.list(task, ctx); + }).map(r -> this.factory.create(r, this)).collect(Collectors.toList()); + } + + @Override + public List getTasksByAppId(String appId, String aippType, OperationContext ctx) { + return getAllFromRangedResult(DEFAULT_LIMIT, (offset) -> { + AppTask task = AppTask.asQueryEntity(offset, DEFAULT_LIMIT) + .putQueryAttribute(AippConst.ATTR_APP_ID_KEY, appId) + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.getType(aippType).type()) + .build(); + return this.list(task, ctx); + }).map(r -> this.factory.create(r, this)).collect(Collectors.toList()); + } + + private RangedResultSet list(AppTask query, OperationContext context) { + TaskQueryEntity queryEntity = query.getEntity(); + return this.metaService.list(queryEntity.toMetaFilter(), queryEntity.isLatest(), queryEntity.getOffset(), + queryEntity.getLimit(), context); + } + + @Override + public Optional getTaskById(String taskId, OperationContext context) { + return Optional.ofNullable(this.metaService.retrieve(taskId, context)).map(r -> this.factory.create(r, this)); + } + + @Override + public AppTask retrieveById(String taskId, OperationContext context) { + return this.getTaskById(taskId, context) + .orElseThrow(() -> new AippException(AippErrCode.TASK_NOT_FOUND, taskId)); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstance.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstance.java new file mode 100644 index 0000000000..2ddfdc3313 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstance.java @@ -0,0 +1,437 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.util.UsefulUtils.lazyGet; + +import lombok.Getter; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.AppTaskRunnable; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.business.MemoryConfig; +import modelengine.fit.jober.aipp.domains.business.MemoryGetter; +import modelengine.fit.jober.aipp.domains.business.MemoryTypeEnum; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.MemoryConfigDto; +import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.service.AppChatSseService; +import modelengine.fit.jober.aipp.util.AippLogUtils; +import modelengine.fit.jober.common.exceptions.JobberException; +import modelengine.fit.jober.entity.FlowInstanceResult; +import modelengine.fit.waterflow.entity.FlowStartInfo; +import modelengine.fitframework.broker.client.BrokerClient; +import modelengine.fitframework.broker.client.filter.route.FitableIdFilter; +import modelengine.fitframework.exception.FitException; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 应用任务实例领域对象. + * + * @author 张越 + * @since 2024-12-31 + */ +public class AppTaskInstance implements AppTaskRunnable { + /** + * 用户自定义日志接口id. + */ + public static final String GENERICABLE_ID = "68dc66a6185cf64c801e55c97fc500e4"; + private static final Logger log = Logger.get(AppTaskInstance.class); + private final TaskInstanceEntity entity; + + @Getter + private String id; + + @Getter + private String taskId; + + private String parentInstanceId; + private List chatRspList; + private List logs; + private List allLogs; + private AppChatSseService appChatSSEService; + private AppTaskInstanceService appTaskInstanceService; + private FlowInstanceService flowInstanceService; + private BrokerClient client; + private AippChatMapper aippChatMapper; + private AippLogRepository aippLogRepository; + + AppTaskInstance(AppTaskInstanceService appTaskInstanceService, + FlowInstanceService flowInstanceService, BrokerClient client, + AppChatSseService appChatSSEService, AippChatMapper aippChatMapper, AippLogRepository aippLogRepository) { + this.entity = new TaskInstanceDomainEntity(); + this.appTaskInstanceService = appTaskInstanceService; + this.flowInstanceService = flowInstanceService; + this.client = client; + this.appChatSSEService = appChatSSEService; + this.aippChatMapper = aippChatMapper; + this.aippLogRepository = aippLogRepository; + } + + AppTaskInstance(TaskInstanceEntity entity) { + this.entity = entity; + } + + /** + * 作为数据对象. + * + * @return {@link TaskInstanceEntity} 对象. + */ + public static TaskInstanceDomainEntity asEntity() { + return new TaskInstanceDomainEntity(); + } + + /** + * 作为创建时的数据对象. + * + * @param taskId 任务id. + * @param creator 创建者. + * @param name 实例名称. + * @return {@link TaskInstanceCreateEntity} 对象. + */ + public static TaskInstanceCreateEntity asCreate(String taskId, String creator, String name) { + return new TaskInstanceCreateEntity(taskId, creator, name); + } + + /** + * 作为修改时的数据对象. + * + * @param taskId 任务id. + * @param instanceId 实例id. + * @return {@link TaskInstanceUpdateEntity} 对象. + */ + public static TaskInstanceUpdateEntity asUpdate(String taskId, String instanceId) { + return new TaskInstanceUpdateEntity(taskId, instanceId); + } + + /** + * 作为查询时的数据对象. + * + * @param order 排序参数. + * @param sort 排序顺序. + * @return {@link TaskInstanceQueryEntity} 对象. + */ + public static TaskInstanceQueryEntity asQuery(String order, String sort) { + return new TaskInstanceQueryEntity().setOrder(order).setSort(sort); + } + + /** + * 将一个 {@link AppTaskInstance} 转换为一个 {@link AippInstLogDataDto}. + * + * @param instance 实例对象. + * @return {@link AippInstLogDataDto} 的 {@link Optional} 对象. + */ + public static Optional toLogDataDto(AppTaskInstance instance) { + List logs = instance.getAllLogs() + .stream() + .filter(l -> !l.is(AippInstLogType.HIDDEN_MSG, AippInstLogType.HIDDEN_FORM)) + .toList(); + if (CollectionUtils.isEmpty(logs)) { + return Optional.empty(); + } + return Optional.of(new AippInstLogDataDto(instance, logs)); + } + + /** + * 将entity转换为特定的Entity类型对象. + * + * @param 代表Entity的类型. + * @return 特定的 {@link TaskInstanceEntity} 类型. + */ + public > T getEntity() { + return ObjectUtils.cast(this.entity); + } + + @Override + public void run(RunContext ctx) { + this.run(ctx, null); + } + + @Override + public void run(RunContext ctx, ChatSession chatSession) { + AppTask task = ctx.getAppTask(); + if (task == null) { + throw new AippException(AippErrCode.TASK_NOT_FOUND, ctx.getTaskId()); + } + String flowDefinitionId = task.getEntity().getFlowDefinitionId(); + if (ctx.shouldUseMemory()) { + if (ctx.isUserCustomMemory()) { + Optional.ofNullable(chatSession).ifPresent(c -> { + MemoryConfigDto dto = MemoryConfigDto.builder() + .initContext(ctx.getBusinessData()) + .instanceId(this.getId()) + .memory(MemoryTypeEnum.USER_SELECT.type()) + .build(); + this.appChatSSEService.sendToAncestorLastData(this.getId(), dto); + }); + } else { + ctx.setMemories(this.getMemories(ctx, task)); + this.start(ctx, flowDefinitionId); + } + } else { + if (!ctx.isUserCustomMemory()) { + ctx.clearMemories(); + } + this.start(ctx, flowDefinitionId); + } + } + + private void start(RunContext ctx, String flowDefinitionId) { + FlowInstanceResult flowInstance = this.startFlow(ctx, flowDefinitionId); + + // 记录流程实例id到meta实例 + // 修改taskInstance. + this.appTaskInstanceService.update( + AppTaskInstance.asUpdate(this.taskId, this.id).setFlowTraceId(flowInstance.getId()).build(), + ctx.getOperationContext()); + } + + private FlowInstanceResult startFlow(RunContext ctx, String flowDefinitionId) { + try { + return this.flowInstanceService.startFlow(flowDefinitionId, + new FlowStartInfo(ctx.getOperationContext().getOperator(), null, ctx.getBusinessData()), + ctx.getOperationContext()); + } catch (JobberException e) { + log.error("start flow failed, flowDefinitionId: {}", flowDefinitionId); + throw new AippException(ctx.getOperationContext(), AippErrCode.APP_CHAT_WAIT_RESPONSE_ERROR); + } + } + + private List> getMemories(RunContext ctx, AppTask task) { + MemoryConfig memoryConfig = ctx.getMemoryConfig(); + MemoryGetter memoryGetter = new MemoryGetter(memoryConfig); + memoryGetter.register(MemoryTypeEnum.BY_CONVERSATION_TURN, (v) -> this.getConversationTurns(v, ctx)); + memoryGetter.register(MemoryTypeEnum.NOT_USE_MEMORY, (v) -> this.getNotUserMemory(ctx)); + memoryGetter.register(MemoryTypeEnum.CUSTOMIZING, (v) -> this.getCustomizedLogs(v, task, ctx)); + return memoryGetter.get(); + } + + private List> getCustomizedLogs(Object value, AppTask task, RunContext ctx) { + // 如何定义这个genericable接口,入参为一个map? + String fitableId = ObjectUtils.cast(value); + + // 目前flow graph中并没有params的配置,暂时用一个空map + Map params = new HashMap<>(); + String appSuiteId = task.getEntity().getAppSuiteId(); + String aippType = Optional.ofNullable(task.getEntity().getAippType()).orElse(NORMAL.name()); + if (fitableId == null) { + log.warn("no fitable id in customized log selection."); + return Collections.emptyList(); + } + try { + return this.client.getRouter(GENERICABLE_ID) + .route(new FitableIdFilter(fitableId)) + .invoke(params, appSuiteId, aippType, ctx.getOperationContext()); + } catch (FitException t) { + log.error("Error occurred when get history logs, error: {}", t.getMessage()); + throw new AippException(AippErrCode.GET_HISTORY_LOG_FAILED); + } + } + + private List> getNotUserMemory(RunContext ctx) { + ctx.setUseMemory(false); + return new ArrayList<>(); + } + + private List> getConversationTurns(Object value, RunContext ctx) { + Integer count = Integer.parseInt(ObjectUtils.cast(value)); + String memoryChatId = ctx.getOriginChatId(); + return Optional.ofNullable(memoryChatId).map(cid -> { + OperationContext operationContext = ctx.getOperationContext(); + List instanceIds = this.aippChatMapper.selectInstanceByChat(cid, count); + List instances = instanceIds.stream() + .map(id -> this.appTaskInstanceService.getInstance(this.taskId, id, operationContext)) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + return instances.stream() + .map(AppTaskInstance::toLogDataDto) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(Comparator.comparing(AippInstLogDataDto::getCreateAt)) + .map(AippInstLogDataDto::toMemory) + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + }).orElseGet(ArrayList::new); + } + + /** + * 实例是否处于运行状态. + * + * @return true/false. + */ + public boolean isRunning() { + return this.getEntity() + .getStatus() + .map(status -> MetaInstStatusEnum.getMetaInstStatus(status) == MetaInstStatusEnum.RUNNING) + .orElse(false); + } + + /** + * 判断实例是否处于传入的多个状态中的其中一个. + * + * @param statusEnums 多个状态. + * @return true/false. + */ + public boolean is(MetaInstStatusEnum... statusEnums) { + Optional statusOp = this.entity.getStatus(); + if (statusOp.isEmpty()) { + return false; + } + int value = MetaInstStatusEnum.getMetaInstStatus(statusOp.get()).getValue(); + return Arrays.stream(statusEnums).anyMatch(e -> value == e.getValue()); + } + + /** + * 设置任务id. + * + * @param taskId 任务id. + */ + public void setTaskId(String taskId) { + this.taskId = taskId; + this.getEntity().setTaskId(taskId); + } + + /** + * 设置以为标识. + * + * @param id 唯一标识. + */ + public void setId(String id) { + this.id = id; + this.getEntity().setInstanceId(id); + } + + /** + * 获取父流程id,若没有父流程,则返回自己. + * + * @return {@link String} 父流程id. + */ + public String getParentInstanceId() { + return lazyGet(this.parentInstanceId, () -> { + String path = this.aippLogRepository.getParentPath(this.id); + return StringUtils.isNotEmpty(path) ? path.split(AippLogUtils.PATH_DELIMITER)[1] : this.id; + }, (t) -> this.parentInstanceId = t); + } + + /** + * 获取全路径. + * + * @param context 操作人上下文信息. + * @return 全路径. + */ + public String getPath(OperationContext context) { + String parentId = this.getParentInstanceId(); + if (StringUtils.equals(parentId, this.id)) { + return AippLogUtils.PATH_DELIMITER + this.getId(); + } else { + String parentPath = this.getParent(context).map(ti -> ti.getPath(context)).orElse(null); + return StringUtils.isEmpty(parentPath) + ? AippLogUtils.PATH_DELIMITER + this.getId() + : String.join(AippLogUtils.PATH_DELIMITER, parentPath, this.id); + } + } + + /** + * 获取父实例. + * + * @param context 操作人上下文信息. + * @return 父实例的 {@link Optional} 对象. + */ + public Optional getParent(OperationContext context) { + String parentId = this.getParentInstanceId(); + if (StringUtils.equals(parentId, this.id)) { + return Optional.empty(); + } + return this.appTaskInstanceService.getInstance(this.taskId, parentId, context); + } + + /** + * 获取会话列表. + * + * @return {@link List}{@code <}{@link QueryChatRsp}{@code >} 会话列表. + */ + public List getChats() { + return lazyGet(this.chatRspList, this::loadChats, (chats) -> this.chatRspList = chats); + } + + private List loadChats() { + List chatIds = this.aippChatMapper.selectChatIdByInstanceId(this.getParentInstanceId()); + if (chatIds.isEmpty()) { + return Collections.emptyList(); + } + return this.aippChatMapper.selectChatListByChatIds(chatIds); + } + + /** + * 获取实例日志列表. + * + * @return {@link List}{@code <}{@link AppLog}{@code >} 会话列表. + */ + public List getLogs() { + return lazyGet(this.logs, () -> this.aippLogRepository.selectByInstanceIdAndLogTypes(this.getParentInstanceId(), + Arrays.asList(AippInstLogType.QUESTION.name(), AippInstLogType.HIDDEN_QUESTION.name())), + (logs) -> this.logs = logs); + } + + /** + * 获取所有日志,包含当前实例的子实例的日志. + * + * @return 日志列表. + */ + public List getAllLogs() { + return lazyGet(this.allLogs, () -> this.aippLogRepository.selectAllLogsByInstanceId(this.getParentInstanceId()), + (logs) -> this.allLogs = logs); + } + + /** + * 覆盖写会话或日志. + */ + public void overWrite() { + String parentId = this.getParentInstanceId(); + this.aippChatMapper.deleteWideRelationshipByInstanceId(parentId); + this.aippLogRepository.deleteByInstanceId(parentId); + } + + /** + * 恢复执行. + * + * @param flowDefinitionId 流程定义id. + * @param formArgs 表单参数. + * @param context 操作人上下文信息. + */ + public void resume(String flowDefinitionId, Map formArgs, OperationContext context) { + String flowTraceId = this.getEntity().getFlowTranceId(); + Validation.notNull(flowTraceId, "flowTraceId can not be null"); + this.flowInstanceService.resumeFlow(flowDefinitionId, flowTraceId, formArgs, context); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactory.java new file mode 100644 index 0000000000..d66f4a3571 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactory.java @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.service.AppChatSseService; + +import lombok.RequiredArgsConstructor; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.broker.client.BrokerClient; + +/** + * {@link AppTaskInstance} 的工厂类 + * + * @author 张越 + * @since 2024-12-31 + */ +@Component +@RequiredArgsConstructor +public class AppTaskInstanceFactory { + private final FlowInstanceService flowInstanceService; + private final BrokerClient client; + private final AppChatSseService appChatSSEService; + private final AippChatMapper aippChatMapper; + private final AippLogRepository aippLogRepository; + + /** + * 将 {@link AppTaskInstance} 转换为 {@link InstanceDeclarationInfo} 对象. + * + * @param taskInstance {@link AppTaskInstance} 对象. + * @return {@link InstanceDeclarationInfo} 对象. + */ + public InstanceDeclarationInfo toDeclarationInfo(AppTaskInstance taskInstance) { + return InstanceDeclarationInfo.custom() + .info(taskInstance.getEntity().getInfos()) + .tags(taskInstance.getEntity().getTags()) + .build(); + } + + /** + * 通过 {@link Instance} 和任务id创建一个实例对象. + * + * @param instance 任务实例对象. + * @param taskId 任务id. + * @param appTaskInstanceService 任务实例服务类. + * @return {@link AppTaskInstance} 对象. + */ + public AppTaskInstance create(Instance instance, String taskId, AppTaskInstanceService appTaskInstanceService) { + AppTaskInstance appTaskInstance = new AppTaskInstance(appTaskInstanceService, + this.flowInstanceService, this.client, this.appChatSSEService, this.aippChatMapper, + this.aippLogRepository); + appTaskInstance.getEntity().putInfos(instance.getInfo()).putTags(instance.getTags()); + appTaskInstance.setTaskId(taskId); + appTaskInstance.setId(instance.getId()); + return appTaskInstance; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceCreateEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceCreateEntity.java new file mode 100644 index 0000000000..55627e7054 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceCreateEntity.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import java.time.LocalDateTime; +import java.util.Optional; + +/** + * 应用实例的创建数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public class TaskInstanceCreateEntity extends TaskInstanceEntity { + private static final String DEFAULT_NAME = "无标题"; + + TaskInstanceCreateEntity(String taskId, String creator, String name) { + super(); + this.setTaskId(taskId) + .setCreator(creator) + .setCreateTime(LocalDateTime.now()) + .setProgress("0") + .setName(Optional.ofNullable(name).orElse(DEFAULT_NAME)); + } + + @Override + public TaskInstanceCreateEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecorator.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecorator.java new file mode 100644 index 0000000000..75b586cd57 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecorator.java @@ -0,0 +1,161 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import modelengine.fit.jober.aipp.common.AppTaskRunnable; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; +import modelengine.fit.jober.aipp.entity.AippLogData; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.jober.aipp.service.AppChatSseService; + +import lombok.Getter; +import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; +import modelengine.fitframework.util.ObjectUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.time.LocalDateTime; +import java.util.Optional; + +/** + * 应用实例的装饰类. + * + * @author 张越 + * @since 2025-01-10 + */ +@Getter +public class TaskInstanceDecorator implements AppTaskRunnable { + private static final Logger log = LoggerFactory.getLogger(TaskInstanceDecorator.class); + + private final AppTaskInstance instance; + + private AppTaskRunnable proxy; + + private TaskInstanceDecorator(AppTaskInstance instance) { + this.instance = instance; + this.proxy = instance; + } + + /** + * 创建装饰器. + * + * @param instance 实例对象 + * @return {@link TaskInstanceDecorator} 装饰器对象. + */ + public static TaskInstanceDecorator create(AppTaskInstance instance) { + return new TaskInstanceDecorator(instance); + } + + /** + * 对instance的run接口进行装饰,为其添加chat相关能力. + * + * @param appChatSessionService {@link AppChatSessionService} 对象. + * @param appChatSSEService {@link AppChatSseService} 对象. + * @return {@link TaskInstanceDecorator} 装饰器对象. + */ + public TaskInstanceDecorator chat(AppChatSessionService appChatSessionService, + AppChatSseService appChatSSEService) { + AppTaskRunnable current = this.proxy; + Object newProxy = Proxy.newProxyInstance(current.getClass().getClassLoader(), + current.getClass().getInterfaces(), (p, method, args) -> { + if (method.getName().startsWith("run") && method.getParameterCount() == 2) { + return interceptChat(method, args, current, appChatSessionService, appChatSSEService); + } + return method.invoke(current, args); + }); + this.proxy = ObjectUtils.cast(newProxy); + return this; + } + + private Object interceptChat(Method method, Object[] args, AppTaskRunnable current, + AppChatSessionService appChatSessionService, AppChatSseService appChatSSEService) + throws IllegalAccessException, InvocationTargetException { + ChatSession session = ObjectUtils.cast(args[1]); + if (session == null) { + return method.invoke(current, args); + } + RunContext ctx = ObjectUtils.cast(args[0]); + appChatSessionService.addSession(this.instance.getId(), session); + sendReady(this.instance, ctx, appChatSSEService); + Object result = method.invoke(current, args); + + // enable memory并且是user_select时,不在结束后发送ready信息,和原逻辑保持一致. + if (!ctx.getMemoryConfig().getEnableMemory() || !ctx.isUserCustomMemory()) { + sendReady(this.instance, ctx, appChatSSEService); + } + return result; + } + + private void sendReady(AppTaskInstance instance, RunContext ctx, AppChatSseService appChatSSEService) { + appChatSSEService.send(instance.getId(), AppChatRsp.builder() + .instanceId(instance.getId()) + .status(FlowTraceStatus.READY.name()) + .atChatId(ObjectUtils.cast(ctx.getAtChatId())) + .chatId(ObjectUtils.cast(ctx.getOriginChatId())) + .build()); + } + + /** + * 对instance的run接口进行装饰,为其添加异常处理及日志相关能力. + * + * @param instanceService {@link AppTaskInstanceService} 对象. + * @param logService {@link AippLogService} 对象. + * @return {@link TaskInstanceDecorator} 装饰器对象. + */ + public TaskInstanceDecorator exceptionLog(AppTaskInstanceService instanceService, AippLogService logService) { + AppTaskRunnable current = this.proxy; + Object newProxy = Proxy.newProxyInstance(current.getClass().getClassLoader(), + current.getClass().getInterfaces(), + (p, method, args) -> this.wrapException(instanceService, logService, method, args, current)); + this.proxy = ObjectUtils.cast(newProxy); + return this; + } + + private Object wrapException(AppTaskInstanceService instanceService, AippLogService logService, Method method, + Object[] args, AppTaskRunnable current) throws IllegalAccessException, InvocationTargetException { + if (!method.getName().startsWith("run")) { + return method.invoke(current, args); + } + + RunContext ctx = ObjectUtils.cast(args[0]); + try { + return method.invoke(current, args); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof AippException) { + instanceService.update(AppTaskInstance.asUpdate(this.instance.getTaskId(), this.instance.getId()) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.ERROR.name()) + .build(), ctx.getOperationContext()); + // 更新日志类型为HIDDEN_FORM + logService.insertLog(AippInstLogType.ERROR.name(), AippLogData.builder().msg(e.getMessage()).build(), + ctx.getBusinessData()); + } + return null; + } + } + + @Override + public void run(RunContext context) { + Optional.ofNullable(this.proxy).orElse(this.instance).run(context); + } + + @Override + public void run(RunContext context, ChatSession chatSession) { + Optional.ofNullable(this.proxy).orElse(this.instance).run(context, chatSession); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDomainEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDomainEntity.java new file mode 100644 index 0000000000..5bc715a592 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDomainEntity.java @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +/** + * 应用实例的数据类,当实例作为领域对象使用时的数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public class TaskInstanceDomainEntity extends TaskInstanceEntity { + TaskInstanceDomainEntity() { + super(); + } + + @Override + public TaskInstanceDomainEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntity.java new file mode 100644 index 0000000000..b3b47aa187 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntity.java @@ -0,0 +1,494 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.util.UsefulUtils; +import modelengine.fit.jober.entity.task.TaskProperty; + +import lombok.Getter; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 应用实例的数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public abstract class TaskInstanceEntity> { + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private final Map info; + private final List tags; + + @Getter + private String taskId; + + @Getter + private String instanceId; + + TaskInstanceEntity() { + this.info = new HashMap<>(); + this.tags = new ArrayList<>(); + } + + /** + * 设置任务id. + * + * @param taskId 任务id. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setTaskId(String taskId) { + this.taskId = taskId; + return this.self(); + } + + /** + * 设置任务实例id. + * + * @param instanceId 任务实例id. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setInstanceId(String instanceId) { + this.instanceId = instanceId; + return this.self(); + } + + /** + * 设置任务实例名称. + * + * @param name 任务实例名称. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setName(String name) { + this.info.put(AippConst.INST_NAME_KEY, name); + return this.self(); + } + + /** + * 设置任务实例创建人. + * + * @param creator 任务实例创建人. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setCreator(String creator) { + this.info.put(AippConst.INST_CREATOR_KEY, creator); + return this.self(); + } + + /** + * 设置任务实例状态. + * + * @param status 任务实例状态. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setStatus(String status) { + this.info.put(AippConst.INST_STATUS_KEY, status); + return this.self(); + } + + /** + * 设置任务实例进度. + * + * @param progress 任务实例进度. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setProgress(String progress) { + this.info.put(AippConst.INST_PROGRESS_KEY, progress); + return this.self(); + } + + /** + * 设置任务实例表单id. + * + * @param formId 任务实例表单. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setFormId(String formId) { + this.info.put(AippConst.INST_CURR_FORM_ID_KEY, formId); + return this.self(); + } + + /** + * 设置任务实例表单版本. + * + * @param formVersion 任务实例表单版本. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setFormVersion(String formVersion) { + this.info.put(AippConst.INST_CURR_FORM_VERSION_KEY, formVersion); + return this.self(); + } + + /** + * 设置当前节点id. + * + * @param nodeId 任务当前节点id. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setCurrentNodeId(String nodeId) { + this.info.put(AippConst.INST_CURR_NODE_ID_KEY, nodeId); + return this.self(); + } + + /** + * 设置创建时间. + * + * @param createTime 任务创建时间. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setCreateTime(LocalDateTime createTime) { + this.info.put(AippConst.INST_CREATE_TIME_KEY, createTime); + return this.self(); + } + + /** + * 设置任务实例完成时间. + * + * @param finishTime 任务实例完成时间. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setFinishTime(LocalDateTime finishTime) { + this.info.put(AippConst.INST_FINISH_TIME_KEY, finishTime); + return this.self(); + } + + /** + * 设置任务实例动态表单时间. + * + * @param smartFormTime 动态表单时间. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setSmartFormTime(LocalDateTime smartFormTime) { + this.info.put(AippConst.INST_SMART_FORM_TIME_KEY, smartFormTime); + return this.self(); + } + + /** + * 设置恢复时间. + * + * @param resumeDuration 恢复时间. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setResumeDuration(String resumeDuration) { + this.info.put(AippConst.INST_RESUME_DURATION_KEY, resumeDuration); + return this.self(); + } + + /** + * 设置任务实例参数. + * + * @param key 参数. + * @param value 参数值. + * @return {@link TaskInstanceEntity} 对象. + */ + public T putInfo(String key, Object value) { + Optional.ofNullable(key).ifPresent(k -> this.info.put(k, value)); + return this.self(); + } + + /** + * 批量设置任务实例参数. + * + * @param infos 任务实例参数的映射集合. + * @return {@link TaskInstanceEntity} 对象. + */ + public T putInfos(Map infos) { + UsefulUtils.doIfNotNull(infos, this.info::putAll); + return this.self(); + } + + /** + * 从业务数据中提取数据. + * + * @param businessData 业务参数. + * @param props 数据库中的参数列表. + * @return {@link TaskInstanceEntity} 对象. + */ + public T fetch(Map businessData, List props) { + businessData.forEach((key, value) -> { + if (props.stream().anyMatch(item -> item.getName().equals(key))) { + this.info.put(key, value); + } + }); + return this.self(); + } + + /** + * 批量添加标签. + * + * @param tags 标签列表. + * @return {@link TaskInstanceEntity} 对象. + */ + public T putTags(List tags) { + UsefulUtils.doIfNotNull(tags, this.tags::addAll); + return this.self(); + } + + /** + * 设置任务实例子实例id. + * + * @param childInstanceId 子实例id. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setChildInstanceId(String childInstanceId) { + this.info.put(AippConst.INST_CHILD_INSTANCE_ID, childInstanceId); + return this.self(); + } + + /** + * 设置任务实例运行追踪id. + * + * @param flowInstanceId 任务实例追踪id. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setFlowTraceId(String flowInstanceId) { + this.info.put(AippConst.INST_FLOW_INST_ID_KEY, flowInstanceId); + return this.self(); + } + + /** + * 设置任务实例大模型输出结果. + * + * @param llmOutput 任务实例大模型输出结果. + * @return {@link TaskInstanceEntity} 对象. + */ + public T setLlmOutput(String llmOutput) { + this.info.put("llmOutput", llmOutput); + return this.self(); + } + + /** + * 获取当前节点id. + * + * @return {@link TaskInstanceEntity} 对象. + */ + public String getCurrentNodeId() { + return this.makeString(this.info.get(AippConst.INST_CURR_NODE_ID_KEY)); + } + + /** + * 获取任务实例名称. + * + * @return {@link String} 对象 + */ + public String getName() { + return this.makeString(this.info.get(AippConst.INST_NAME_KEY)); + } + + /** + * 获取大模型输出. + * + * @return {@link String} 对象 + */ + public String getLlmOutput() { + return this.makeString(this.info.get("llmOutput")); + } + + /** + * 获取标签列表. + * + * @return {@link List}{@code <}{@link String}{@code >} 列表. + */ + public List getTags() { + return new ArrayList<>(this.tags); + } + + /** + * 获取任务实例子实例id. + * + * @return {@link String} 对象 + */ + public String getChildInstanceId() { + return this.makeString(this.info.get(AippConst.INST_CHILD_INSTANCE_ID)); + } + + /** + * 获取任务实例进度. + * + * @return {@link String} 进度. + */ + public String getProgress() { + return this.makeString(this.info.get(AippConst.INST_PROGRESS_KEY)); + } + + /** + * 获取状态. + * + * @return {@link Optional}{@code <}{@link String}{@code >} 状态值. + */ + public Optional getStatus() { + return Optional.ofNullable(this.makeString(this.info.get(AippConst.INST_STATUS_KEY))); + } + + /** + * 获取创建人. + * + * @return {@link String} 对象 + */ + public String getCreator() { + return this.makeString(this.info.get(AippConst.INST_CREATOR_KEY)); + } + + /** + * 获取任务追溯id. + * + * @return {@link String} 对象 + */ + public String getFlowTranceId() { + return this.makeString(this.info.get(AippConst.INST_FLOW_INST_ID_KEY)); + } + + /** + * 获取表单id. + * + * @return {@link String} 对象 + */ + public String getFormId() { + return this.makeString(this.info.get(AippConst.INST_CURR_FORM_ID_KEY)); + } + + /** + * 获取表单版本. + * + * @return {@link String} 对象 + */ + public String getFormVersion() { + return this.makeString(this.info.get(AippConst.INST_CURR_FORM_VERSION_KEY)); + } + + /** + * 获取{@link String} 类型的参数映射集合. + * + * @return {@link Map}{@code <}{@link String}{@code ,}{@link String}{@code >} 对象. + */ + public Map getStringInfos() { + return this.info.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> this.makeString(e.getValue()))); + } + + /** + * 获取{@link Object} 类型的参数映射集合. + * + * @return {@link Map}{@code <}{@link String}{@code ,}{@link Object}{@code >} 对象. + */ + public Map getInfos() { + return this.info; + } + + /** + * 获取动态表单时间. + * + * @return {@link String} 的 {@link Optional} 对象. + */ + public Optional getSmartFormTime() { + Object smartFormTime = this.info.get(AippConst.INST_SMART_FORM_TIME_KEY); + if (smartFormTime == null) { + return Optional.empty(); + } + if (smartFormTime instanceof LocalDateTime) { + return Optional.of(DF.format(ObjectUtils.cast(smartFormTime))); + } + return Optional.of(this.makeString(smartFormTime)); + } + + /** + * 获取duration. + * + * @return {@link Duration} 对象. + */ + public Duration getDuration() { + String smartFormTimeStr = this.getSmartFormTime().orElse(StringUtils.EMPTY); + LocalDateTime smartFormTime = StringUtils.isBlank(smartFormTimeStr) + ? LocalDateTime.now() + : LocalDateTime.parse(smartFormTimeStr, DF); + return Duration.between(smartFormTime, LocalDateTime.now()); + } + + /** + * 获取创建时间. + * + * @return {@link String} 对象 + */ + public String getCreateTime() { + Object createTime = this.info.get(AippConst.INST_CREATE_TIME_KEY); + if (createTime == null) { + return StringUtils.EMPTY; + } + if (createTime instanceof LocalDateTime) { + return DF.format(ObjectUtils.cast(createTime)); + } + return this.makeString(createTime); + } + + /** + * 获取完成时间. + * + * @param defaultValue 默认值. + * @return {@link String} 对象 + */ + public String getFinishTime(String defaultValue) { + Object finishTime = this.info.get(AippConst.INST_FINISH_TIME_KEY); + if (finishTime == null) { + return defaultValue; + } + if (finishTime instanceof LocalDateTime) { + return DF.format(ObjectUtils.cast(finishTime)); + } + return this.makeString(finishTime); + } + + /** + * 获取恢复时间. + * + * @return 恢复时间数值. + */ + public long getResumeDuration() { + String resumeDuration = + ObjectUtils.cast(Optional.ofNullable(this.info.get(AippConst.INST_RESUME_DURATION_KEY)).orElse("0")); + return Long.parseLong(resumeDuration); + } + + /** + * 通过实例数据构建任务实例对象. + * + * @return {@link AppTaskInstance} 对象. + */ + public AppTaskInstance build() { + AppTaskInstance taskInstance = new AppTaskInstance(this); + taskInstance.setTaskId(this.getTaskId()); + taskInstance.setId(this.getInstanceId()); + return taskInstance; + } + + private String makeString(Object value) { + return this.makeString(value, null); + } + + private String makeString(Object value, String defaultValue) { + return Optional.ofNullable(value).map(ObjectUtils::cast).orElse(defaultValue); + } + + /** + * 返回自身引用. + * + * @return 自身的引用类型. + */ + public abstract T self(); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceQueryEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceQueryEntity.java new file mode 100644 index 0000000000..4753c7636b --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceQueryEntity.java @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import modelengine.fit.jober.aipp.util.UsefulUtils; + +import lombok.Getter; + +/** + * 应用实例的查询数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +@Getter +public class TaskInstanceQueryEntity extends TaskInstanceEntity { + private String order; + private String sort; + + TaskInstanceQueryEntity() { + super(); + } + + /** + * 设置order. + * + * @param order 顺序. + * @return {TaskInstanceQueryEntity} 对象. + */ + public TaskInstanceQueryEntity setOrder(String order) { + UsefulUtils.doIfNotBlank(order, o -> this.order = o); + return this.self(); + } + + /** + * 设置sort. + * + * @param sort 排序字段. + * @return {TaskInstanceQueryEntity} 对象. + */ + public TaskInstanceQueryEntity setSort(String sort) { + UsefulUtils.doIfNotBlank(sort, o -> this.sort = o); + return this.self(); + } + + /** + * 返回自身引用. + * + * @return 自身的引用类型. + */ + @Override + public TaskInstanceQueryEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceUpdateEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceUpdateEntity.java new file mode 100644 index 0000000000..b9986de41d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceUpdateEntity.java @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +/** + * 应用实例的修改数据类. + * + * @author 张越 + * @since 2025-01-08 + */ +public class TaskInstanceUpdateEntity extends TaskInstanceEntity { + public TaskInstanceUpdateEntity(String taskId, String instanceId) { + super(); + this.setTaskId(taskId).setInstanceId(instanceId); + } + + @Override + public TaskInstanceUpdateEntity self() { + return this; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/AppTaskInstanceService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/AppTaskInstanceService.java new file mode 100644 index 0000000000..53e2f6d5f7 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/AppTaskInstanceService.java @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance.service; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * 应用任务实例服务. + * + * @author 张越 + * @since 2024-12-31 + */ +public interface AppTaskInstanceService { + /** + * 获取单个任务实例. + * + * @param taskId 任务唯一标识. + * @param taskInstanceId 任务实例唯一标识. + * @param context 操作人上下文信息. + * @return {@link Optional}{@code <}{@link AppTaskInstance}{@code >} 对象. + */ + Optional getInstance(String taskId, String taskInstanceId, OperationContext context); + + /** + * 通过任务实例id获取任务实例列表. + * + * @param taskId 任务唯一标识. + * @param limit 单次查询条数. + * @param context 操作人上下文信息. + * @return {@link List}{@code <}{@link AppTaskInstance}{@code >} 任务实例列表. + */ + List getInstancesByTaskId(String taskId, int limit, OperationContext context); + + /** + * 通过任务实例id获取任务实例列表流. + * + * @param taskId 任务唯一标识. + * @param limit 单次查询条数. + * @param context 操作人上下文信息. + * @return {@link Stream}{@code <}{@link AppTaskInstance}{@code >} 任务实例列表流. + */ + Stream getInstanceStreamByTaskId(String taskId, int limit, OperationContext context); + + /** + * 修改单个任务实例. + * + * @param instance 待修改任务实例参数. + * @param context 操作人上下文信息. + */ + void update(AppTaskInstance instance, OperationContext context); + + /** + * 创建任务实例. + * + * @param instance 任务实例创建参数. + * @param context 操作人上下文信息. + * @return {@link AppTaskInstance} 对象. + */ + AppTaskInstance createInstance(AppTaskInstance instance, OperationContext context); + + /** + * 删除单个任务实例. + * + * @param taskId 任务唯一标识. + * @param taskInstanceId 任务实例唯一标识. + * @param context 操作人上下文信息. + */ + void delete(String taskId, String taskInstanceId, OperationContext context); + + /** + * 通过任务实例id获取任务id. + * + * @param taskInstanceId 任务实例唯一标识. + * @return {@link String} 任务id. + */ + String getTaskId(String taskInstanceId); + + /** + * 通过实例id获取实例对象. + * + * @param taskInstanceId 实例id. + * @param context 操作人上下文. + * @return {@link AppTaskInstance} 的 {@link Optional} 对象. + */ + Optional getInstanceById(String taskInstanceId, OperationContext context); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/impl/AppTaskInstanceServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/impl/AppTaskInstanceServiceImpl.java new file mode 100644 index 0000000000..4f3cc4a7a4 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/taskinstance/service/impl/AppTaskInstanceServiceImpl.java @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance.service.impl; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.meta.multiversion.MetaInstanceService; +import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jane.meta.multiversion.instance.MetaInstanceFilter; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstanceFactory; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.util.MetaUtils; + +import lombok.RequiredArgsConstructor; +import modelengine.fitframework.annotation.Component; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 应用任务实例服务实现类. + * + * @author 张越 + * @since 2024-12-31 + */ +@Component +@RequiredArgsConstructor +public class AppTaskInstanceServiceImpl implements AppTaskInstanceService { + private final MetaInstanceService metaInstanceService; + private final AppTaskInstanceFactory factory; + + @Override + public Optional getInstance(String taskId, String taskInstanceId, OperationContext context) { + Instance metaInst = this.metaInstanceService.retrieveById(taskInstanceId, context); + return Optional.ofNullable(metaInst).map(i -> this.factory.create(i, taskId, this)); + } + + @Override + public List getInstancesByTaskId(String taskId, int limit, OperationContext context) { + return this.getInstanceStreamByTaskId(taskId, limit, context).collect(Collectors.toList()); + } + + @Override + public Stream getInstanceStreamByTaskId(String taskId, int limit, OperationContext context) { + return MetaUtils.getAllFromRangedResult(limit, os -> metaInstanceService.list(taskId, os, limit, context)) + .map(instance -> this.factory.create(instance, taskId, this)); + } + + @Override + public void update(AppTaskInstance instance, OperationContext context) { + InstanceDeclarationInfo declarationInfo = this.factory.toDeclarationInfo(instance); + this.metaInstanceService.patchMetaInstance(instance.getTaskId(), instance.getId(), declarationInfo, context); + } + + @Override + public AppTaskInstance createInstance(AppTaskInstance instance, OperationContext context) { + InstanceDeclarationInfo declarationInfo = this.factory.toDeclarationInfo(instance); + Instance metaInst = this.metaInstanceService.createMetaInstance(instance.getTaskId(), declarationInfo, context); + return this.factory.create(metaInst, instance.getTaskId(), this); + } + + @Override + public void delete(String taskId, String taskInstanceId, OperationContext context) { + this.metaInstanceService.deleteMetaInstance(taskId, taskInstanceId, context); + } + + @Override + public String getTaskId(String taskInstanceId) { + return this.metaInstanceService.getMetaVersionId(taskInstanceId); + } + + @Override + public Optional getInstanceById(String taskInstanceId, OperationContext context) { + String taskId = this.getTaskId(taskInstanceId); + Instance metaInst = this.metaInstanceService.retrieveById(taskInstanceId, context); + return Optional.ofNullable(metaInst).map(i -> this.factory.create(i, taskId, this)); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippDto.java index 23c4f3d618..01969a7a68 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippDto.java @@ -11,9 +11,13 @@ import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import modelengine.fit.jane.common.validation.Size; +import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fitframework.annotation.Property; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; import java.util.Map; +import java.util.Optional; /** * Aipp创建/更新参数 @@ -69,4 +73,51 @@ public class AippDto { @Property(description = "应用分类") private String appCategory; + + /** + * 获取元数据id. + * + * @return 元数据id. + */ + public String getMetaId() { + return String.valueOf(this.flowViewData.getOrDefault(AippConst.FLOW_CONFIG_ID_KEY, StringUtils.EMPTY)); + } + + /** + * 获取版本. + * + * @return 版本. + */ + public String getVersion() { + return Optional.ofNullable(this.version) + .orElseGet(() -> String.valueOf( + this.flowViewData.getOrDefault(AippConst.FLOW_CONFIG_VERSION_KEY, StringUtils.EMPTY))); + } + + /** + * 获取流程id. + * + * @return 流程id. + */ + public String getFlowId() { + return ObjectUtils.cast(this.flowViewData.get(AippConst.FLOW_CONFIG_ID_KEY)); + } + + /** + * 获取preview版本. + * + * @return preview版本. + */ + public String getPreviewVersion() { + return ObjectUtils.cast(this.flowViewData.get(AippConst.FLOW_CONFIG_VERSION_KEY)); + } + + /** + * 设置预览版本. + * + * @param previewVersion 预览版本. + */ + public void setPreviewVersion(String previewVersion) { + this.flowViewData.put(AippConst.FLOW_CONFIG_VERSION_KEY, previewVersion); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippNodeForms.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippNodeForms.java index 50b126d615..7fb98e9de6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippNodeForms.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AippNodeForms.java @@ -6,12 +6,12 @@ package modelengine.fit.jober.aipp.dto; +import modelengine.fit.dynamicform.entity.FormMetaInfo; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.dynamicform.entity.FormMetaInfo; -import modelengine.fit.jober.entity.consts.NodeTypes; import java.util.List; @@ -28,7 +28,7 @@ public class AippNodeForms { /** * 节点类型 - * 查看 {@link NodeTypes} + * 查看 {@link modelengine.fit.jober.entity.consts.NodeTypes} */ private String type; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppCreateDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppCreateDto.java index 34cdb01420..87618f0d21 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppCreateDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppCreateDto.java @@ -13,6 +13,7 @@ import modelengine.fitframework.annotation.Property; /** + * * This class is used to create a new application. * 应用创建Dto * @@ -25,21 +26,16 @@ @NoArgsConstructor public class AppBuilderAppCreateDto { private String name; - private String description; - private String icon; - private String greeting; @Property(name = "app_type") private String appType; - private String type; @Property(name = "store_id") private String storeId; - @Property(name = "app_built_type") private String appBuiltType; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppMetadataDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppMetadataDto.java index 65beca6365..303880a6eb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppMetadataDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppMetadataDto.java @@ -27,30 +27,17 @@ @NoArgsConstructor public class AppBuilderAppMetadataDto { private String name; - private String type; - private String createBy; - private String updateBy; - private String version; - private LocalDateTime createAt; - private LocalDateTime updateAt; - private String id; - private Map attributes; - private String state; - private List tags; - private String appType; - private String appCategory; - private String appBuiltType; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormDto.java index e8498bdcf6..9bf87e0d55 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormDto.java @@ -26,22 +26,13 @@ @NoArgsConstructor public class AppBuilderFormDto { private String id; - private String name; - private Map appearance; - private String type; - private String createBy; - private LocalDateTime createAt; - private String updateBy; - private LocalDateTime updateAt; - private String version; - private String formSuiteId; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormPropertyDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormPropertyDto.java index 237881472a..585423db75 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormPropertyDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderFormPropertyDto.java @@ -23,12 +23,8 @@ @Builder public class AppBuilderFormPropertyDto { private String id; - private String formId; - private String name; - private String dataType; - private String defaultValue; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptCategoryDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptCategoryDto.java index 8b8e4cd97b..3885c7e904 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptCategoryDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptCategoryDto.java @@ -25,12 +25,8 @@ @NoArgsConstructor public class AppBuilderPromptCategoryDto { private String title; - private String id; - private String parent; - private Boolean disable; - private List children; } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptDto.java index 3b7d726dcd..19f2aa3300 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderPromptDto.java @@ -25,7 +25,6 @@ @NoArgsConstructor public class AppBuilderPromptDto { private List categories; - private List inspirations; /** @@ -37,19 +36,12 @@ public class AppBuilderPromptDto { @NoArgsConstructor public static class AppBuilderInspirationDto { private String name; - private String id; - private String prompt; - private String promptTemplate; - private String category; - private String description; - private Boolean auto; - private List promptVarData; } @@ -62,15 +54,10 @@ public static class AppBuilderInspirationDto { @NoArgsConstructor public static class AppBuilderPromptVarDataDto { private String key; - private String var; - private String varType; - private String sourceType; - private String sourceInfo; - private Boolean multiple; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDto.java index ed9f6c0afd..ab1f8bb1b2 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDto.java @@ -25,6 +25,5 @@ @NoArgsConstructor public class AppBuilderSaveConfigDto { private List input; - private String graph; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppCreateToolDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppCreateToolDto.java index 7ed892b8e4..366f9a0ffe 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppCreateToolDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppCreateToolDto.java @@ -24,17 +24,12 @@ @NoArgsConstructor public class AppCreateToolDto { private String name; - private String description; - private String icon; - private String greeting; @Property(name = "app_type") private String appType; - private String type; - private String systemPrompt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppInputParam.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppInputParam.java index 3f34a5b2e6..97bb73c59b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppInputParam.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/AppInputParam.java @@ -6,10 +6,24 @@ package modelengine.fit.jober.aipp.dto; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.INPUT_PARAM_IS_INVALID; +import static modelengine.fitframework.util.ObjectUtils.cast; +import static modelengine.fitframework.util.StringUtils.lengthBetween; + +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.enums.InputParamType; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.function.Predicate; /** * 表示应用开始节点配置参数 @@ -17,18 +31,77 @@ * @author 孙怡菲 * @since 2024-11-25 */ -@Builder @Data -@AllArgsConstructor @NoArgsConstructor public class AppInputParam { + private int stringMaxLength = 500; + private Map> paramTypePredicateMap; private String name; - private String type; - private String description; - private boolean isRequired; - private boolean isVisible; + + /** + * 通过键值对构建一个 {@link AppInputParam} 对象. + * + * @param rawParam 原始参数. + * @return {@link AppInputParam} 对象. + */ + public static AppInputParam from(Map rawParam) { + AppInputParam appInputParam = new AppInputParam(); + appInputParam.setName(ObjectUtils.cast(rawParam.get("name"))); + appInputParam.setType(ObjectUtils.cast(rawParam.get("type"))); + appInputParam.setDescription(ObjectUtils.cast(rawParam.get("description"))); + appInputParam.setRequired(ObjectUtils.cast(rawParam.getOrDefault("isRequired", true))); + appInputParam.setVisible(ObjectUtils.cast(rawParam.getOrDefault("isVisible", true))); + Integer stringMaxLength = cast(rawParam.getOrDefault("stringMaxLength", 500)); + appInputParam.setStringMaxLength(stringMaxLength); + Map> paramTypePredicateMap + = MapBuilder.>get() + .put(InputParamType.STRING_TYPE, + v -> v instanceof String && lengthBetween(cast(v), 1, stringMaxLength, true, true)) + .put(InputParamType.BOOLEAN_TYPE, v -> v instanceof Boolean) + .put(InputParamType.INTEGER_TYPE, + v -> v instanceof Integer && ObjectUtils.between((int) v, -999999999, 999999999)) + .put(InputParamType.NUMBER_TYPE, AppInputParam::isValidNumber) + .build(); + appInputParam.setParamTypePredicateMap(paramTypePredicateMap); + return appInputParam; + } + + private static boolean isValidNumber(Object value) { + if (!(value instanceof Number)) { + return false; + } + BigDecimal numberValue = new BigDecimal(value.toString()); + if (numberValue.compareTo(new BigDecimal("-999999999.99")) < 0 + || numberValue.compareTo(new BigDecimal("999999999.99")) > 0) { + return false; + } + int scale = numberValue.scale(); + return scale <= 2; + } + + /** + * 校验数据. + * + * @param dataMap 数据集合. + */ + public void validate(Map dataMap) { + String paramName = this.getName(); + if (this.isRequired()) { + Validation.notNull(cast(dataMap.get(paramName)), + () -> new AippParamException(INPUT_PARAM_IS_INVALID, paramName)); + } + if (dataMap.get(paramName) == null) { + return; + } + + Object v = dataMap.get(this.getName()); + Predicate predicate = paramTypePredicateMap.get(InputParamType.getParamType(this.getType())); + if (!predicate.test(v)) { + throw new AippParamException(INPUT_PARAM_IS_INVALID, paramName); + } + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/FormFileDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/FormFileDto.java index a56c487230..a78f15117c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/FormFileDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/FormFileDto.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.dto; +import modelengine.fitframework.annotation.Property; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fitframework.annotation.Property; import java.util.Map; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/MemoryConfigDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/MemoryConfigDto.java index 6d8227aded..93293dac83 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/MemoryConfigDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/MemoryConfigDto.java @@ -25,8 +25,6 @@ @AllArgsConstructor public class MemoryConfigDto { private Map initContext; - private String instanceId; - private String memory; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelAccessInfo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelAccessInfo.java index 1f0a444dde..a41716d954 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelAccessInfo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelAccessInfo.java @@ -23,6 +23,5 @@ @NoArgsConstructor public class ModelAccessInfo { private String serviceName; - private String tag; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelDto.java index 5cddcce3e1..c9e60a3706 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelDto.java @@ -27,6 +27,5 @@ @NoArgsConstructor public class ModelDto { private List modelDatas; - private int total; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelListDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelListDto.java index a3b07e7d6d..739d3075d1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelListDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ModelListDto.java @@ -25,6 +25,5 @@ @NoArgsConstructor public class ModelListDto { private List models; - private int total; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/PluginToolDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/PluginToolDto.java index 5f7e3aa7a9..95e7c7244d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/PluginToolDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/PluginToolDto.java @@ -27,6 +27,5 @@ @NoArgsConstructor public class PluginToolDto { private List pluginToolData; - private int total; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ResumeAippDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ResumeAippDto.java index d821699a07..535d3a2137 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ResumeAippDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ResumeAippDto.java @@ -6,13 +6,14 @@ package modelengine.fit.jober.aipp.dto; +import modelengine.fit.http.annotation.PathVariable; +import modelengine.fit.http.annotation.RequestParam; +import modelengine.fitframework.annotation.Property; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.http.annotation.PathVariable; -import modelengine.fit.http.annotation.RequestParam; -import modelengine.fitframework.annotation.Property; /** * 恢复实例运行的启动参数类 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StatisticsDTO.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StatisticsDTO.java index 0da93fdd79..251abd888d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StatisticsDTO.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StatisticsDTO.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.dto; +import modelengine.fitframework.annotation.Property; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fitframework.annotation.Property; /** * appengine统计数据 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StoreNodeInfoDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StoreNodeInfoDto.java index 1f443441c3..f0ca36d83d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StoreNodeInfoDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/StoreNodeInfoDto.java @@ -23,8 +23,6 @@ @NoArgsConstructor public class StoreNodeInfoDto { private String name; - private String type; - private String uniqueName; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ToolModelDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ToolModelDto.java index efc380ec9d..e30e1822ac 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ToolModelDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/ToolModelDto.java @@ -29,27 +29,16 @@ @NoArgsConstructor public class ToolModelDto { private String creator; - private String modifier; - private String name; - private String description; - private String uniqueName; - private Map schema; - private Map runnables; - private Map extensions; - private String source; - private String icon; - private Set tags; - private String defaultModel; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippInstLogDataDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippInstLogDataDto.java index e8296425e7..0faf188903 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippInstLogDataDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippInstLogDataDto.java @@ -6,24 +6,35 @@ package modelengine.fit.jober.aipp.dto.aipplog; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.entity.AippInstLog; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -33,48 +44,57 @@ * @since 2024-01-08 */ @AllArgsConstructor -@NoArgsConstructor @Data public class AippInstLogDataDto { private static final Logger log = Logger.get(AippInstLogDataDto.class); - private static final int LOG_COUNT_AFTER_TZ_TOOL = 3; - - private static final HashSet QUESTION_TYPE = new HashSet<>( - Arrays.asList(AippInstLogType.QUESTION.name(), AippInstLogType.HIDDEN_QUESTION.name(), - AippInstLogType.QUESTION_WITH_FILE.name())); + private static final HashSet QUESTION_TYPE = new HashSet<>(Arrays.asList(AippInstLogType.QUESTION.name(), + AippInstLogType.HIDDEN_QUESTION.name(), + AippInstLogType.QUESTION_WITH_FILE.name())); + private static final List MEMORY_MSG_TYPE_WHITE_LIST = Arrays.asList(AippInstLogType.MSG.name(), + AippInstLogType.FORM.name(), + AippInstLogType.META_MSG.name()); private String aippId; - private String version; - private String instanceId; - private String status; - private String appName; - private String appIcon; - private LocalDateTime createAt; - private AippInstanceLogBody question; - private List instanceLogBodies; + public AippInstLogDataDto(AppTaskInstance instance, List logs) { + if (CollectionUtils.isNotEmpty(logs)) { + this.aippId = logs.get(0).getLogData().getAippId(); + this.version = logs.get(0).getLogData().getVersion(); + this.instanceId = logs.get(0).getLogData().getInstanceId(); + this.status = instance.getEntity().getStatus().orElse(MetaInstStatusEnum.ARCHIVED.name()); + this.appName = null; + this.appIcon = null; + this.createAt = logs.get(0).getLogData().getCreateAt(); + this.question = logs.stream().filter(AppLog::isQuestionType).findFirst().map(AppLog::toBody).orElse(null); + this.instanceLogBodies = logs.stream() + .filter(l -> !l.isQuestionType()) + .map(AppLog::toBody) + .collect(Collectors.toList()); + } + } + /** * 从原始日志列表中获取实例历史记录 * * @param rawLogs 表示日志列表的 {@link List}{@code <}{@link AippInstLog}{@code >} * @param context 表示操作上下文的 {@link OperationContext} - * @param metaInstanceService 表示元数据实例服务的 {@link MetaInstanceService} + * @param appTaskInstanceService 表示app任务实例服务的 {@link AppTaskInstanceService} * @return 实例历史记录 */ public static AippInstLogDataDto fromAippInstLogList(List rawLogs, OperationContext context, - MetaInstanceService metaInstanceService) { + AppTaskInstanceService appTaskInstanceService) { List instanceLogs = rawLogs.stream() .sorted((d1, d2) -> Math.toIntExact(d1.getLogId() - d2.getLogId())) - .collect(Collectors.toList()); + .toList(); final String inAippId = instanceLogs.get(0).getAippId(); final String inAippVersion = instanceLogs.get(0).getVersion(); @@ -89,15 +109,11 @@ public static AippInstLogDataDto fromAippInstLogList(List rawLogs, .filter(log -> QUESTION_TYPE.contains(log.getLogType())) .findFirst() .orElse(null); - String status = MetaInstStatusEnum.ARCHIVED.name(); - try { - String metaVersionId = metaInstanceService.getMetaVersionId(inInstanceId); - Instance instance = MetaInstanceUtils.getInstanceDetail(metaVersionId, inInstanceId, context, - metaInstanceService); - status = instance.getInfo().getOrDefault(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.ARCHIVED.name()); - } catch (Exception e) { - log.error("Failed to get status through meta. [instanceId]", inInstanceId); - } + String metaVersionId = appTaskInstanceService.getTaskId(inInstanceId); + AppTaskInstance instance = appTaskInstanceService.getInstance(metaVersionId, inInstanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", inInstanceId))); + String status = instance.getEntity().getStatus().orElse(MetaInstStatusEnum.ARCHIVED.name()); return new AippInstLogDataDto(inAippId, inAippVersion, inInstanceId, status, null, null, inCreateAt, convert(questionInfo), logBodies); } @@ -106,23 +122,72 @@ public static AippInstLogDataDto fromAippInstLogList(List rawLogs, * 获取经过AI提示词拼接工具处理后的历史记录 * * @param rawLogs 表示日志列表的 {@link List}{@code <}{@link AippInstLog}{@code >} + * @param appTaskInstanceService 表示app任务实例服务的 {@link AppTaskInstanceService} * @param context 表示操作上下文的 {@link OperationContext} - * @param metaInstanceService 表示元数据实例服务的 {@link MetaInstanceService} * @return 实例历史记录 */ public static AippInstLogDataDto fromAippInstLogListAfterSplice(List rawLogs, - MetaInstanceService metaInstanceService, OperationContext context) { + AppTaskInstanceService appTaskInstanceService, OperationContext context) { List updatedLogs = rawLogs.stream() - .filter(log -> !isUserQuestionLogBeforeTzTool(rawLogs, log)) - .collect(Collectors.toList()); + .filter(log -> !isUserQuestionLogBeforeTzTool(rawLogs, log)) + .collect(Collectors.toList()); - return fromAippInstLogList(updatedLogs, context, metaInstanceService); + return fromAippInstLogList(updatedLogs, context, appTaskInstanceService); } private static boolean isUserQuestionLogBeforeTzTool(List rawLogs, AippInstLog log) { return rawLogs.size() == LOG_COUNT_AFTER_TZ_TOOL && AippInstLogType.QUESTION.name().equals(log.getLogType()); } + /** + * 将日志转换为memory. + * + * @return {@link Optional}{@code <}{@link Map}{@code <}{@link String}{@code ,}{@link Object}{@code >} 对象. + */ + public Optional> toMemory() { + Map logMap = new HashMap<>(); + AippInstLogDataDto.AippInstanceLogBody questionBody = this.getQuestion(); + if (questionBody == null) { + return Optional.empty(); + } + logMap.put("question", getLogData(questionBody.getLogData(), questionBody.getLogType())); + List answers = this.getInstanceLogBodies() + .stream() + .filter(item -> MEMORY_MSG_TYPE_WHITE_LIST.contains(StringUtils.toUpperCase(item.getLogType()))) + .toList(); + List files = this.getInstanceLogBodies() + .stream() + .filter(l -> StringUtils.equals(l.getLogType(), AippInstLogType.FILE.name())) + .toList(); + if (!answers.isEmpty()) { + AippInstLogDataDto.AippInstanceLogBody logBody = answers.get(answers.size() - 1); + logMap.put("answer", getLogData(logBody.getLogData(), logBody.getLogType())); + } + if (!files.isEmpty()) { + AippInstLogDataDto.AippInstanceLogBody fileBody = files.get(0); + logMap.put("fileDescription", getLogData(fileBody.getLogData(), fileBody.getLogType())); + } + return Optional.of(logMap); + } + + private static String getLogData(String logData, String logType) { + Map logInfo = ObjectUtils.cast(JSON.parse(logData)); + if (!StringUtils.isEmpty(logInfo.get("form_args"))) { + return logInfo.get("form_args"); + } + String msg = logInfo.get("msg"); + if (Objects.equals(logType, AippInstLogType.META_MSG.name())) { + List> referenceMsg = ObjectUtils.cast(JSON.parse(msg)); + StringBuilder sb = new StringBuilder(); + referenceMsg.stream().map(item -> ObjectUtils.cast(item.get("data"))).forEach(sb::append); + return sb.toString(); + } + if (Objects.equals(logType, AippInstLogType.QUESTION_WITH_FILE.name())) { + return JSONObject.parseObject(msg).getString("question"); + } + return msg; + } + /** * 转换实例日志为实例日志体 */ @@ -130,13 +195,9 @@ private static boolean isUserQuestionLogBeforeTzTool(List rawLogs, @Getter public static class AippInstanceLogBody { private long logId; - private String logData; - private String logType; - private LocalDateTime createAt; - private String createUserAccount; } @@ -144,7 +205,10 @@ static AippInstLogDataDto.AippInstanceLogBody convert(AippInstLog instanceLog) { if (instanceLog == null) { return null; } - return new AippInstanceLogBody(instanceLog.getLogId(), instanceLog.getLogData(), instanceLog.getLogType(), - instanceLog.getCreateAt(), instanceLog.getCreateUserAccount()); + return new AippInstanceLogBody(instanceLog.getLogId(), + instanceLog.getLogData(), + instanceLog.getLogType(), + instanceLog.getCreateAt(), + instanceLog.getCreateUserAccount()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippUploadedFileInfoDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippUploadedFileInfoDto.java index f187b5fb6e..c8f2ae6756 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippUploadedFileInfoDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/aipplog/AippUploadedFileInfoDto.java @@ -14,8 +14,8 @@ /** * AIPP上传文件信息DTO * - * @author: 孙怡菲 - * @since: 2024-04-14 16:48 + * @author 孙怡菲 + * @since 2024-04-14 */ @Data @Builder @@ -23,10 +23,7 @@ @AllArgsConstructor public class AippUploadedFileInfoDto { private String aippId; - private String createUserAccount; - private String filename; - private String fileUuid; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/AudioSplitInfo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/AudioSplitInfo.java index 1f80766578..380f7dde73 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/AudioSplitInfo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/AudioSplitInfo.java @@ -23,7 +23,6 @@ public class AudioSplitInfo { @Property(description = "音频目录") private String dirPath; - @Property(description = "音频分段大小") private int segmentSize; } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummaryDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummaryDto.java index e00c9ec900..b8fdfa6121 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummaryDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummaryDto.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.dto.audio; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import modelengine.fit.jober.aipp.entity.ffmpeg.FfmpegUtil; import modelengine.fit.jober.aipp.util.JsonUtils; import modelengine.fit.jober.aipp.util.LLMUtils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import modelengine.fitframework.annotation.Property; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.CollectionUtils; @@ -33,7 +34,6 @@ @NoArgsConstructor public class SummaryDto { private static final Random POS_RANDOM = new Random(); - private static final Logger log = Logger.get(SummaryDto.class); @Property(description = "视频摘要") @@ -57,13 +57,13 @@ public SummaryDto(List summaryList, int segmentSize) { continue; } try { - SummarySection section = JsonUtils.parseObject(LLMUtils.tryFixLlmJsonString(item), - SummarySection.class); - section.setPosition( - FfmpegUtil.formatTimestamps(Math.max(segmentSize * i - POS_RANDOM.nextInt(60) - 30, 0))); + SummarySection section = + JsonUtils.parseObject(LLMUtils.tryFixLlmJsonString(item), SummarySection.class); + section.setPosition(FfmpegUtil.formatTimestamps(Math.max( + segmentSize * i - POS_RANDOM.nextInt(60) - 30, 0))); sectionList.add(section); - } catch (IOException | IllegalArgumentException | UnsupportedOperationException | ClassCastException | - NullPointerException e) { + } catch (IOException | IllegalArgumentException | UnsupportedOperationException | ClassCastException + | NullPointerException e) { log.warn("Llm generate unexpect rsp."); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummarySection.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummarySection.java index 1b52703e35..e1e769a2bb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummarySection.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/audio/SummarySection.java @@ -23,10 +23,8 @@ public class SummarySection { @Property(description = "音频坐标, 格式HH:mm:ss") private String position; - @Property(description = "摘要标题") private String title; - @Property(description = "片段摘要") private String text; } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/AppChatRsp.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/AppChatRsp.java index 348082e884..b4dc35f449 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/AppChatRsp.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/AppChatRsp.java @@ -55,9 +55,7 @@ public class AppChatRsp { @Builder public static class Answer { Object content; - String type; - String msgId; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/ChatCreateEntity.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/ChatCreateEntity.java new file mode 100644 index 0000000000..336755c98d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/ChatCreateEntity.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.dto.chat; + +import lombok.Builder; +import lombok.Data; + +import java.util.Map; + +/** + * chat 创建参数类. + * + * @author 张越 + * @since 2025-01-14 + */ +@Data +@Builder +public class ChatCreateEntity { + private String appId; + private String appVersion; + private Map attributes; + private String chatName; + private String chatId; + private String taskInstanceId; +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/QueryChatRsp.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/QueryChatRsp.java index 27f8975cbf..941db727cb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/QueryChatRsp.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/chat/QueryChatRsp.java @@ -6,13 +6,20 @@ package modelengine.fit.jober.aipp.dto.chat; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.util.JsonUtils; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import modelengine.fitframework.annotation.Property; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; import java.util.List; +import java.util.Map; /** * 查询会话响应体 @@ -69,4 +76,15 @@ public class QueryChatRsp { @Property(description = "total", name = "total") private Integer total; + + /** + * chat是否是调试模式. + * + * @return true/false. + */ + public boolean isDebug() { + Map jsonAttributes = JsonUtils.parseObject(this.getAttributes()); + String state = ObjectUtils.cast(jsonAttributes.get(AippConst.ATTR_CHAT_STATE_KEY)); + return StringUtils.equals(AppState.INACTIVE.getName(), state); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/AppCheckDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/AppCheckDto.java index 69a6bf896e..6b07192683 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/AppCheckDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/AppCheckDto.java @@ -26,7 +26,6 @@ @NoArgsConstructor public class AppCheckDto { private String type; - private List nodeInfos; /** @@ -38,9 +37,7 @@ public class AppCheckDto { @NoArgsConstructor public static class NodeInfo { private String nodeId; - private String nodeName; - private List> configs; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/CheckResult.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/CheckResult.java index 891141e1d7..c81b008cf7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/CheckResult.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/check/CheckResult.java @@ -26,12 +26,8 @@ @NoArgsConstructor public class CheckResult { private String nodeId; - private String name; - private String type; - private boolean isValid; - private List> configChecks; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportApp.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportApp.java index cb1f280797..49d3ff2d99 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportApp.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportApp.java @@ -6,11 +6,21 @@ package modelengine.fit.jober.aipp.dto.export; +import modelengine.fit.jober.aipp.util.AippFileUtils; +import modelengine.fit.jober.aipp.util.AppImExportUtil; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import modelengine.fitframework.util.FileUtils; +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.StringUtils; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Base64; import java.util.Map; /** @@ -25,18 +35,33 @@ @NoArgsConstructor public class AppExportApp { private String name; - private String tenantId; - private String type; - private String appBuiltType; - private String version; - private Map attributes; - private String appCategory; - private String appType; + + /** + * 设置图标. + * + * @param icon 图标. + */ + public void setIcon(String icon) { + this.attributes.put("icon", this.getIconAttributes(AippFileUtils.getFileNameFromIcon(icon))); + } + + private Map getIconAttributes(String iconPath) { + try { + File iconFile = FileUtils.canonicalize(iconPath); + Map iconAttr = MapBuilder.get().build(); + byte[] iconBytes = AppImExportUtil.readAllBytes(Files.newInputStream(iconFile.toPath())); + iconAttr.put("content", Base64.getEncoder().encodeToString(iconBytes)); + iconAttr.put("type", AppImExportUtil.extractIconExtension(iconFile.getName())); + return iconAttr; + } catch (IllegalStateException | IOException e) { + return MapBuilder.get().put("content", StringUtils.EMPTY).build(); + } + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfig.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfig.java index 2edf3935fd..9455c287af 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfig.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfig.java @@ -25,6 +25,5 @@ @NoArgsConstructor public class AppExportConfig { private AppExportForm form; - private List configProperties; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfigProperty.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfigProperty.java index 82546b7c72..7191050454 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfigProperty.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportConfigProperty.java @@ -23,6 +23,5 @@ @NoArgsConstructor public class AppExportConfigProperty { private String nodeId; - private AppExportFormProperty formProperty; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportDto.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportDto.java index e31431b22f..11ee655680 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportDto.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportDto.java @@ -6,11 +6,21 @@ package modelengine.fit.jober.aipp.dto.export; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.enums.AppTypeEnum; +import modelengine.fit.jober.aipp.util.AppImExportUtil; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import modelengine.fitframework.annotation.Property; +import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.Map; /** * 应用导出配置类。 @@ -34,4 +44,38 @@ public class AppExportDto { @Property(description = "应用流程图配置信息") AppExportFlowGraph flowGraph; + + /** + * 获取头像文件的路径。 + * + * @param contextRoot 表示请求上下文根的 {@link String}。 + * @param context 表示操作人上下文信息的 {@link String}。 + * @param resourcePath 表示资源目录的 {@link String}。 + * @return 表示获取到的头像文件的路径的 {@link String}。 + */ + @JsonIgnore + public String getIconPath(String contextRoot, String resourcePath, OperationContext context) { + Object iconAttr = this.app.getAttributes().get("icon"); + String iconContent = iconAttr instanceof Map ? ObjectUtils.cast( + ObjectUtils.>cast(iconAttr).get("content")) : StringUtils.EMPTY; + if (StringUtils.isBlank(iconContent)) { + return iconContent; + } + String iconExtension = ObjectUtils.cast(ObjectUtils.>cast(iconAttr).get("type")); + return AppImExportUtil.saveIconFile(iconContent, + iconExtension, + context.getTenantId(), + contextRoot, + resourcePath); + } + + /** + * 获取类型. + * + * @return 类型. + */ + @JsonIgnore + public String getType() { + return ObjectUtils.cast(this.app.getAttributes().getOrDefault("appType", AppTypeEnum.APP.code())); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFlowGraph.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFlowGraph.java index 97c03f66d4..8158bde6f9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFlowGraph.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFlowGraph.java @@ -23,6 +23,5 @@ @NoArgsConstructor public class AppExportFlowGraph { private String name; - private String appearance; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFormProperty.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFormProperty.java index dd7c8d4ce5..de3c64cc2b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFormProperty.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/export/AppExportFormProperty.java @@ -23,16 +23,10 @@ @NoArgsConstructor public class AppExportFormProperty { private String name; - private String dataType; - private String defaultValue; - private String from; - private String group; - private String description; - private int index; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/image/StableDiffusionInput.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/image/StableDiffusionInput.java index 1742f5ae9a..3ad39ea79c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/image/StableDiffusionInput.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/dto/image/StableDiffusionInput.java @@ -17,6 +17,5 @@ @Data public class StableDiffusionInput { private String prompt; - private String negativePrompt; } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ChatSession.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ChatSession.java index c97e1bc64c..68442ae7dd 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ChatSession.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ChatSession.java @@ -24,15 +24,10 @@ @Setter public class ChatSession { Emitter emitter; - String appId; - boolean isDebug; - Locale locale; - LocalDateTime expireTime; - boolean isOccupied; public ChatSession(Emitter emitter, String appId, boolean isDebug, Locale locale) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/MindJsonElement.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/MindJsonElement.java index 9c31d122d0..dc9adde170 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/MindJsonElement.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/MindJsonElement.java @@ -19,7 +19,6 @@ @AllArgsConstructor public class MindJsonElement { String name; - String children; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ffmpeg/FfmpegTask.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ffmpeg/FfmpegTask.java index 22baa77591..5e9bfec17c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ffmpeg/FfmpegTask.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/entity/ffmpeg/FfmpegTask.java @@ -73,8 +73,8 @@ public String exec() throws IOException { ProcessBuilder builder = new ProcessBuilder(); StringBuilder sb = new StringBuilder(); Process p = builder.command(command).redirectErrorStream(true).start(); - try (BufferedReader br = new BufferedReader( - new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), + StandardCharsets.UTF_8))) { String s; while ((s = br.readLine()) != null) { sb.append(s); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AippMetaStatusEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AippMetaStatusEnum.java index 7ba04d15dc..1b2e2c93d4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AippMetaStatusEnum.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AippMetaStatusEnum.java @@ -6,10 +6,11 @@ package modelengine.fit.jober.aipp.enums; -import lombok.Getter; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; +import lombok.Getter; + import java.util.Arrays; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppCategory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppCategory.java index 6e938e77d3..10a3e6bad6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppCategory.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppCategory.java @@ -24,13 +24,9 @@ public enum AppCategory { FIT("工具", "fit", "TOOL", "FIT", "system"); private final String description; - private final String type; - private final String category; - private final String tag; - private final String source; AppCategory(String description, String type, String category, String tag, String source) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppState.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppState.java index 1787bf7386..11b9a09625 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppState.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppState.java @@ -8,6 +8,8 @@ import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; + +import lombok.Getter; import modelengine.fitframework.util.StringUtils; import java.util.Arrays; @@ -18,6 +20,7 @@ * @author 张越 * @since 2024-05-24 */ +@Getter public enum AppState { PUBLISHED("active"), INACTIVE("inactive"), @@ -29,15 +32,6 @@ public enum AppState { this.name = name; } - /** - * 获取状态名称. - * - * @return 状态名称. - */ - public String getName() { - return name; - } - /** * 获取状态 * diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppStatus.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppStatus.java new file mode 100644 index 0000000000..47350bbffc --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/AppStatus.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.enums; + +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippParamException; + +import lombok.Getter; +import modelengine.fitframework.util.StringUtils; + +import java.util.Arrays; + +/** + * App的状态. + * + * @author 张越 + * @since 2024-12-30 + */ +@Getter +public enum AppStatus { + PUBLISHED("published"), + DRAFT("draft"), + IMPORTING("importing"); + + private final String name; + + AppStatus(String name) { + this.name = name; + } + + /** + * 将状态转换为 {@link AppStatus} 对象. + * + * @param name 名称 + * @return 发布状态 + */ + public static AppStatus toStatus(String name) { + return Arrays.stream(values()) + .filter(value -> StringUtils.equalsIgnoreCase(name, value.getName())) + .findFirst() + .orElseThrow(() -> new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID, name)); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormEdgeEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormEdgeEnum.java index eb8242ead8..5de56151ec 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormEdgeEnum.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormEdgeEnum.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.enums; -import lombok.Getter; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.constants.AippConst; +import lombok.Getter; + import java.util.Arrays; /** @@ -25,7 +26,6 @@ public enum FormEdgeEnum { END(AippConst.ATTR_END_FORM_ID_KEY, AippConst.ATTR_END_FORM_VERSION_KEY); private final String formIdKey; - private final String versionKey; FormEdgeEnum(String formIdKey, String versionKey) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormPropertyTypeEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormPropertyTypeEnum.java index 890192d9f7..72d217c60f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormPropertyTypeEnum.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/FormPropertyTypeEnum.java @@ -8,6 +8,7 @@ import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; + import modelengine.fitframework.util.StringUtils; import java.util.Arrays; @@ -32,7 +33,6 @@ public enum FormPropertyTypeEnum { LIST("List", java.util.List.class); private final String code; - private final Class clazz; FormPropertyTypeEnum(String code, Class clazz) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/InputParamType.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/InputParamType.java index b38f77677d..0eb97ba02a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/InputParamType.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/InputParamType.java @@ -39,8 +39,8 @@ public enum InputParamType { */ public static InputParamType getParamType(String type) { return Arrays.stream(InputParamType.values()) - .filter(paramType -> StringUtils.equals(paramType.type, type)) - .findFirst() - .orElse(InputParamType.UNKNOWN_TYPE); + .filter(paramType -> StringUtils.equals(paramType.type, type)) + .findFirst() + .orElse(InputParamType.UNKNOWN_TYPE); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/MetaInstSortKeyEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/MetaInstSortKeyEnum.java index dc7af81fa8..19889f9098 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/MetaInstSortKeyEnum.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/MetaInstSortKeyEnum.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.enums; -import lombok.Getter; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.constants.AippConst; +import lombok.Getter; + import java.util.Arrays; /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/RestartModeEnum.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/RestartModeEnum.java index c09c9539ab..ba479e02d9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/RestartModeEnum.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/RestartModeEnum.java @@ -6,12 +6,15 @@ package modelengine.fit.jober.aipp.enums; +import lombok.Getter; + /** * 表示重新对话的模式。 * * @author 黄夏露 * @since 2024-07-12 */ +@Getter public enum RestartModeEnum { /** * 覆盖式。 @@ -28,13 +31,4 @@ public enum RestartModeEnum { RestartModeEnum(String mode) { this.mode = mode; } - - /** - * 获取重新对话的模式。 - * - * @return 表示重新对话模式的 {@link String}。 - */ - public String getMode() { - return this.mode; - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/StreamMsgType.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/StreamMsgType.java index 45a2cc73fa..f079ff6095 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/StreamMsgType.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/enums/StreamMsgType.java @@ -8,6 +8,7 @@ import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; + import modelengine.fitframework.util.StringUtils; import java.util.Arrays; @@ -96,6 +97,6 @@ public static StreamMsgType from(String value) { * @return 表示流式响应消息的 {@link StreamMsgType}。 */ public static StreamMsgType from(AippInstLogType value) { - return StreamMsgType.from(value.name()); + return from(value.name()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppBuilderAppFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppBuilderAppFactory.java index 84a534fe1f..ef7a579a6a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppBuilderAppFactory.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppBuilderAppFactory.java @@ -16,6 +16,7 @@ import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.StringUtils; @@ -32,21 +33,18 @@ @Component public class AppBuilderAppFactory { private final AppBuilderFlowGraphRepository flowGraphRepository; - private final AppBuilderConfigRepository configRepository; - private final AppBuilderFormRepository formRepository; - private final AppBuilderConfigPropertyRepository configPropertyRepository; - private final AppBuilderFormPropertyRepository formPropertyRepository; - private final AppBuilderAppRepository appRepository; public AppBuilderAppFactory(AppBuilderFlowGraphRepository flowGraphRepository, - AppBuilderConfigRepository configRepository, AppBuilderFormRepository formRepository, + AppBuilderConfigRepository configRepository, + AppBuilderFormRepository formRepository, AppBuilderConfigPropertyRepository configPropertyRepository, - AppBuilderFormPropertyRepository formPropertyRepository, AppBuilderAppRepository appRepository) { + AppBuilderFormPropertyRepository formPropertyRepository, + AppBuilderAppRepository appRepository) { this.flowGraphRepository = flowGraphRepository; this.configRepository = configRepository; this.configPropertyRepository = configPropertyRepository; @@ -61,8 +59,11 @@ public AppBuilderAppFactory(AppBuilderFlowGraphRepository flowGraphRepository, * @return AppBuilderApp。 */ public AppBuilderApp create() { - return new AppBuilderApp(this.flowGraphRepository, this.configRepository, this.formRepository, - this.configPropertyRepository, this.formPropertyRepository); + return new AppBuilderApp(this.flowGraphRepository, + this.configRepository, + this.formRepository, + this.configPropertyRepository, + this.formPropertyRepository); } /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppTemplateFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppTemplateFactory.java index 55f7cae723..393c0204b7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppTemplateFactory.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/AppTemplateFactory.java @@ -18,6 +18,7 @@ import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fit.jober.aipp.repository.AppTemplateRepository; + import modelengine.fitframework.annotation.Component; import java.util.Collections; @@ -32,15 +33,10 @@ @Component public class AppTemplateFactory { private final AppBuilderFlowGraphRepository flowGraphRepository; - private final AppBuilderConfigRepository configRepository; - private final AppBuilderFormRepository formRepository; - private final AppBuilderConfigPropertyRepository configPropertyRepository; - private final AppBuilderFormPropertyRepository formPropertyRepository; - private final AppTemplateRepository appTemplateRepository; public AppTemplateFactory(AppBuilderFlowGraphRepository flowGraphRepository, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/CheckerFactory.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/CheckerFactory.java index 7d3de76219..88a19254ce 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/CheckerFactory.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/factory/CheckerFactory.java @@ -13,8 +13,6 @@ import static modelengine.fit.jober.aipp.enums.NodeType.RETRIEVAL_NODE; import static modelengine.fit.jober.aipp.enums.NodeType.TOOL_INVOKE_NODE; -import modelengine.jade.store.service.PluginToolService; - import modelengine.fit.jade.aipp.model.service.AippModelCenter; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; @@ -24,6 +22,8 @@ import modelengine.fit.jober.aipp.service.impl.ManualCheckNodeChecker; import modelengine.fit.jober.aipp.service.impl.RetrievalNodeChecker; import modelengine.fit.jober.aipp.service.impl.ToolInvokeNodeChecker; +import modelengine.jade.store.service.PluginToolService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Initialize; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/AippLlmMeta.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/AippLlmMeta.java index 0ec4d84dab..f5d972a957 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/AippLlmMeta.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/AippLlmMeta.java @@ -6,15 +6,16 @@ package modelengine.fit.jober.aipp.fel; -import lombok.Getter; -import lombok.Setter; -import modelengine.fel.core.chat.Prompt; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.common.ErrorCodes; import modelengine.fit.jober.common.exceptions.JobberException; import modelengine.fit.jober.util.FlowDataUtils; + +import lombok.Getter; +import lombok.Setter; +import modelengine.fel.core.chat.Prompt; import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.util.ObjectUtils; @@ -30,36 +31,30 @@ @Getter public class AippLlmMeta { private List> flowData; - private Map businessData; - private OperationContext context; - private String versionId; - private String instId; - private String flowDataId; @Setter private Prompt trace; - @Setter private Map promptMetadata; - private AippLlmMeta() { - } + private AippLlmMeta() {} /** * 根据businessData解析大模型节点元数据。 * * @param flowData 表示携带元数据的 {@link List}{@code <}{@link Map}{@code <}{@link String}{@code ,} - * {@link Object}{@code >}{@code >}。 + * {@link Object}{@code >}{@code >}。 * @return 返回表示大模型节点元数据的 {@link AippLlmMeta}。 */ public static AippLlmMeta parse(List> flowData) { AippLlmMeta aippLlmMeta = new AippLlmMeta(); - Validation.notEmpty(flowData, () -> new JobberException(ErrorCodes.INPUT_PARAM_IS_EMPTY, AippConst.FLOW_DATA)); + Validation.notEmpty(flowData, + () -> new JobberException(ErrorCodes.INPUT_PARAM_IS_EMPTY, AippConst.FLOW_DATA)); aippLlmMeta.flowData = flowData; aippLlmMeta.businessData = DataUtils.getBusiness(flowData); aippLlmMeta.versionId = ObjectUtils.cast(aippLlmMeta.businessData.get(AippConst.BS_META_VERSION_ID_KEY)); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/WaterFlowAgent.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/WaterFlowAgent.java index bb3f2e41b6..d48303c08e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/WaterFlowAgent.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fel/WaterFlowAgent.java @@ -6,8 +6,6 @@ package modelengine.fit.jober.aipp.fel; -import com.alibaba.fastjson.JSONObject; - import modelengine.fel.core.chat.ChatMessage; import modelengine.fel.core.chat.ChatModel; import modelengine.fel.core.chat.ChatOption; @@ -41,13 +39,10 @@ */ public class WaterFlowAgent extends AbstractAgent { private static final String AGENT_MSG_KEY = "water_flow_agent_request"; - private static final String GOTO_NODE_ID = "ahead_llm_node"; private final ToolProvider toolProvider; - private final ChatStreamModel model; - private final String agentMsgKey; public WaterFlowAgent(ToolProvider toolProvider, ChatModel chatStreamModel, ChatOption option) { @@ -73,8 +68,8 @@ protected AiProcessFlow buildFlow() { private ChatMessage handleTool(ChatMessage input, StateContext ctx) { Validation.notNull(ctx, "The state context cannot be null."); - Map toolContext = ObjectUtils.getIfNull(ctx.getState(AippConst.TOOL_CONTEXT_KEY), - Collections::emptyMap); + Map toolContext = + ObjectUtils.getIfNull(ctx.getState(AippConst.TOOL_CONTEXT_KEY), Collections::emptyMap); ChatMessages lastRequest = ctx.getState(this.agentMsgKey); lastRequest.add(Validation.notNull(input, "The input message cannot be null.")); lastRequest.addAll(AbstractAgent.toolCallHandle(this.toolProvider, input, toolContext).messages()); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallback.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallback.java index 5aed170a80..b28e56990d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallback.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallback.java @@ -10,20 +10,21 @@ import modelengine.fit.jade.aipp.formatter.constant.Constant; import modelengine.fit.jade.aipp.formatter.support.ResponsibilityResult; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; import modelengine.fit.jober.aipp.entity.AippFlowData; import modelengine.fit.jober.aipp.entity.AippLogData; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; import modelengine.fit.jober.aipp.events.InsertConversationEnd; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.genericable.AppFlowFinishObserver; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppBuilderFormService; @@ -31,6 +32,8 @@ import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.aipp.util.FormUtils; import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; import modelengine.fit.waterflow.spi.FlowCallbackService; import modelengine.fitframework.annotation.Component; @@ -68,53 +71,40 @@ @Component public class AippFlowEndCallback implements FlowCallbackService { private static final Logger log = Logger.get(AippFlowEndCallback.class); - private static final String DEFAULT_END_FORM_VERSION = "1.0.0"; - private static final String CHECK_TIP = "获取到的结果为 null,请检查配置。"; - private static final Map LOG_STRATEGY = MapBuilder.get() .put(Constant.DEFAULT, AippInstLogType.MSG) .put(Constant.LLM_OUTPUT, AippInstLogType.META_MSG) .build(); - private final MetaService metaService; - private final AippLogService aippLogService; - private final BrokerClient brokerClient; - private final BeanContainer beanContainer; - private final ConversationRecordService conversationRecordService; - private final AppBuilderFormService formService; - - private final AppBuilderAppFactory appFactory; - - private final MetaInstanceService metaInstanceService; - private final AppChatSseService appChatSseService; - private final OutputFormatterChain formatterChain; - private final FitRuntime fitRuntime; + private final AppTaskInstanceService appTaskInstanceService; + private final AppTaskService appTaskService; + private final AppVersionService appVersionService; - public AippFlowEndCallback(@Fit MetaService metaService, @Fit AippLogService aippLogService, - @Fit BrokerClient brokerClient, @Fit BeanContainer beanContainer, - @Fit ConversationRecordService conversationRecordService, @Fit AppBuilderFormService formService, - @Fit MetaInstanceService metaInstanceService, @Fit AppChatSseService appChatSseService, - @Fit AppBuilderAppFactory appFactory, @Fit OutputFormatterChain formatterChain, FitRuntime fitRuntime) { + public AippFlowEndCallback(@Fit AippLogService aippLogService, @Fit BrokerClient brokerClient, + @Fit BeanContainer beanContainer, @Fit ConversationRecordService conversationRecordService, + @Fit AppBuilderFormService formService, @Fit AppChatSseService appChatSseService, + @Fit OutputFormatterChain formatterChain, @Fit AppTaskInstanceService appTaskInstanceService, + @Fit AppTaskService appTaskService, @Fit AppVersionService appVersionService, FitRuntime fitRuntime) { this.formService = formService; - this.metaService = metaService; this.aippLogService = aippLogService; this.brokerClient = brokerClient; this.beanContainer = beanContainer; - this.metaInstanceService = metaInstanceService; this.conversationRecordService = conversationRecordService; this.appChatSseService = appChatSseService; - this.appFactory = appFactory; this.formatterChain = formatterChain; + this.appTaskInstanceService = appTaskInstanceService; + this.appTaskService = appTaskService; + this.appVersionService = appVersionService; this.fitRuntime = fitRuntime; } @@ -125,14 +115,17 @@ public void callback(List> contexts) { log.debug("AippFlowEndCallback businessData {}", businessData); String versionId = ObjectUtils.cast(businessData.get(AippConst.BS_META_VERSION_ID_KEY)); - OperationContext context = JsonUtils.parseObject( - ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), OperationContext.class); - Meta meta = this.metaService.retrieve(versionId, context); + OperationContext context = + JsonUtils.parseObject( + ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), OperationContext.class); + + AppTask appTask = this.appTaskService.getTaskById(versionId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task[{0}] not found.", versionId))); String aippInstId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); - this.saveInstance(businessData, versionId, aippInstId, context, meta); - Map attr = meta.getAttributes(); + this.saveInstance(businessData, versionId, aippInstId, context, appTask); String parentInstanceId = ObjectUtils.cast(businessData.get(AippConst.PARENT_INSTANCE_ID)); - String appId = ObjectUtils.cast(attr.get(AippConst.ATTR_APP_ID_KEY)); + String appId = ObjectUtils.cast(appTask.getEntity().getAppId()); businessData.put(AippConst.ATTR_APP_ID_KEY, appId); // 表明流程结果是否需要再经过模型加工,当前场景全为false。 // 正常情况下应该是在结束节点配上该key并放入businessData中,此处模拟该过程。 @@ -144,20 +137,19 @@ public void callback(List> contexts) { String endFormVersion = DEFAULT_END_FORM_VERSION; AppBuilderForm appBuilderForm = this.formService.selectWithId(endFormId); Map formDataMap = FormUtils.buildFormData(businessData, appBuilderForm, parentInstanceId); - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String atChatId = ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID)); + + RunContext runContext = new RunContext(businessData, context); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); String returnedLogId = null; if (StringUtils.isNotEmpty(endFormId) && StringUtils.isNotEmpty(endFormVersion)) { returnedLogId = this.saveFormToLog(appId, businessData, endFormId, endFormVersion, formDataMap); } - AppChatRsp appChatRsp = AppChatRsp.builder() - .chatId(chatId) - .atChatId(atChatId) + AppChatRsp appChatRsp = AppChatRsp.builder().chatId(chatId).atChatId(atChatId) .status(FlowTraceStatus.ARCHIVED.name()) - .answer(Collections.singletonList( - AppChatRsp.Answer.builder().content(formDataMap).type(AippInstLogType.FORM.name()).build())) - .instanceId(aippInstId) - .logId(returnedLogId) + .answer(Collections.singletonList(AppChatRsp.Answer.builder() + .content(formDataMap).type(AippInstLogType.FORM.name()).build())) + .instanceId(aippInstId).logId(returnedLogId) .build(); this.appChatSseService.sendLastData(aippInstId, appChatRsp); this.insertConversation(businessData, aippInstId, ObjectUtils.cast(businessData.get("chartsData"))); @@ -176,24 +168,19 @@ public void callback(List> contexts) { } private void saveInstance(Map businessData, String versionId, String aippInstId, - OperationContext context, Meta meta) { - InstanceDeclarationInfo declarationInfo = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.ARCHIVED.name()) - .build(); - businessData.forEach((key, value) -> { - if (meta.getProperties().stream().anyMatch(item -> item.getName().equals(key))) { - declarationInfo.getInfo().getValue().put(key, value); - } - }); - this.metaInstanceService.patchMetaInstance(versionId, aippInstId, declarationInfo, context); + OperationContext context, AppTask appTask) { + this.appTaskInstanceService.update(AppTaskInstance.asUpdate(versionId, aippInstId) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.ARCHIVED.name()) + .fetch(businessData, appTask.getEntity().getProperties()) + .build(), context); } private String saveFormToLog(String appId, Map businessData, String endFormId, String endFormVersion, Map formDataMap) { - AppBuilderApp app = this.appFactory.create(appId); - AippLogData logData = FormUtils.buildLogDataWithFormData(app.getFormProperties(), endFormId, endFormVersion, - businessData); + AppVersion appVersion = this.appVersionService.retrieval(appId); + AippLogData logData = FormUtils.buildLogDataWithFormData(appVersion.getFormProperties(), endFormId, + endFormVersion, businessData); logData.setFormAppearance(JsonUtils.toJsonString(formDataMap.get(AippConst.FORM_APPEARANCE_KEY))); logData.setFormData(JsonUtils.toJsonString(formDataMap.get(AippConst.FORM_DATA_KEY))); // 子应用/工作流的结束节点表单不需要在历史记录展示 @@ -231,12 +218,13 @@ private void insertConversation(Map businessData, String aippIns // 评估调用接口时不记录历史会话 Object isEval = businessData.get(AippConst.IS_EVAL_INVOCATION); if (isEval == null || !ObjectUtils.cast(isEval)) { - OperationContext context = JsonUtils.parseObject( - ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), OperationContext.class); + OperationContext context = + JsonUtils.parseObject(ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), + OperationContext.class); // 构造用户历史对话记录并插表 - String resumeDuration = ObjectUtils.cast( - businessData.getOrDefault(AippConst.INST_RESUME_DURATION_KEY, "0")); + String resumeDuration = + ObjectUtils.cast(businessData.getOrDefault(AippConst.INST_RESUME_DURATION_KEY, "0")); Object createTimeObj = Validation.notNull(businessData.get(AippConst.INSTANCE_START_TIME), "The create time cannot be null."); LocalDateTime createTime = LocalDateTime.parse(createTimeObj.toString()); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandle.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandle.java index 3da357bca9..e19e1166e1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandle.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandle.java @@ -7,18 +7,16 @@ package modelengine.fit.jober.aipp.fitable; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.entity.AippInstLog; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppChatSessionService; import modelengine.fit.jober.aipp.util.DataUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; import modelengine.fit.waterflow.entity.FlowErrorInfo; import modelengine.fit.waterflow.spi.FlowExceptionService; import modelengine.fitframework.annotation.Component; @@ -37,6 +35,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; /** * 流程异常处理服务 @@ -47,38 +46,35 @@ @Component public class AippFlowExceptionHandle implements FlowExceptionService { private static final Logger log = Logger.get(AippFlowExceptionHandle.class); - private static final String UI_WORD_KEY = "aipp.fitable.AippFlowExceptionHandle"; - private static final String UI_WORD_KEY_HINT = "aipp.fitable.AippFlowExceptionHandle.hint"; private final AippLogService aippLogService; - - private final MetaInstanceService metaInstanceService; - private final LocaleService localeService; - private final ToolExceptionHandle toolExceptionHandle; - private final AppChatSessionService appChatSessionService; - private final BrokerClient brokerClient; + private final AppTaskInstanceService appTaskInstanceService; - public AippFlowExceptionHandle(@Fit AippLogService aippLogService, @Fit MetaInstanceService metaInstanceService, - @Fit LocaleService localeService, @Fit AppChatSessionService appChatSessionService, - @Fit ToolExceptionHandle toolExceptionHandle, @Fit BrokerClient brokerClient) { + public AippFlowExceptionHandle(@Fit AippLogService aippLogService, @Fit LocaleService localeService, + @Fit AppChatSessionService appChatSessionService, @Fit ToolExceptionHandle toolExceptionHandle, + @Fit BrokerClient brokerClient, @Fit AppTaskInstanceService appTaskInstanceService) { this.aippLogService = aippLogService; - this.metaInstanceService = metaInstanceService; this.localeService = localeService; this.appChatSessionService = appChatSessionService; this.toolExceptionHandle = toolExceptionHandle; this.brokerClient = brokerClient; + this.appTaskInstanceService = appTaskInstanceService; } private void addErrorLog(String aippInstId, List> contexts, boolean enableErrorDetails, Locale locale, String errorMessage) { - Instance instance = MetaInstanceUtils.getInstanceDetailByInstanceId(aippInstId, null, this.metaInstanceService); - String instanceStatus = instance.getInfo().get(AippConst.INST_STATUS_KEY); + Optional instanceOptional = this.appTaskInstanceService.getInstanceById(aippInstId, null); + if (instanceOptional.isEmpty()) { + return; + } + String instanceStatus = + ObjectUtils.cast(instanceOptional.get().getEntity().getInfos().get(AippConst.INST_STATUS_KEY)); if (MetaInstStatusEnum.TERMINATED.name().equals(instanceStatus)) { log.debug("Aipp instance is already terminated. [aippInstId={}]", aippInstId); return; @@ -111,17 +107,19 @@ private void addErrorLog(String aippInstId, List> contexts, public void handleException(String nodeId, List> contexts, FlowErrorInfo errorMessage) { Map businessData = DataUtils.getBusiness(contexts); String versionId = ObjectUtils.cast(businessData.get(AippConst.BS_META_VERSION_ID_KEY)); - log.error("versionId {} nodeId {} errorMessage {}", versionId, nodeId, errorMessage.getErrorMessage()); + log.error("versionId {} nodeId {} errorMessage {}", + versionId, + nodeId, + errorMessage.getErrorMessage()); log.debug("handleException businessData {}", businessData); String aippInstId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); - InstanceDeclarationInfo declarationInfo = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.ERROR.name()) - .build(); OperationContext context = DataUtils.getOpContext(businessData); - metaInstanceService.patchMetaInstance(versionId, aippInstId, declarationInfo, context); + this.appTaskInstanceService.update(AppTaskInstance.asUpdate(versionId, aippInstId) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.ERROR.name()) + .build(), context); this.appChatSessionService.getSession(aippInstId).ifPresent(e -> { - this.toolExceptionHandle.handleFitException(errorMessage); + ToolExceptionHandle.handleFitException(errorMessage); String finalErrorMsg = this.toolExceptionHandle.getFixErrorMsg(errorMessage, e.getLocale(), true); boolean enableErrorDetails = e.isDebug() || isModelError(errorMessage); addErrorLog(aippInstId, contexts, enableErrorDetails, e.getLocale(), finalErrorMsg); @@ -138,8 +136,8 @@ public void handleException(String nodeId, List> contexts, F log.info("Call parent exception fitable successfully, fitableId:{}, aippInstId {}.", parentExceptionFitableId, aippInstId); } catch (FitException exception) { - log.error("Call parent exception fitable error, fitableId:{}, aippInstId {}.", parentExceptionFitableId, - aippInstId); + log.error("Call parent exception fitable error, fitableId:{}, aippInstId {}.", + parentExceptionFitableId, aippInstId); log.error("exception: ", exception); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandle.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandle.java index 22a9f3a265..3e7bd5d5cb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandle.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandle.java @@ -9,25 +9,25 @@ import static modelengine.fit.jober.aipp.constants.AippConst.BS_NODE_ID_KEY; import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_DATA_INTERNAL_KEY; -import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jober.FlowSmartFormService; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; import modelengine.fit.jober.aipp.entity.AippLogData; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AopAippLogService; import modelengine.fit.jober.aipp.service.AppBuilderFormService; @@ -37,13 +37,16 @@ import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.aipp.util.FormUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; + +import lombok.AllArgsConstructor; import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; import modelengine.fitframework.annotation.Component; -import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; import java.time.LocalDateTime; import java.util.Collections; @@ -57,45 +60,21 @@ * @since 2023-12-25 */ @Component +@AllArgsConstructor public class AippFlowSmartFormHandle implements FlowSmartFormService { private static final Logger log = Logger.get(AippFlowSmartFormHandle.class); - private static final String DEFAULT_CURR_FORM_VERSION = "1.0.0"; private final AppBuilderFormService formService; - - private final MetaInstanceService metaInstanceService; - private final AppChatSseService appChatSseService; - private final AippLogService aippLogService; - - private final AppBuilderAppFactory appFactory; - + private final AppTaskService appTaskService; + private final AppTaskInstanceService appTaskInstanceService; + private final AppVersionService appVersionService; private final FlowInstanceService flowInstanceService; - - private final MetaService metaService; - private final AopAippLogService aopAippLogService; - private final RuntimeInfoService runtimeInfoService; - public AippFlowSmartFormHandle(@Fit AppBuilderFormService formService, @Fit MetaInstanceService metaInstanceService, - AppChatSseService appChatSseService, @Fit AippLogService aippLogService, - @Fit AppBuilderAppFactory appFactory, @Fit FlowInstanceService flowInstanceService, - @Fit MetaService metaService, @Fit AopAippLogService aopAippLogService, - @Fit RuntimeInfoService runtimeInfoService) { - this.formService = formService; - this.metaInstanceService = metaInstanceService; - this.appChatSseService = appChatSseService; - this.aippLogService = aippLogService; - this.appFactory = appFactory; - this.flowInstanceService = flowInstanceService; - this.metaService = metaService; - this.aopAippLogService = aopAippLogService; - this.runtimeInfoService = runtimeInfoService; - } - /** * 智能表单处理 * @@ -119,25 +98,28 @@ public void handleSmartForm(List> contexts, String sheetId) this.exceptionHandler(businessData); return; } - String parentInstanceId = ObjectUtils.cast(businessData.get(AippConst.PARENT_INSTANCE_ID)); - String instanceId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String atChatId = ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID)); - String appId = ObjectUtils.cast(businessData.get(AippConst.CONTEXT_APP_ID)); - AppBuilderApp app = this.appFactory.create(appId); + RunContext runContext = new RunContext(businessData, new OperationContext()); + String parentInstanceId = runContext.getParentInstanceId(); + String instanceId = runContext.getTaskInstanceId(); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); + String appId = runContext.getAppId(); + AppVersion appVersion = this.appVersionService.retrieval(appId); Map formDataMap = FormUtils.buildFormData(businessData, appBuilderForm, parentInstanceId); formDataMap.put(AippConst.NODE_START_TIME_KEY, startTime); if (businessData.containsKey(BUSINESS_DATA_INTERNAL_KEY)) { formDataMap.put(BUSINESS_DATA_INTERNAL_KEY, businessData.get(BUSINESS_DATA_INTERNAL_KEY)); } formDataMap.put(BS_NODE_ID_KEY, nodeId); - String logId = this.insertFormLog(app.getFormProperties(), sheetId, businessData, formDataMap); + String logId = this.insertFormLog(appVersion.getFormProperties(), sheetId, businessData, formDataMap); AppChatRsp appChatRsp = AppChatRsp.builder() .chatId(chatId) .atChatId(atChatId) .status(FlowTraceStatus.RUNNING.name()) - .answer(Collections.singletonList( - AppChatRsp.Answer.builder().content(formDataMap).type(AippInstLogType.FORM.name()).build())) + .answer(Collections.singletonList(AppChatRsp.Answer.builder() + .content(formDataMap) + .type(AippInstLogType.FORM.name()) + .build())) .instanceId(instanceId) .logId(logId) .build(); @@ -150,26 +132,35 @@ public void handleSmartForm(List> contexts, String sheetId) * @param businessData 表示业务数据 */ private void exceptionHandler(Map businessData) { - OperationContext context = JsonUtils.parseObject( - ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), OperationContext.class); + OperationContext context = + JsonUtils.parseObject(ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), + OperationContext.class); String instanceId = ObjectUtils.cast(businessData.get(AippConst.CONTEXT_INSTANCE_ID)); - String versionId = this.metaInstanceService.getMetaVersionId(instanceId); - Meta meta = this.metaService.retrieve(versionId, context); - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, - this.metaInstanceService); - String flowTraceId = instDetail.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); + String taskId = this.appTaskInstanceService.getTaskId(instanceId); + AppTask task = this.appTaskService.retrieveById(taskId, context); + AppTaskInstance instance = this.appTaskInstanceService.getInstanceById(instanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + + // 终止所有流程. + String flowTraceId = instance.getEntity().getFlowTranceId(); this.flowInstanceService.terminateFlows(null, flowTraceId, Collections.emptyMap(), context); - InstanceDeclarationInfo info = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.TERMINATED.name()) + + // 修改实例. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(taskId, instanceId) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.TERMINATED.name()) .build(); - this.metaInstanceService.patchMetaInstance(versionId, instanceId, info, context); + this.appTaskInstanceService.update(updateEntity, + JsonUtils.parseObject(ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), + OperationContext.class)); + + // 插入日志. String message = AippErrCode.FORM_RUNNING_FAILED_CAUSE_NOT_EXISTED.getMessage(); this.aopAippLogService.insertLog(AippLogCreateDto.builder() - .aippId(meta.getId()) - .version(meta.getVersion()) - .aippType(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY))) - .aippType(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY))) + .aippId(task.getEntity().getAppSuiteId()) + .version(task.getEntity().getVersion()) + .aippType(task.getEntity().getAippType()) .instanceId(instanceId) .logType(AippInstLogType.ERROR.name()) .logData(JsonUtils.toJsonString(AippLogData.builder().msg(message).build())) @@ -181,8 +172,8 @@ private void exceptionHandler(Map businessData) { private String insertFormLog(List formProperties, String sheetId, Map businessData, Map formDataMap) { - AippLogData logData = FormUtils.buildLogDataWithFormData(formProperties, sheetId, DEFAULT_CURR_FORM_VERSION, - businessData); + AippLogData logData = + FormUtils.buildLogDataWithFormData(formProperties, sheetId, DEFAULT_CURR_FORM_VERSION, businessData); Object appearance = formDataMap.get(AippConst.FORM_APPEARANCE_KEY); logData.setFormAppearance(ObjectUtils.cast(JsonUtils.toJsonString(appearance))); logData.setFormData(ObjectUtils.cast(JsonUtils.toJsonString(formDataMap.get(AippConst.FORM_DATA_KEY)))); @@ -190,15 +181,15 @@ private String insertFormLog(List formProperties, String } private void updateInstance(String sheetId, String nodeId, Map businessData) { - InstanceDeclarationInfo declarationInfo = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_CURR_FORM_ID_KEY, sheetId) - .putInfo(AippConst.INST_CURR_FORM_VERSION_KEY, DEFAULT_CURR_FORM_VERSION) - .putInfo(AippConst.INST_CURR_NODE_ID_KEY, nodeId) - .putInfo(AippConst.INST_SMART_FORM_TIME_KEY, LocalDateTime.now()) + String taskId = ObjectUtils.cast(businessData.get(AippConst.BS_META_VERSION_ID_KEY)); + String taskInstanceId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(taskId, taskInstanceId) + .setFormId(sheetId) + .setFormVersion(DEFAULT_CURR_FORM_VERSION) + .setCurrentNodeId(nodeId) + .setSmartFormTime(LocalDateTime.now()) .build(); - - this.metaInstanceService.patchMetaInstance(ObjectUtils.cast(businessData.get(AippConst.BS_META_VERSION_ID_KEY)), - ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)), declarationInfo, + this.appTaskInstanceService.update(updateEntity, JsonUtils.parseObject(ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), OperationContext.class)); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListener.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListener.java index d0e4bee2c7..d50dff1d3a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListener.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListener.java @@ -6,15 +6,16 @@ package modelengine.fit.jober.aipp.fitable; -import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.waterflow.entity.JoberErrorInfo; +import modelengine.fit.waterflow.spi.FlowCallbackService; + +import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.common.FlowDataConstant; -import modelengine.fit.jober.util.FlowDataUtils; import modelengine.fit.waterflow.entity.FlowErrorInfo; -import modelengine.fit.waterflow.entity.JoberErrorInfo; -import modelengine.fit.waterflow.spi.FlowCallbackService; +import modelengine.fit.jober.util.FlowDataUtils; import modelengine.fit.waterflow.spi.FlowExceptionService; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; @@ -60,8 +61,8 @@ public void callback(List> contexts) { LOG.info("[AppNodeListener] handle callback. instanceId={}, parentInstanceId={}, parentFlowDataId={}", instanceId, parentInstanceId, parentFlowDataId); - List> executeInfo = FlowDataUtils.getExecuteInfo(businessData, - FlowDataUtils.getNodeId(contextData)); + List> executeInfo = + FlowDataUtils.getExecuteInfo(businessData, FlowDataUtils.getNodeId(contextData)); if (executeInfo.isEmpty()) { LOG.error("Can not find the node app output. parentInstanceId={}, parentFlowDataId={}, instanceId={}", parentInstanceId, parentFlowDataId, instanceId); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriber.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriber.java index 1ade931657..2430802395 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriber.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriber.java @@ -6,8 +6,9 @@ package modelengine.fit.jober.aipp.fitable; -import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.domains.business.RunContext; import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; import modelengine.fit.jober.aipp.entity.ChatSession; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; @@ -41,13 +42,9 @@ @Component public class FlowPublishSubscriber implements FlowPublishService { private final AppBuilderRuntimeInfoRepository runtimeInfoRepository; - private final RuntimeInfoService runtimeInfoService; - private final ToolExceptionHandle toolExceptionHandle; - private final AppChatSessionService appChatSessionService; - private final AppChatSseService appChatSSEService; /** @@ -73,9 +70,10 @@ public FlowPublishSubscriber(AppBuilderRuntimeInfoRepository runtimeInfoReposito @Override public void publishNodeInfo(FlowNodePublishInfo flowNodePublishInfo) { Map businessData = flowNodePublishInfo.getBusinessData(); - String aippInstId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String atChatId = ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID)); + RunContext runContext = new RunContext(businessData, new OperationContext()); + String aippInstId = runContext.getTaskInstanceId(); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); String stage = flowNodePublishInfo.getFlowContext() == null ? StringUtils.EMPTY : flowNodePublishInfo.getFlowContext().getStage(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/GetQaFromLog.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/GetQaFromLog.java index b60ae7e585..8c73c54475 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/GetQaFromLog.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/GetQaFromLog.java @@ -12,6 +12,7 @@ import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppInspirationService; import modelengine.fit.jober.aipp.util.JsonUtils; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; import modelengine.fitframework.log.Logger; @@ -39,8 +40,8 @@ public GetQaFromLog(AippLogService aippLogService) { @Override @Fitable(id = "GetQAFromLog") - public List> getCustomizedLogs(Map params, String aippId, String appType, - OperationContext context) { + public List> getCustomizedLogs(Map params, String aippId, + String appType, OperationContext context) { List logs = aippLogService.queryRecentLogsSinceResume(aippId, appType, context); List> res = new ArrayList<>(); for (AippInstLogDataDto log : logs) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/LlmComponent.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/LlmComponent.java index b16b3cd734..64d40696af 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/LlmComponent.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/LlmComponent.java @@ -16,6 +16,7 @@ import modelengine.fel.core.chat.support.ChatMessages; import modelengine.fel.core.chat.support.ToolMessage; import modelengine.fel.core.model.http.SecureConfig; +import modelengine.fel.core.model.http.ModelExtraHttpBody; import modelengine.fel.core.tool.ToolInfo; import modelengine.fel.core.tool.ToolProvider; import modelengine.fel.core.util.Tip; @@ -30,12 +31,13 @@ import modelengine.fit.jade.aipp.prompt.repository.PromptBuilderChain; import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.entity.AippLogData; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.ModelErrCode; @@ -55,7 +57,7 @@ import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; -import modelengine.fitframework.broker.client.BrokerClient; +import modelengine.fitframework.annotation.Property; import modelengine.fitframework.log.Logger; import modelengine.fitframework.parameterization.StringFormatException; import modelengine.fitframework.serialization.ObjectSerializer; @@ -65,7 +67,6 @@ import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; import modelengine.fitframework.util.UuidUtils; -import modelengine.jade.common.globalization.LocaleService; import java.util.ArrayList; import java.util.Collections; @@ -86,80 +87,56 @@ @Component public class LlmComponent implements FlowableService, FlowCallbackService, FlowExceptionService { private static final Logger log = Logger.get(LlmComponent.class); - private static final String SYSTEM_PROMPT = "{{0}}"; - private static final String PROMPT_TEMPLATE = "{{1}}"; - private static final String AGENT_NODE_ID = "agent"; - - private static final String UI_WORD_KEY = "aipp.fitable.LlmComponent"; - private static final String REGEX_MODEL = "statusCode=(\\d+)"; - - private static final String WORKFLOW_CALLBACK_FITABLE_ID = - "modelengine.fit.jober.aipp.fitable.LLMComponentCallback"; - + private static final String WORKFLOW_CALLBACK_FITABLE_ID = "modelengine.fit.jober.aipp.fitable.LLMComponentCallback"; private static final String WORKFLOW_EXCEPTION_FITABLE_ID = "modelengine.fit.jober.aipp.fitable.LLMComponentException"; - private static final String TOOL_UNIQUE_NAME = "toolUniqueName"; - private static final String TOOL_NAME = "name"; // 暂时使用ConcurrentHashMap存储父节点的元数据 private final ConcurrentHashMap llmCache = new ConcurrentHashMap<>(); private final FlowInstanceService flowInstanceService; - - private final MetaInstanceService metaInstanceService; - private final ToolProvider toolProvider; - private final AiProcessFlow agentFlow; - private final AippLogService aippLogService; - private final AippLogStreamService aippLogStreamService; - - private final BrokerClient client; - private final ObjectSerializer serializer; - - private final LocaleService localeService; - private final AippModelCenter aippModelCenter; - private final PromptBuilderChain promptBuilderChain; + private final AppTaskInstanceService appTaskInstanceService; /** * 大模型节点构造器,内部通过提供的 agent 和 tool 构建智能体工作流。 * * @param flowInstanceService 表示流程实例服务的 {@link FlowInstanceService}。 - * @param metaInstanceService 表示元数据实例服务的 {@link MetaInstanceService}。 * @param toolProvider 表示具提供者功能的 {@link ToolProvider}。 * @param agent 表示提供智能体功能的 {@link AbstractAgent}{@code <}{@link ChatMessages}{@code , * }{@link ChatMessages}{@code >}。 * @param aippLogService 表示提供日志服务的 {@link AippLogService}。 * @param aippLogStreamService 表示提供日志流服务的 {@link AippLogStreamService}。 - * @param client 表示消息代理客户端的 {@link BrokerClient}。 * @param serializer 表示序列化器的 {@link ObjectSerializer}。 - * @param localeService 表示界面词国际化转换器的 {@link LocaleService}。 * @param aippModelCenter 表示模型中心的 {@link AippModelCenter}。 * @param promptBuilderChain 表示提示器构造器职责链的 {@link PromptBuilderChain}。 + * @param appTaskInstanceService 表示任务实例服务的 {@link AppTaskInstanceService}。 */ - public LlmComponent(FlowInstanceService flowInstanceService, MetaInstanceService metaInstanceService, + public LlmComponent(FlowInstanceService flowInstanceService, ToolProvider toolProvider, @Fit(alias = AippConst.WATER_FLOW_AGENT_BEAN) AbstractAgent agent, - AippLogService aippLogService, AippLogStreamService aippLogStreamService, BrokerClient client, - @Fit(alias = "json") ObjectSerializer serializer, LocaleService localeService, - AippModelCenter aippModelCenter, PromptBuilderChain promptBuilderChain) { + AippLogService aippLogService, + AippLogStreamService aippLogStreamService, + @Fit(alias = "json") ObjectSerializer serializer, + AippModelCenter aippModelCenter, + PromptBuilderChain promptBuilderChain, + AppTaskInstanceService appTaskInstanceService) { this.flowInstanceService = flowInstanceService; - this.metaInstanceService = metaInstanceService; this.toolProvider = toolProvider; this.aippLogService = aippLogService; this.aippLogStreamService = aippLogStreamService; - this.client = client; this.serializer = notNull(serializer, "The serializer cannot be nul."); this.aippModelCenter = aippModelCenter; @@ -169,8 +146,8 @@ public LlmComponent(FlowInstanceService flowInstanceService, MetaInstanceService .id(AGENT_NODE_ID) .delegate(agent) .close(); - this.localeService = localeService; this.promptBuilderChain = promptBuilderChain; + this.appTaskInstanceService = appTaskInstanceService; } /** @@ -235,17 +212,12 @@ public void handleException(String nodeId, List> contexts, F : ObjectUtils.cast(toolInfoList.get(0).parameters().getOrDefault(TOOL_NAME, toolUniqueName)); String parentFlowDataId = llmMeta.getFlowDataId(); log.info("[LlmComponent] handle exception start. instanceId={}, parentInstanceId={}, parentFlowDataId={}", - instanceId, - parentInstanceId, - parentFlowDataId); + instanceId, parentInstanceId, parentFlowDataId); this.failLlmComponentNode(llmMeta, new JoberErrorInfo(StringUtils.format("[{0}] {1}", toolName, errorInfo.getErrorMessage()), - errorInfo.getErrorCode(), - errorInfo.getArgs())); + errorInfo.getErrorCode(), errorInfo.getArgs())); log.info("[LlmComponent] handle exception end. instanceId={}, parentInstanceId={}, parentFlowDataId={}", - instanceId, - parentInstanceId, - parentFlowDataId); + instanceId, parentInstanceId, parentFlowDataId); } /** @@ -268,6 +240,7 @@ public List> handleTask(List> flowData) if (!this.checkModelAvailable(businessData)) { this.doOnAgentError(llmMeta, "statusCode=500"); + return flowData; } // 待add多模态,期望使用image的url,当前传入的历史记录里面没有image @@ -286,13 +259,12 @@ public List> handleTask(List> flowData) agentFlow.converse() .bind((acc, chunk) -> { if (firstTokenFlag[0]) { - log.info("[perf] [{}] converse sendLog start, instId={}, chunk={}", - System.currentTimeMillis(), - instId, - chunk.text()); + log.info("[perf] [{}] converse sendLog start, instId={}, chunk={}", System.currentTimeMillis(), + instId, chunk.text()); firstTokenFlag[0] = false; streamMsgSender.sendMsg(chunk.text(), businessData); - log.info("[perf] [{}] converse sendLog end, instId={}", System.currentTimeMillis(), instId); + log.info("[perf] [{}] converse sendLog end, instId={}", System.currentTimeMillis(), + instId); return; } streamMsgSender.sendMsg(chunk.text(), businessData); @@ -391,21 +363,23 @@ private void addAnswer(AippLlmMeta llmMeta, String answer, Map p output.put("llmOutput", answer); output.put("reference", promptMetadata.getOrDefault(PROMPT_METADATA_KEY, Collections.emptyMap())); businessData.put("output", output); - InstanceDeclarationInfo info = InstanceDeclarationInfo.custom().putInfo("llmOutput", answer).build(); - this.metaInstanceService.patchMetaInstance(llmMeta.getVersionId(), - llmMeta.getInstId(), - info, - llmMeta.getContext()); + + // 修改taskInstance. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(llmMeta.getVersionId(), llmMeta.getInstId()) + .setLlmOutput(answer) + .build(); + this.appTaskInstanceService.update(updateEntity, + JsonUtils.parseObject(ObjectUtils.cast(businessData.get(AippConst.BS_HTTP_CONTEXT_KEY)), + OperationContext.class)); doOnAgentComplete(llmMeta); } private void setChildInstanceId(AippLlmMeta llmMeta, String childInstanceId) { - InstanceDeclarationInfo info = - InstanceDeclarationInfo.custom().putInfo(AippConst.INST_CHILD_INSTANCE_ID, childInstanceId).build(); - this.metaInstanceService.patchMetaInstance(llmMeta.getVersionId(), - llmMeta.getInstId(), - info, - llmMeta.getContext()); + // 修改taskInstance. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(llmMeta.getVersionId(), llmMeta.getInstId()) + .setChildInstanceId(childInstanceId) + .build(); + this.appTaskInstanceService.update(updateEntity, llmMeta.getContext()); } /** @@ -530,7 +504,9 @@ private ChatOption buildChatOptions(Map businessData) { String model = ObjectUtils.cast(businessData.get("model")); Map accessInfo = ObjectUtils.nullIf(ObjectUtils.cast(businessData.get("accessInfo")), MapBuilder.get().put("serviceName", model).put("tag", "INTERNAL").build()); - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); + + RunContext runContext = new RunContext(businessData, new OperationContext()); + String chatId = runContext.getOriginChatId(); OperationContext opContext = DataUtils.getOpContext(businessData); ModelAccessInfo modelAccessInfo = this.aippModelCenter.getModelAccessInfo(accessInfo.get("tag"), accessInfo.get("serviceName"), opContext); @@ -542,22 +518,34 @@ private ChatOption buildChatOptions(Map businessData) { .temperature(ObjectUtils.cast(businessData.get("temperature"))) .tools(this.toolProvider.getTool(skillNameList)) .user(opContext.getOperator()) + .extras(Collections.singletonList(new ModelExtraHttpBody(new ModelExtraBody(chatId)))) .build(); } + private static class ModelExtraBody { + @Property(name = "session_id") + private String sessionId; + + public ModelExtraBody(String sessionId) { + if (!UuidUtils.isUuidString(sessionId, true)) { + throw new IllegalArgumentException("Invalid session id. It should be 32 characters without hyphens."); + } + // 下层要求是一个标准的 uuid,理论上不应该有这个限制,后续应该可以放开,目前暂时做一次标准化 + this.sessionId = + sessionId.substring(0, 8) + "-" + sessionId.substring(8, 12) + "-" + sessionId.substring(12, 16) + + "-" + sessionId.substring(16, 20) + "-" + sessionId.substring(20); + } + } + static class StreamMsgSender { private final AippLogStreamService aippLogStreamService; - private final ObjectSerializer serializer; - private final String path; - private final String msgId; - private final String instId; - StreamMsgSender(AippLogStreamService aippLogStreamService, ObjectSerializer serializer, String path, - String msgId, String instId) { + StreamMsgSender(AippLogStreamService aippLogStreamService, ObjectSerializer serializer, + String path, String msgId, String instId) { this.aippLogStreamService = aippLogStreamService; this.serializer = serializer; this.path = path; @@ -600,8 +588,9 @@ private Boolean checkEnableLog(Map businessData) { } private void sendMsgHandle(String msg, StreamMsgType logType, Map businessData) { - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String atChatId = ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID)); + RunContext runContext = new RunContext(businessData, new OperationContext()); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); AippLogData logData = AippLogData.builder().msg(msg).build(); AippLogVO logVO = AippLogVO.builder() .logData(JsonUtils.toJsonString(logData)) diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/MemoryAfterResume.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/MemoryAfterResume.java index 6da2c40e1d..6d801425c7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/MemoryAfterResume.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/MemoryAfterResume.java @@ -12,6 +12,7 @@ import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppLogService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponent.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponent.java index eb01fdfcc0..c756b9c0bd 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponent.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponent.java @@ -6,12 +6,12 @@ package modelengine.fit.jober.aipp.fitable; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.waterflow.spi.FlowableService; import modelengine.jade.app.engine.knowledge.dto.KbVectorSearchDto; import modelengine.jade.app.engine.knowledge.service.KnowledgeBaseService; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; import modelengine.fitframework.log.Logger; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NodeAppComponent.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NodeAppComponent.java index 6c0b9c6115..830526297a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NodeAppComponent.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/fitable/NodeAppComponent.java @@ -6,16 +6,17 @@ package modelengine.fit.jober.aipp.fitable; -import lombok.AllArgsConstructor; -import lombok.Getter; import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.waterflow.spi.FlowableService; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.genericable.AippRunTimeService; import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.common.ErrorCodes; import modelengine.fit.jober.common.exceptions.JobberParamException; import modelengine.fit.jober.util.FlowDataUtils; -import modelengine.fit.waterflow.spi.FlowableService; + +import lombok.AllArgsConstructor; +import lombok.Getter; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; import modelengine.fitframework.inspection.Validation; @@ -56,11 +57,11 @@ public NodeAppComponent(AippRunTimeService aippRunTimeService) { * 节点执行应用的实现 * 该组件的入参格式如下: * { - * "aippId": "", - * "version": "", - * "inputParams": { - * "key1": "value1" - * } + * "aippId": "", + * "version": "", + * "inputParams": { + * "key1": "value1" + * } * } * * @param flowDataList 流程执行上下文数据 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java index 047ff103b5..07275a9a2b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippChatMapper.java @@ -58,7 +58,8 @@ List selectChatList(@Param("requestParam") QueryChatRequest reques * @param chatId 会话ID * @return 会话记录数目 */ - long getChatListCount(@Param("requestParam") QueryChatRequest request, @Param("chatId") String chatId, @Param("createBy") String createBy); + long getChatListCount(@Param("requestParam") QueryChatRequest request, @Param("chatId") String chatId, + @Param("createBy") String createBy); /** * 查询会话 @@ -115,6 +116,7 @@ List selectChat(@Param("chatId") String chatId, @Param("offset") Intege /** * 根据instance id列表批量查询对话消息 + * * @param instanceIds instance id列表 * @return 对应会话信息 */ @@ -178,4 +180,43 @@ List selectChat(@Param("chatId") String chatId, @Param("offset") Intege */ List selectChatByCondition(@Param("condition") Map condition, @Param("requestParam") QueryChatInfoRequest queryChatInfoRequest); + + /** + * 获取超期的对话唯一标识。 + * + * @param expiredDays 表示超期时长的 {@code int}。 + * @param limit 表示查询数量的 {@code int}。 + * @return 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + List getExpiredChatIds(int expiredDays, int limit); + + /** + * 根据对话标识列表强制删除对话。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + void forceDeleteChat(List chatIds); + + /** + * 根据对话标识列表强制删除对话和任务实例关系。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + void deleteWideRelationshipByChatIds(List chatIds); + + /** + * 根据对话唯一标识列表批量查询会话记录实体。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + * @return 表示会话记录实体列表的 {@link List}{@code <}{@link ChatInfo}{@code >}。 + */ + List selectByChatIds(@Param("chatIds") List chatIds); + + /** + * 根据对话唯一标识列表批量查询会话记录和任务实例的关系。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + * @return 表示会话记录和任务实例的关系的 {@link List}{@code <}{@link ChatAndInstanceMap}{@code >}。 + */ + List selectTaskInstanceRelationsByChatIds(@Param("chatIds") List chatIds); } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java index a309ecd39c..7114d8305d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java @@ -180,7 +180,7 @@ List selectRecentInstanceIdByAippIds(List aippIds, String aippTy * @param logTypes 表示指定日志类型列表的 {@link List}{@code <}{@link String}{@code >}。 * @return 表示 aipp 实例历史记录的 {@link List}{@code <}{@link AippInstLog}{@code >}。 */ - List getLogsByInstanceIdAndLogTypes(String instanceId, @Param("logTypes") List logTypes); + List getLogsByInstanceIdAndLogTypes(String instanceId, @Param("logTypes")List logTypes); /** * 删除指定实例的历史记录。 @@ -195,4 +195,28 @@ List selectRecentInstanceIdByAippIds(List aippIds, String aippTy * @param logIds 表示指定的历史记录 id 的 {@link List}{@code <}{@link Long}{@code >}。 */ void deleteInstanceLogs(@Param("logIds") List logIds); + + /** + * 获取超期的调试对话记录唯一标识列表。 + * + * @param expiredDays 表示超期时间的 {@code int}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示历史会话记录的id列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + List getExpireInstanceLogIds(String aippType, int expiredDays, int limit); + + /** + * 根据实例唯一标识列表强制删除会话记录。 + * + * @param logIds 表示会话实例id列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + void forceDeleteInstanceLogsByIds(List logIds); + + /** + * 根据日志唯一标识列表查询会话历史记录。 + * + * @param logIds 标识日志唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。 + * @return 表示实例历史记录列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。 + */ + List selectByLogIds(@Param("logIds") List logIds); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderAppMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderAppMapper.java index bdc98dfe11..4440f220ff 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderAppMapper.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderAppMapper.java @@ -37,6 +37,15 @@ public interface AppBuilderAppMapper { @Locale AppBuilderAppPo selectWithPath(String path); + /** + * 通过应用的id来查询版本列表. + * + * @param appSuiteId 应用id. + * @return {@link AppBuilderAppPo} 列表. + */ + @Locale + List selectByAppSuiteId(String appSuiteId); + /** * 根据租户 id 获取 App 数据对象。 * diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java index 9262a7182a..f1c0ccd569 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppBuilderRuntimeInfoMapper.java @@ -31,4 +31,20 @@ public interface AppBuilderRuntimeInfoMapper { * @param appBuilderRuntimeInfoPO {@link AppBuilderRuntimeInfoPo} 对象. */ void insertOne(AppBuilderRuntimeInfoPo appBuilderRuntimeInfoPO); + + /** + * 获取超期的运行时信息唯一标识列表。 + * + * @param expiredDays 表示超期时间的 {@code int}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示运行时信息唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + List getExpiredRuntimeInfos(int expiredDays, int limit); + + /** + * 根据运行时信息唯一标识列表强制删除会话记录。 + * + * @param runtimeInfoIds 表示运行时信息唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + void deleteRuntimeInfos(List runtimeInfoIds); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppChatNumMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppChatNumMapper.java index ad7b8c761e..1545f9206b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppChatNumMapper.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AppChatNumMapper.java @@ -29,4 +29,9 @@ public interface AppChatNumMapper { * @param chatMode 应用对话方式 */ void minusOne(String appId, String chatMode); + + /** + * 清空所有 app 计数 + */ + void clearNum(); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImpl.java index 5bef870e7b..92983e9e2a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImpl.java @@ -16,6 +16,7 @@ import modelengine.fit.jober.aipp.dto.chat.PromptInfo; import modelengine.fit.jober.aipp.genericable.adapter.AppBuilderPromptServiceAdapter; import modelengine.fit.jober.aipp.service.AppBuilderPromptService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.serialization.ObjectSerializer; @@ -32,20 +33,19 @@ @Component public class AppBuilderPromptServiceAdapterImpl implements AppBuilderPromptServiceAdapter { private final AppBuilderPromptService appBuilderPromptService; - private final ObjectSerializer objectSerializer; public AppBuilderPromptServiceAdapterImpl(AppBuilderPromptService appBuilderPromptService, @Fit(alias = "json") ObjectSerializer objectSerializer) { - this.appBuilderPromptService = notNull(appBuilderPromptService, - "The app builder prompt service cannot be null."); + this.appBuilderPromptService = + notNull(appBuilderPromptService, "The app builder prompt service cannot be null."); this.objectSerializer = notNull(objectSerializer, "The object serializer cannot be null."); } @Override public List listPromptCategories(String appId, OperationContext operationContext, boolean isDebug) { - Rsp> rsp = this.appBuilderPromptService.listPromptCategories(appId, - operationContext, isDebug); + Rsp> rsp = + this.appBuilderPromptService.listPromptCategories(appId, operationContext, isDebug); return rsp.getData() .stream() .map(appBuilderPromptCategoryDto -> this.objectSerializer.deserialize( @@ -56,8 +56,8 @@ public List listPromptCategories(String appId, OperationContext @Override public PromptInfo queryInspirations(String appId, String categoryId, OperationContext operationContext, boolean isDebug) { - Rsp rsp = this.appBuilderPromptService.queryInspirations(appId, categoryId, - operationContext, isDebug); + Rsp rsp = + this.appBuilderPromptService.queryInspirations(appId, categoryId, operationContext, isDebug); AppBuilderPromptDto appBuilderPromptDto = rsp.getData(); return this.appBuilderPromptDtoConvertToAdapter(appBuilderPromptDto); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppChatServiceAdapterImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppChatServiceAdapterImpl.java index adc2f7e4a9..347b4919b8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppChatServiceAdapterImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/AppChatServiceAdapterImpl.java @@ -13,6 +13,7 @@ import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; import modelengine.fit.jober.aipp.genericable.adapter.AppChatServiceAdapter; import modelengine.fit.jober.aipp.service.AppChatService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.flowable.Choir; @@ -29,7 +30,6 @@ @Component public class AppChatServiceAdapterImpl implements AppChatServiceAdapter { private final AppChatService appChatService; - private final ObjectSerializer serializer; public AppChatServiceAdapterImpl(AppChatService appChatService, @Fit(alias = "json") ObjectSerializer serializer) { @@ -39,8 +39,8 @@ public AppChatServiceAdapterImpl(AppChatService appChatService, @Fit(alias = "js @Override public Choir chat(String appId, ChatRequest params, OperationContext operationContext, boolean isDebug) { - CreateAppChatRequest createAppChatRequest = this.serializer.deserialize(this.serializer.serialize(params), - CreateAppChatRequest.class); + CreateAppChatRequest createAppChatRequest = + this.serializer.deserialize(this.serializer.serialize(params), CreateAppChatRequest.class); createAppChatRequest.setAppId(appId); return this.appChatService.chat(createAppChatRequest, operationContext, isDebug); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImpl.java index 5ccc8130b3..712394878b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImpl.java @@ -6,20 +6,18 @@ package modelengine.fit.jober.aipp.northbound; -import static modelengine.fitframework.inspection.Validation.notNull; - +import lombok.AllArgsConstructor; import modelengine.fit.http.entity.FileEntity; import modelengine.fit.http.entity.PartitionedEntity; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.chat.FileUploadInfo; import modelengine.fit.jober.aipp.genericable.adapter.FileServiceAdapter; import modelengine.fit.jober.aipp.service.FileService; import modelengine.fit.jober.aipp.util.AippFileUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.beans.BeanUtils; @@ -33,30 +31,21 @@ * @since 2024-12-20 */ @Component +@AllArgsConstructor public class FileServiceAdapterImpl implements FileServiceAdapter { private final FileService fileService; - - private final MetaService metaService; - - public FileServiceAdapterImpl(FileService fileService, MetaService metaService) { - this.fileService = notNull(fileService, "The file service cannot be null."); - this.metaService = notNull(metaService, "The meta service cannot be null."); - } + private final AppVersionService appVersionService; @Override public FileUploadInfo uploadFile(OperationContext context, String tenantId, String fileName, String appId, PartitionedEntity receivedFile) throws IOException { - String aippId; - try { - aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - } catch (AippTaskNotFoundException e) { - throw new AippException(AippErrCode.APP_NOT_FOUND); - } + AppVersion appVersion = this.appVersionService.retrieval(appId); List files = AippFileUtils.getFileEntity(receivedFile); if (files.isEmpty()) { throw new AippException(AippErrCode.UPLOAD_FAILED); } - return BeanUtils.copyProperties(this.fileService.uploadFile(context, tenantId, fileName, aippId, files.get(0)), - FileUploadInfo.class); + return BeanUtils.copyProperties( + this.fileService.uploadFile(context, tenantId, fileName, appVersion.getData().getAppSuiteId(), + files.get(0)), FileUploadInfo.class); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AippSystemConfigPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AippSystemConfigPo.java index 9fafadc452..c3a1b72df8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AippSystemConfigPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AippSystemConfigPo.java @@ -23,12 +23,8 @@ @NoArgsConstructor public class AippSystemConfigPo { private Long id; - private String configKey; - private String configValue; - private String configGroup; - private String configParent; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppPo.java index 9670a15cbb..7981821fec 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppPo.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.po; +import modelengine.fit.jober.aipp.aop.LocaleField; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.aop.LocaleField; import java.time.LocalDateTime; @@ -26,38 +27,30 @@ @NoArgsConstructor public class AppBuilderAppPo { private String id; - @LocaleField private String name; - private String tenantId; - private String configId; - private String flowGraphId; - private String type; - private String createBy; - private String updateBy; - private String version; - private LocalDateTime createAt; - private LocalDateTime updateAt; - @LocaleField private String attributes; - private String path; - private String state; - private String appBuiltType; - private String appCategory; - private String appType; + + // 新增字段. + private String appId; + private String appSuiteId; + private Boolean isActive; + private String status; + private String uniqueName; + private LocalDateTime publishAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppTypePo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppTypePo.java index 3f0652ebf5..b86fca9244 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppTypePo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderAppTypePo.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.po; +import modelengine.fit.jober.aipp.aop.LocaleField; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import modelengine.fit.jober.aipp.aop.LocaleField; import java.time.LocalDateTime; @@ -28,13 +29,9 @@ @Builder public class AppBuilderAppTypePo { private String id; - @LocaleField private String name; - private String tenantId; - private LocalDateTime createAt; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderComponentPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderComponentPo.java index ac2ae14621..08aa54f1e8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderComponentPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderComponentPo.java @@ -25,24 +25,14 @@ @NoArgsConstructor public class AppBuilderComponentPo { private String id; - private String name; - private String type; - private String description; - private String formId; - private String serviceId; - private String tenantId; - private String createBy; - private String updateBy; - private LocalDateTime createAt; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPo.java index 449cc590be..0d4379edba 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPo.java @@ -25,18 +25,11 @@ @NoArgsConstructor public class AppBuilderConfigPo { private String id; - private String formId; - private String tenantId; - private String appId; - private String createBy; - private String updateBy; - private LocalDateTime createAt; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPropertyPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPropertyPo.java index 1ca0c30a7b..a4183ddc19 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPropertyPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderConfigPropertyPo.java @@ -23,10 +23,7 @@ @NoArgsConstructor public class AppBuilderConfigPropertyPo { private String id; - private String nodeId; - private String formPropertyId; - private String configId; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFlowGraphPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFlowGraphPo.java index 0c586bc9b6..4e06e7af28 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFlowGraphPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFlowGraphPo.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.po; +import modelengine.fit.jober.aipp.aop.LocaleField; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.aop.LocaleField; import java.time.LocalDateTime; @@ -26,17 +27,11 @@ @NoArgsConstructor public class AppBuilderFlowGraphPo { private String id; - private String name; - @LocaleField private String appearance; - private String createBy; - private String updateBy; - private LocalDateTime createAt; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPo.java index 51466c58bc..a5c2647c76 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPo.java @@ -25,24 +25,14 @@ @NoArgsConstructor public class AppBuilderFormPo { private String id; - private String name; - private String tenantId; - private String appearance; - private String createBy; - private String updateBy; - private String type; - private LocalDateTime createAt; - private LocalDateTime updateAt; - private String version; - private String formSuiteId; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPropertyPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPropertyPo.java index ffdfab3b6c..14ad585808 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPropertyPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderFormPropertyPo.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.po; +import modelengine.fit.jober.aipp.aop.LocaleField; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.aop.LocaleField; /** * AppBuilder表单属性结构体 @@ -24,24 +25,15 @@ @Builder public class AppBuilderFormPropertyPo { private String id; - private String formId; - private String name; - private String dataType; - private String appId; - @LocaleField private String defaultValue; - private String from; - private String group; - @LocaleField private String description; - private int index; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderRuntimeInfoPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderRuntimeInfoPo.java index cfb988af03..86ad66437a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderRuntimeInfoPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppBuilderRuntimeInfoPo.java @@ -25,37 +25,22 @@ @AllArgsConstructor public class AppBuilderRuntimeInfoPo { private String traceId; - private String flowDefinitionId; - private String instanceId; - private String nodeId; - private String nodeType; - private long startTime; - private long endTime; - private String status; - private int published; - private String errorMsg; - private String nextPositionId; - private String parameters; /* ------------ 公共字段 ------------ */ private Long id; - private String createBy; - private String updateBy; - private LocalDateTime createAt; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppTemplatePo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppTemplatePo.java index 4b7be915ff..828b6bb140 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppTemplatePo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/AppTemplatePo.java @@ -25,34 +25,19 @@ @Builder public class AppTemplatePo { private String id; - private String name; - private String builtType; - private String category; - private String attributes; - private String appType; - private long like; - private long collection; - private long usage; - private String version; - private String configId; - private String flowGraphId; - private String createBy; - private LocalDateTime createAt; - private String updateBy; - private LocalDateTime updateAt; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/I18nPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/I18nPo.java index e299da32dc..701c309f7f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/I18nPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/I18nPo.java @@ -23,10 +23,7 @@ @NoArgsConstructor public class I18nPo { private String id; - private String key; - private String language; - private String value; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/InspirationPo.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/InspirationPo.java index b15f26cefe..2edc14b466 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/InspirationPo.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/po/InspirationPo.java @@ -23,14 +23,9 @@ @NoArgsConstructor public class InspirationPo { private String aippId; - private String parentId; - private String categoryId; - private String inspirationId; - private String value; - private String createUser; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java new file mode 100644 index 0000000000..5c997cc12d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippChatRepository.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository; + +import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; +import modelengine.fit.jober.aipp.entity.ChatInfo; + +import java.util.List; + +/** + * 应用对话的存储仓库。 + * + * @author 杨祥宇 + * @since 2025-04-09 + */ +public interface AippChatRepository { + /** + * 获取超期的对话唯一标识列表。 + * + * @param expiredDays 表示超期时长的 {@code int}。 + * @param limit 表示查询数量的 {@code int}。 + * @return 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + List getExpiredChatIds(int expiredDays, int limit); + + /** + * 根据对话标识列表强制删除对话。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + void forceDeleteChat(List chatIds); + + /** + * 根据对话唯一标识列表批量查询会话记录实体。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + * @return 表示会话记录实体列表的 {@link List}{@code <}{@link ChatInfo}{@code >}。 + */ + List selectByChatIds(List chatIds); + + /** + * 根据对话唯一标识列表批量查询会话记录和任务实例的关系。 + * + * @param chatIds 表示对话唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + * @return 表示会话记录和任务实例的关系的 {@link List}{@code <}{@link ChatAndInstanceMap}{@code >}。 + */ + List selectTaskInstanceRelationsByChatIds(List chatIds); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java new file mode 100644 index 0000000000..2d3d74e602 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AippInstanceLogRepository.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository; + +import modelengine.fit.jober.aipp.entity.AippInstLog; + +import java.util.List; + +/** + * 应用实例历史记录的存储仓库。 + * + * @author 杨祥宇 + * @since 2025-04-09 + */ +public interface AippInstanceLogRepository { + /** + * 获取调试类型的应用过期历史记录。 + * + * @param expiredDays 表示超期天数的 {@code int}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示超期历史记录id的 {@link List}{@code <}{@link Long}{@code >}。 + */ + List getExpireInstanceLogIds(String aippType, int expiredDays, int limit); + + /** + * 根据日志唯一标识列表强制删除历史记录。 + * + * @param logIds 表示历史记录的唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + void forceDeleteInstanceLogs(List logIds); + + /** + * 根据日志唯一标识列表查询会话历史记录 + * + * @param logIds 标识日志唯一标识列表的 {@link List}{@code <}{@link Long}{@code >}。 + * @return 表示实例历史记录列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。 + */ + List selectByLogIds(List logIds); +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderAppRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderAppRepository.java index d85fd3634c..4f6f6ad4e2 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderAppRepository.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderAppRepository.java @@ -35,7 +35,8 @@ public interface AppBuilderAppRepository { * @param limit 表示获取到的个数的 {@code int}。 * @return 表示获取到的最新的 app 对象列表的 {@link List}{@code <}{@link AppBuilderApp}{@code >}。 */ - List selectWithLatestApp(AppQueryCondition cond, String tenantId, long offset, int limit); + List selectWithLatestApp(AppQueryCondition cond, String tenantId, + long offset, int limit); /** * 根据查询条件获取的 app 对象列表。 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java index 55b043d806..19d756c6dc 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/AppBuilderRuntimeInfoRepository.java @@ -31,4 +31,20 @@ public interface AppBuilderRuntimeInfoRepository { * @param info {@link AppBuilderRuntimeInfo} 运行时信息. */ void insertOne(AppBuilderRuntimeInfo info); + + /** + * 获取运行时信息过期历史记录。 + * + * @param expiredDays 表示超期天数的 {@code int}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示超期运行时信息id的 {@link List}{@code <}{@link Long}{@code >}。 + */ + List getExpiredRuntimeInfos(int expiredDays, int limit); + + /** + * 根据运行时信息id列表强制删除历史记录。 + * + * @param runtimeInfoIds 表示历史记录的id列表的 {@link List}{@code <}{@link Long}{@code >}。 + */ + void deleteRuntimeInfos(List runtimeInfoIds); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java new file mode 100644 index 0000000000..71a3a0954f --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippChatRepositoryImpl.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository.impl; + +import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; +import modelengine.fit.jober.aipp.entity.ChatInfo; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.repository.AippChatRepository; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.transaction.Transactional; +import modelengine.fitframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link AippChatRepository} 对应实现类。 + * + * @author 杨祥宇 + * @since 2025-04-09 + */ +@Component +public class AippChatRepositoryImpl implements AippChatRepository { + private final AippChatMapper aippChatMapper; + + /** + * 表示用对话持久层构造 {@link AippChatRepositoryImpl} 的实例。 + * + * @param aippChatMapper 表示对话持久层实例的 {@link AippChatMapper}。 + */ + public AippChatRepositoryImpl(AippChatMapper aippChatMapper) {this.aippChatMapper = aippChatMapper;} + + @Override + public List getExpiredChatIds(int expiredDays, int limit) { + return this.aippChatMapper.getExpiredChatIds(expiredDays, limit); + } + + @Override + @Transactional + public void forceDeleteChat(List chatIds) { + if (CollectionUtils.isEmpty(chatIds)) { + return; + } + this.aippChatMapper.forceDeleteChat(chatIds); + this.aippChatMapper.deleteWideRelationshipByChatIds(chatIds); + } + + @Override + public List selectByChatIds(List chatIds) { + if (CollectionUtils.isEmpty(chatIds)) { + return new ArrayList<>(); + } + return this.aippChatMapper.selectByChatIds(chatIds); + } + + @Override + public List selectTaskInstanceRelationsByChatIds(List chatIds) { + if (CollectionUtils.isEmpty(chatIds)) { + return new ArrayList<>(); + } + return this.aippChatMapper.selectTaskInstanceRelationsByChatIds(chatIds); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java new file mode 100644 index 0000000000..394a605d45 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImpl.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository.impl; + +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; +import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.CollectionUtils; + +import java.util.List; + +/** + * {@link AippInstanceLogRepository} 对应实现类。 + * + * @author 杨祥宇 + * @since 2025-04-09 + */ +@Component +public class AippInstanceLogRepositoryImpl implements AippInstanceLogRepository { + private final AippLogMapper aippLogMapper; + + /** + * 表示用日志持久层构造 {@link AippInstanceLogRepositoryImpl} 的实例。 + * + * @param aippLogMapper 表示日志持久层实例的 {@link AippLogMapper}。 + */ + public AippInstanceLogRepositoryImpl(AippLogMapper aippLogMapper) {this.aippLogMapper = aippLogMapper;} + + @Override + public List getExpireInstanceLogIds(String aippType, int expiredDays, int limit) { + return this.aippLogMapper.getExpireInstanceLogIds(aippType, expiredDays, limit); + } + + @Override + public void forceDeleteInstanceLogs(List logIds) { + if (CollectionUtils.isEmpty(logIds)) { + return; + } + this.aippLogMapper.forceDeleteInstanceLogsByIds(logIds); + } + + @Override + public List selectByLogIds(List logIds) { + return this.aippLogMapper.selectByLogIds(logIds); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippSystemConfigRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippSystemConfigRepositoryImpl.java index eccc61a12c..98243e14e9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippSystemConfigRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AippSystemConfigRepositoryImpl.java @@ -10,6 +10,7 @@ import modelengine.fit.jober.aipp.mapper.AippSystemConfigMapper; import modelengine.fit.jober.aipp.repository.AippSystemConfigRepository; import modelengine.fit.jober.aipp.serializer.impl.AippSystemConfigSerializer; + import modelengine.fitframework.annotation.Component; import java.util.Optional; @@ -23,7 +24,6 @@ @Component public class AippSystemConfigRepositoryImpl implements AippSystemConfigRepository { private final AippSystemConfigMapper aippSystemConfigMapper; - private final AippSystemConfigSerializer serializer; public AippSystemConfigRepositoryImpl(AippSystemConfigMapper aippSystemConfigMapper) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderAppRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderAppRepositoryImpl.java index f24fab5c95..ef17bd5325 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderAppRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderAppRepositoryImpl.java @@ -27,7 +27,6 @@ @Component public class AppBuilderAppRepositoryImpl implements AppBuilderAppRepository { private final AppBuilderAppMapper appBuilderAppMapper; - private final AppBuilderAppSerializer serializer; public AppBuilderAppRepositoryImpl(AppBuilderAppMapper appBuilderAppMapper) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderComponentRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderComponentRepositoryImpl.java index 9910a1642c..8d48f3225c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderComponentRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderComponentRepositoryImpl.java @@ -21,7 +21,6 @@ @Component public class AppBuilderComponentRepositoryImpl implements AppBuilderComponentRepository { private final AppBuilderComponentMapper appBuilderComponentMapper; - private final AppBuilderComponentSerializer serializer; public AppBuilderComponentRepositoryImpl(AppBuilderComponentMapper appBuilderComponentMapper) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigPropertyRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigPropertyRepositoryImpl.java index f8be9ef5a9..15b5bd3183 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigPropertyRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigPropertyRepositoryImpl.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.po.AppBuilderConfigPropertyPo; import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; import modelengine.fit.jober.aipp.serializer.impl.AppBuilderConfigPropertySerializer; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.CollectionUtils; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigRepositoryImpl.java index 8f22e15515..9ee861eb9a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderConfigRepositoryImpl.java @@ -12,6 +12,7 @@ import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; import modelengine.fit.jober.aipp.serializer.impl.AppBuilderConfigSerializer; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.CollectionUtils; @@ -27,9 +28,7 @@ @Component public class AppBuilderConfigRepositoryImpl implements AppBuilderConfigRepository { private final AppBuilderConfigMapper appBuilderConfigMapper; - private final AppBuilderConfigSerializer serializer; - private final AppBuilderConfigPropertyRepository appBuilderConfigPropertyRepository; public AppBuilderConfigRepositoryImpl(AppBuilderConfigMapper appBuilderConfigMapper, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFlowGraphRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFlowGraphRepositoryImpl.java index 59b194c332..fa610a1a0a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFlowGraphRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFlowGraphRepositoryImpl.java @@ -23,7 +23,6 @@ @Component public class AppBuilderFlowGraphRepositoryImpl implements AppBuilderFlowGraphRepository { private final AppBuilderFlowGraphMapper appBuilderFlowGraphMapper; - private final AppBuilderFlowGraphSerializer serializer; public AppBuilderFlowGraphRepositoryImpl(AppBuilderFlowGraphMapper appBuilderFlowGraphMapper) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormPropertyRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormPropertyRepositoryImpl.java index c1c5a3401b..ff8e2eb494 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormPropertyRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormPropertyRepositoryImpl.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.po.AppBuilderFormPropertyPo; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.serializer.impl.AppBuilderFormPropertySerializer; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.CollectionUtils; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormRepositoryImpl.java index 48f7f3e9c5..2d3d689c35 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderFormRepositoryImpl.java @@ -27,9 +27,7 @@ @Component public class AppBuilderFormRepositoryImpl implements AppBuilderFormRepository { private final AppBuilderFormMapper appBuilderFormMapper; - private final AppBuilderFormSerializer serializer; - private final AppBuilderFormPropertyRepository formPropertyRepository; public AppBuilderFormRepositoryImpl(AppBuilderFormMapper appBuilderFormMapper, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java index a7b02c19e4..e18b50e93d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImpl.java @@ -10,7 +10,9 @@ import modelengine.fit.jober.aipp.mapper.AppBuilderRuntimeInfoMapper; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; import modelengine.fit.jober.aipp.serializer.impl.AppBuilderRuntimeInfoSerializer; + import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.CollectionUtils; import java.util.List; import java.util.stream.Collectors; @@ -24,7 +26,6 @@ @Component public class AppBuilderRuntimeInfoRepositoryImpl implements AppBuilderRuntimeInfoRepository { private final AppBuilderRuntimeInfoMapper mapper; - private final AppBuilderRuntimeInfoSerializer serializer; public AppBuilderRuntimeInfoRepositoryImpl(AppBuilderRuntimeInfoMapper mapper) { @@ -44,4 +45,17 @@ public List selectByTraceId(String traceId) { public void insertOne(AppBuilderRuntimeInfo info) { this.mapper.insertOne(this.serializer.serialize(info)); } + + @Override + public List getExpiredRuntimeInfos(int expiredDays, int limit) { + return this.mapper.getExpiredRuntimeInfos(expiredDays, limit); + } + + @Override + public void deleteRuntimeInfos(List runtimeInfoIds) { + if (CollectionUtils.isEmpty(runtimeInfoIds)) { + return; + } + this.mapper.deleteRuntimeInfos(runtimeInfoIds); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppTemplateRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppTemplateRepositoryImpl.java index 41a228472b..69c356cb70 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppTemplateRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/AppTemplateRepositoryImpl.java @@ -25,7 +25,6 @@ @Component public class AppTemplateRepositoryImpl implements AppTemplateRepository { private final AppTemplateMapper appTemplateMapper; - private final AppTemplateSerializer serializer; public AppTemplateRepositoryImpl(AppTemplateMapper appTemplateMapper) { @@ -35,8 +34,7 @@ public AppTemplateRepositoryImpl(AppTemplateMapper appTemplateMapper) { @Override public List selectWithCondition(TemplateQueryCondition cond) { - return this.appTemplateMapper.selectWithCondition(cond) - .stream() + return this.appTemplateMapper.selectWithCondition(cond).stream() .map(this.serializer::deserialize) .collect(Collectors.toList()); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/I18nRepositoryImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/I18nRepositoryImpl.java index 5d06e63825..0aa91db150 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/I18nRepositoryImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/repository/impl/I18nRepositoryImpl.java @@ -9,6 +9,7 @@ import modelengine.fit.jober.aipp.mapper.I18nMapper; import modelengine.fit.jober.aipp.po.I18nPo; import modelengine.fit.jober.aipp.repository.I18nRepository; + import modelengine.fitframework.annotation.Component; import java.util.List; @@ -32,8 +33,13 @@ public I18nRepositoryImpl(I18nMapper i18nMapper) { @Override public Map> selectResource() { List i18nPoList = this.i18nMapper.selectResource(); - return i18nPoList.stream() - .collect(Collectors.groupingBy(I18nPo::getLanguage, - Collectors.toMap(I18nPo::getKey, I18nPo::getValue, (existing, replacement) -> existing))); + return i18nPoList.stream().collect(Collectors.groupingBy( + I18nPo::getLanguage, + Collectors.toMap( + I18nPo::getKey, + I18nPo::getValue, + (existing, replacement) -> existing) + ) + ); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/serializer/impl/AppBuilderAppSerializer.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/serializer/impl/AppBuilderAppSerializer.java index df73a1e9c9..565e9fffe7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/serializer/impl/AppBuilderAppSerializer.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/serializer/impl/AppBuilderAppSerializer.java @@ -28,6 +28,8 @@ public AppBuilderAppPo serialize(AppBuilderApp appBuilderApp) { return AppBuilderAppPo.builder() .id(appBuilderApp.getId()) .name(appBuilderApp.getName()) + .appId(appBuilderApp.getAppId()) + .appSuiteId(appBuilderApp.getAppSuiteId()) .tenantId(appBuilderApp.getTenantId()) .configId(appBuilderApp.getConfigId()) .flowGraphId(appBuilderApp.getFlowGraphId()) @@ -43,6 +45,10 @@ public AppBuilderAppPo serialize(AppBuilderApp appBuilderApp) { .updateAt(appBuilderApp.getUpdateAt()) .createBy(appBuilderApp.getCreateBy()) .updateBy(appBuilderApp.getUpdateBy()) + .isActive(appBuilderApp.getIsActive()) + .status(appBuilderApp.getStatus()) + .uniqueName(appBuilderApp.getUniqueName()) + .publishAt(appBuilderApp.getPublishAt()) .build(); } @@ -53,6 +59,8 @@ public AppBuilderApp deserialize(AppBuilderAppPo appBuilderAppPO) { : AppBuilderApp.builder() .id(appBuilderAppPO.getId()) .name(appBuilderAppPO.getName()) + .appId(appBuilderAppPO.getAppId()) + .appSuiteId(appBuilderAppPO.getAppSuiteId()) .tenantId(appBuilderAppPO.getTenantId()) .configId(appBuilderAppPO.getConfigId()) .flowGraphId(appBuilderAppPO.getFlowGraphId()) @@ -68,6 +76,10 @@ public AppBuilderApp deserialize(AppBuilderAppPo appBuilderAppPO) { .updateAt(appBuilderAppPO.getUpdateAt()) .createBy(appBuilderAppPO.getCreateBy()) .updateBy(appBuilderAppPO.getUpdateBy()) + .isActive(appBuilderAppPO.getIsActive()) + .status(appBuilderAppPO.getStatus()) + .uniqueName(appBuilderAppPO.getUniqueName()) + .publishAt(appBuilderAppPO.getPublishAt()) .build(); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippChatService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippChatService.java index 6b339e5a92..5e54d78140 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippChatService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippChatService.java @@ -8,6 +8,7 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; import modelengine.fit.jober.aipp.dto.chat.ChatInfoRspDto; import modelengine.fit.jober.aipp.dto.chat.CreateChatRequest; import modelengine.fit.jober.aipp.dto.chat.QueryChatInfoRequest; @@ -17,7 +18,6 @@ import modelengine.fit.jober.common.RangedResultSet; import java.util.List; -import java.util.Map; /** * 历史会话服务接口. @@ -78,16 +78,6 @@ QueryChatRsp queryChat(QueryChatRequest body, String chatId, OperationContext co QueryChatRsp updateChat(String originChatId, CreateChatRequest body, OperationContext context) throws AippTaskNotFoundException; - /** - * 重新发起会话。 - * - * @param currentInstanceId 需要重新发起会话的实例 ID。 - * @param additionalContext 重新会话需要的信息,如是否使用多轮对话等等。 - * @param context 上下文。 - * @return 表示会话相应体的 {@link QueryChatRsp}。 - */ - QueryChatRsp restartChat(String currentInstanceId, Map additionalContext, OperationContext context); - /** * 查询对话列表集合 * @@ -96,4 +86,12 @@ QueryChatRsp updateChat(String originChatId, CreateChatRequest body, OperationCo * @return 表示会话相应体列表 {@link ChatInfoRspDto}。List */ List queryChatInfo(QueryChatInfoRequest queryChatInfoRequest, OperationContext context); + + /** + * 保存会话数据. + * + * @param chatCreateEntity 待保存数据. + * @param context 操作人上下文信息. + */ + void saveChatInfo(ChatCreateEntity chatCreateEntity, OperationContext context); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippFlowService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippFlowService.java index 1b5d3fcc34..10aa91c5a3 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippFlowService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippFlowService.java @@ -6,6 +6,7 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.common.exception.AippException; @@ -13,13 +14,11 @@ import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.condition.AippQueryCondition; import modelengine.fit.jober.aipp.condition.PaginationCondition; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDetailDto; import modelengine.fit.jober.aipp.dto.AippDto; import modelengine.fit.jober.aipp.dto.AippOverviewRspDto; import modelengine.fit.jober.aipp.dto.AippVersionDto; -import modelengine.fit.jane.common.entity.OperationContext; import java.util.List; @@ -92,17 +91,6 @@ PageResponse listAipp(AippQueryCondition cond, PaginationCon */ AippCreateDto update(AippDto aippDto, OperationContext context) throws AippForbiddenException, AippParamException; - /** - * 预览aipp - * - * @param baselineVersion aipp 的基线版本 - * @param aippDto aipp定义 - * @param context 操作上下文 - * @return 创建预览aipp的id和version - * @throws AippException 预览aipp异常 - */ - AippCreateDto previewAipp(String baselineVersion, AippDto aippDto, OperationContext context); - /** * 退出预览aipp的清理 * @@ -121,17 +109,4 @@ PageResponse listAipp(AippQueryCondition cond, PaginationCon * @return aipp id信息 */ AippCreateDto upgrade(String baselineVersion, AippDto aippDto, OperationContext context); - - /** - * 发布aipp - * - * @param aippDto aipp定义 - * @param app app定义 - * @param context 操作上下文 - * @return 发布aipp概况 - * @throws AippForbiddenException 禁止更新aipp异常 - * @throws AippParamException 入参异常 - * @throws AippException 发布aipp异常 - */ - Rsp publish(AippDto aippDto, AppBuilderApp app, OperationContext context) throws AippException; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippLogService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippLogService.java index 0b5f707d87..555c367fa7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippLogService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AippLogService.java @@ -6,10 +6,10 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; import modelengine.fit.jober.aipp.entity.AippInstLog; import modelengine.fit.jober.aipp.entity.AippLogData; -import modelengine.fit.jane.common.entity.OperationContext; import java.util.List; import java.util.Map; @@ -31,31 +31,6 @@ public interface AippLogService { */ List queryAippRecentInstLog(String appId, String type, OperationContext context); - /** - * 查询指定aipp最近轮次的历史记录 - * - * @param aippId 指定aipp的id - * @param aippType 指定aipp的类型 - * @param count 轮次数目 - * @param context 登录信息 - * @return log数据 - */ - List queryAippRecentInstLog(String aippId, String aippType, Integer count, - OperationContext context); - - /** - * 查询指定chatId的历史记录, - * - * @param aippId 指定aipp的id - * @param aippType 指定aipp的类型 - * @param count 轮次数目 - * @param context 登录信息 - * @param chatId 会话ID - * @return log数据 - */ - List queryChatRecentInstLog(String aippId, String aippType, Integer count, - OperationContext context, String chatId); - /** * 查询指定appId的最近一次会话的历史记录 * @@ -120,14 +95,6 @@ List queryChatRecentInstLog(String aippId, String aippType, */ String insertLog(String logType, AippLogData logData, Map businessData); - /** - * 插入MSG类型的历史记录 - * - * @param msg MSG日志内容 - * @param flowData 流程执行上下文数据。 - */ - void insertMsgLog(String msg, List> flowData); - /** * 插入ERROR类型的历史记录 * @@ -191,13 +158,6 @@ List queryChatRecentInstLog(String aippId, String aippType, */ String buildPath(String instId, String parentInstId); - /** - * 删除指定实例的历史记录。 - * - * @param instanceId 指定实例的 id。 - */ - void deleteInstanceLog(String instanceId); - /** * 查询提示词拼接后的历史记录 * @@ -219,24 +179,6 @@ List queryAippRecentInstLogAfterSplice(String aippId, String */ List queryBatchAndFilterFullLogsByLogType(List instanceIds, List filterLogTypes); - /** - * 查询指定实例的日志,并过滤掉指定类型的日志。 - * - * @param instanceId 表示指定实例 id 的 {@link String}。 - * @param filterLogTypes 表示指定日志类型列表的 {@link List}{@code <}{@link String}{@code >}。 - * @return 表示查询到的日志列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。 - */ - List queryAndFilterLogsByLogType(String instanceId, List filterLogTypes); - - /** - * 查询指定实例且指定类型的的日志。 - * - * @param instanceId 表示指定实例 id 的 {@link String}。 - * @param logTypes 表示指定日志类型列表的 {@link List}{@code <}{@link String}{@code >}。 - * @return 表示查询到的日志列表的 {@link List}{@code <}{@link AippInstLog}{@code >}。 - */ - List queryLogsByInstanceIdAndLogTypes(String instanceId, List logTypes); - /** * 删除指定的对话历史记录。 * diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderAppService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderAppService.java index f04d65eabb..0bc560e289 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderAppService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderAppService.java @@ -6,6 +6,7 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jober.aipp.condition.AppQueryCondition; import modelengine.fit.jober.aipp.dto.AippCreateDto; @@ -19,12 +20,9 @@ import modelengine.fit.jober.aipp.dto.PublishedAppResDto; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; -import modelengine.fit.jober.aipp.dto.export.AppExportDto; import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; import modelengine.fit.jober.common.RangedResultSet; -import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fitframework.annotation.Genericable; import java.util.List; @@ -157,10 +155,10 @@ Rsp> list(AppQueryCondition cond, Oper * @param limit 表示获取数据的最大个数的 {@code int}。 * @param appId 表示应用唯一标识的 {@link String}。 * @param context 表示操作上下文的 {@link OperationContext}。 - * @return 获取到的历史版本信息集合的 {@link List}{@code <}{@link PublishedAppResDto}{@code >}。 + * @return 获取到的历史版本信息集合的 {@link RangedResultSet}{@code <}{@link PublishedAppResDto}{@code >}。 */ @Genericable(id = "modelengine.fit.jober.aipp.service.app.recent.published") - List recentPublished(AppQueryCondition cond, long offset, int limit, String appId, + RangedResultSet recentPublished(AppQueryCondition cond, long offset, int limit, String appId, OperationContext context); /** @@ -173,16 +171,6 @@ List recentPublished(AppQueryCondition cond, long offset, in @Genericable(id = "modelengine.fit.jober.aipp.service.app.published") PublishedAppResDto published(String uniqueName, OperationContext context); - /** - * 导出应用配置。 - * - * @param appId 表示应用的唯一表示的 {@link String}。 - * @param context 表示操作上下文的 {@link OperationContext}。 - * @return 导出的应用配置信息的 {@link AppExportDto}。 - */ - @Genericable(id = "modelengine.fit.jober.aipp.service.app.export") - AppExportDto export(String appId, OperationContext context); - /** * 根据应用配置进行可用性校验。 * @@ -193,16 +181,6 @@ List recentPublished(AppQueryCondition cond, long offset, in @Genericable(id = "modelengine.fit.jober.aipp.service.app.check") List checkAvailable(List appCheckDtos, OperationContext context); - /** - * 导入应用。 - * - * @param appConfig 表示上传的应用配置文件的 {@link String}。 - * @param context 表示操作上下文的 {@link OperationContext}。 - * @return 表示创建的应用的 {@link AppBuilderAppDto}。 - */ - @Genericable(id = "modelengine.fit.jober.aipp.service.app.import") - AppBuilderAppDto importApp(String appConfig, OperationContext context); - /** * 将应用发布为应用模板。 * @@ -231,4 +209,15 @@ List recentPublished(AppQueryCondition cond, long offset, in */ @Genericable(id = "modelengine.fit.jober.aipp.service.app.deleteTemplate") void deleteTemplate(String templateId, OperationContext context); + + /** + * 恢复应用到指定历史版本。 + * + * @param appId 表示应用唯一标识的 {@link String}。 + * @param resetId 表示指定历史版本唯一标识的 {@link String}。 + * @param context 表示接口操作上下文的 {@link OperationContext}。 + * @return 表示恢复应用完成后应用详情的 {@link AppBuilderAppDto}。 + */ + @Genericable(id = "modelengine.fit.jober.aipp.service.app.recover") + AppBuilderAppDto recoverApp(String appId, String resetId, OperationContext context); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderFormService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderFormService.java index 512f43d86d..c6c7b72f05 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderFormService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderFormService.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.dto.AppBuilderFormDto; import modelengine.fit.jober.common.RangedResultSet; + import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.jane.common.entity.OperationContext; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderPromptService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderPromptService.java index 046a5ceb33..bcb79f5283 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderPromptService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppBuilderPromptService.java @@ -6,10 +6,10 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jober.aipp.dto.AppBuilderPromptCategoryDto; import modelengine.fit.jober.aipp.dto.AppBuilderPromptDto; -import modelengine.fit.jane.common.entity.OperationContext; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppInspirationService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppInspirationService.java index 9d353f1f53..c7710b44c5 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppInspirationService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppInspirationService.java @@ -7,6 +7,7 @@ package modelengine.fit.jober.aipp.service; import modelengine.fit.jane.common.entity.OperationContext; + import modelengine.fitframework.annotation.Genericable; import java.util.List; @@ -31,6 +32,6 @@ public interface AppInspirationService { * @since 2024-04-25 */ @Genericable(id = "d01041a73e00ac46bedde08d02c6818e") - List> getCustomizedLogs(Map params, String aippId, String appType, - OperationContext context); + List> getCustomizedLogs(Map params, + String aippId, String appType, OperationContext context); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppLogService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppLogService.java index 17a261d8c3..302644c015 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppLogService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppLogService.java @@ -7,6 +7,7 @@ package modelengine.fit.jober.aipp.service; import modelengine.fit.jane.common.entity.OperationContext; + import modelengine.fitframework.annotation.Genericable; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppTemplateService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppTemplateService.java index 75da2ac338..0b8eec7066 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppTemplateService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/AppTemplateService.java @@ -6,12 +6,12 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.condition.TemplateQueryCondition; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; import modelengine.fit.jober.common.RangedResultSet; -import modelengine.fit.jane.common.entity.OperationContext; /** * 应用模板 Service 接口定义。 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/FileService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/FileService.java index 7a3b699b3a..be630dbabb 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/FileService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/FileService.java @@ -6,15 +6,16 @@ package modelengine.fit.jober.aipp.service; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jober.aipp.dto.FileRspDto; import modelengine.fit.jober.aipp.dto.FormFileDto; import modelengine.fit.jober.aipp.dto.GenerateImageDto; + import modelengine.fit.http.entity.FileEntity; import modelengine.fit.http.entity.PartitionedEntity; import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fit.http.server.HttpClassicServerResponse; -import modelengine.fit.jane.common.entity.OperationContext; import java.io.IOException; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/GenericableManageService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/GenericableManageService.java index 5185641e8e..773a94fa6e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/GenericableManageService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/GenericableManageService.java @@ -6,8 +6,8 @@ package modelengine.fit.jober.aipp.service; -import modelengine.fit.jober.aipp.dto.FitableInfoDto; import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.dto.FitableInfoDto; import java.util.List; import java.util.Map; @@ -38,6 +38,6 @@ public interface GenericableManageService { * @param operationContext 操作上下文 * @return 执行结果 */ - List> executeInspirationFitable(String fitableId, String appId, String appType, - OperationContext operationContext); + List> executeInspirationFitable(String fitableId, + String appId, String appType, OperationContext operationContext); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/KnowledgeService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/KnowledgeService.java index 7aac7972b8..4c1615b462 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/KnowledgeService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/KnowledgeService.java @@ -8,7 +8,6 @@ import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.condition.KnowledgeQueryCondition; - import modelengine.jade.app.engine.knowledge.dto.KRepoDto; import modelengine.jade.app.engine.knowledge.dto.KTableDto; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/StatisticsService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/StatisticsService.java index d547f3d0a7..5379333bf5 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/StatisticsService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/StatisticsService.java @@ -6,8 +6,8 @@ package modelengine.fit.jober.aipp.service; -import modelengine.fit.jober.aipp.dto.StatisticsDTO; import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.dto.StatisticsDTO; /** * Statistics相关服务 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/UploadedFileManageService.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/UploadedFileManageService.java index a729ece15c..0f112e73c1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/UploadedFileManageService.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/UploadedFileManageService.java @@ -6,6 +6,7 @@ package modelengine.fit.jober.aipp.service; +import java.io.IOException; import java.util.List; /** @@ -56,4 +57,15 @@ public interface UploadedFileManageService { * @param status 文件是否可以清理的标识 */ void updateRecord(String appId, String fileName, Integer status); + + /** + * 拷贝图标文件. + * + * @param icon 图标. + * @param aippId 应用id. + * @param operator 操作人. + * @return {@link String} 拷贝后的图标. + * @throws IOException io异常. + */ + String copyIconFiles(String icon, String aippId, String operator) throws IOException; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AgentInfoGenerateServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AgentInfoGenerateServiceImpl.java index 2d9e4943cc..445372e11e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AgentInfoGenerateServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AgentInfoGenerateServiceImpl.java @@ -8,33 +8,32 @@ import static modelengine.jade.carver.validation.ValidateTagMode.validateTagMode; -import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jober.aipp.common.utils.ContentProcessUtils; -import modelengine.fit.jober.aipp.condition.AppQueryCondition; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; -import modelengine.fit.jober.aipp.util.UUIDUtil; -import modelengine.jade.carver.ListResult; -import modelengine.jade.common.globalization.LocaleService; -import modelengine.jade.store.entity.query.PluginToolQuery; -import modelengine.jade.store.entity.transfer.PluginToolData; -import modelengine.jade.store.service.PluginToolService; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import modelengine.fel.core.template.support.DefaultStringTemplate; import modelengine.fit.jade.aipp.model.dto.ModelAccessInfo; import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.utils.ContentProcessUtils; +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; import modelengine.fit.jober.aipp.service.AgentInfoGenerateService; import modelengine.fit.jober.aipp.service.AippModelService; +import modelengine.fit.jober.aipp.util.UUIDUtil; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.IoUtils; import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.StringUtils; +import modelengine.jade.carver.ListResult; +import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.store.entity.query.PluginToolQuery; +import modelengine.jade.store.entity.transfer.PluginToolData; +import modelengine.jade.store.service.PluginToolService; import java.io.IOException; import java.util.ArrayList; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippChatServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippChatServiceImpl.java index 9ace97bd4c..ad872a268a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippChatServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippChatServiceImpl.java @@ -18,6 +18,11 @@ import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.domain.AppBuilderApp; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; import modelengine.fit.jober.aipp.dto.chat.ChatDto; import modelengine.fit.jober.aipp.dto.chat.ChatInfoRspDto; import modelengine.fit.jober.aipp.dto.chat.CreateChatRequest; @@ -44,6 +49,7 @@ import modelengine.fit.jober.aipp.util.UUIDUtil; import modelengine.fit.jober.aipp.vo.AippLogVO; import modelengine.fit.jober.common.RangedResultSet; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.util.CollectionUtils; @@ -70,28 +76,30 @@ @Component public class AippChatServiceImpl implements AippChatService { private static final String NORMAL_CHAT = "normal"; - private static final String FROM_OTHER_CHAT = "fromOtherApp"; private final AippChatMapper aippChatMapper; - private final MetaService metaService; - private final AppBuilderAppMapper appBuilderAppMapper; - + private final AppTaskService appTaskService; + private final AppVersionRepository appVersionRepository; + private final AppChatRepository appChatRepository; private final AippLogService aippLogService; - private final AppBuilderAppRepository appRepository; @Fit private modelengine.fit.jober.aipp.genericable.AippRunTimeService aippRunTimeService; - public AippChatServiceImpl(AippChatMapper aippChatMapper, MetaService metaService, - AppBuilderAppMapper appBuilderAppMapper, AippLogService aippLogService, + public AippChatServiceImpl(AippChatMapper aippChatMapper, AppBuilderAppMapper appBuilderAppMapper, + AppTaskService appTaskService, AppVersionRepository appVersionRepository, + AppChatRepository appChatRepository, MetaService metaService, AippLogService aippLogService, AppBuilderAppRepository appRepository) { this.aippChatMapper = aippChatMapper; this.metaService = metaService; this.appBuilderAppMapper = appBuilderAppMapper; + this.appTaskService = appTaskService; + this.appVersionRepository = appVersionRepository; + this.appChatRepository = appChatRepository; this.aippLogService = aippLogService; this.appRepository = appRepository; } @@ -123,8 +131,10 @@ public QueryChatRsp createChat(CreateChatRequest body, OperationContext context) private String persistChat(CreateChatRequest body, OperationContext context, String chatId, String unCutChatName) { String chatName = (unCutChatName.length() > 64) ? unCutChatName.substring(0, 32) : unCutChatName; - String instId = this.aippRunTimeService.createAippInstance(body.getAippId(), body.getAippVersion(), - body.getInitContext(), context); + String instId = this.aippRunTimeService.createAippInstance(body.getAippId(), + body.getAippVersion(), + body.getInitContext(), + context); Map attributesMap = new HashMap<>(); attributesMap.put("instId", instId); if (body.getOriginApp() != null) { @@ -189,16 +199,14 @@ private void persistOriginAppChat(CreateChatRequest body, OperationContext conte } private AppBuilderAppPo convertAippToApp(String aippId, String appVersion, OperationContext context) { - Meta meta = MetaUtils.getAnyMeta(this.metaService, aippId, appVersion, context); - if (meta == null) { - throw new AippException(AippErrCode.APP_NOT_FOUND); - } - String appId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - return this.appBuilderAppMapper.selectWithId(appId); + AppTask task = this.appTaskService.getLatest(aippId, appVersion, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", aippId, appVersion))); + return this.appBuilderAppMapper.selectWithId(task.getEntity().getAppId()); } - private QueryChatRequest buildQueryHistoryChatRequest(QueryChatRequest body, OperationContext context) - throws AippTaskNotFoundException { + private QueryChatRequest buildQueryHistoryChatRequest(QueryChatRequest body) + throws AippTaskNotFoundException { QueryChatRequest request = QueryChatRequest.builder().build(); request.setAppState(body.getAppState()); request.setLimit(body.getLimit()); @@ -209,12 +217,8 @@ private QueryChatRequest buildQueryHistoryChatRequest(QueryChatRequest body, Ope return request; } if (body.getAppId() != null) { - List metas = MetaUtils.getAllMetasByAppId(this.metaService, body.getAppId(), context); - if (CollectionUtils.isEmpty(metas)) { - throw new AippTaskNotFoundException(AippErrCode.TASK_NOT_FOUND); - } - String aippId = metas.get(0).getId(); - request.setAippId(aippId); + String appSuiteId = this.appVersionRepository.getAppSuiteIdByAppId(body.getAppId()); + request.setAippId(appSuiteId); return request; } return request; @@ -227,7 +231,6 @@ private QueryChatRequest buildQueryHistoryChatRequest(QueryChatRequest body, Ope */ private void validate(QueryChatRequest body) { String aippId = body.getAippId(); - String aippVersion = body.getAippVersion(); String appId = body.getAppId(); if (StringUtils.isEmpty(aippId) && StringUtils.isEmpty(appId)) { throw new AippException(AippErrCode.APP_NOT_FOUND); @@ -238,22 +241,22 @@ private void validate(QueryChatRequest body) { public QueryChatRsp queryChat(QueryChatRequest body, String chatId, OperationContext context) throws AippTaskNotFoundException { QueryChatRsp rsp = new QueryChatRsp(); - QueryChatRequest request = this.buildQueryHistoryChatRequest(body, context); + QueryChatRequest request = this.buildQueryHistoryChatRequest(body); List chatResult = this.aippChatMapper.selectChatList(request, chatId, context.getAccount()); - if (chatResult != null && chatResult.size() > 0 && chatResult.get(0) != null) { + if (chatResult != null && !chatResult.isEmpty() && chatResult.get(0) != null) { rsp = chatResult.get(0); } else { return rsp; } List result = this.aippChatMapper.selectChat(chatId, body.getOffset(), body.getLimit()); getChatAppInfo(result, body.getAippId(), context); - ArrayList msgList = new ArrayList<>(); + List msgList = new ArrayList<>(); result.forEach((chat) -> { AippLogData data = JsonUtils.parseObject(chat.getLogData(), AippLogData.class); String content = data.getMsg(); MessageInfo messageInfo = MessageInfo.builder() .contentType(0) - .content(Arrays.asList(new String[] {content})) + .content(Collections.singletonList(content)) .role((AippInstLogType.QUESTION.name().equals(chat.getLogType())) ? "USER" : "SYSTEM") .createTime(chat.getCreateTime()) .msgId(chat.getMsgId()) @@ -275,41 +278,30 @@ public QueryChatRsp queryChat(QueryChatRequest body, String chatId, OperationCon private void getChatAppInfo(List chatList, String originAippId, OperationContext context) { // 620出包需要 与logService的getAippLogWithAppInfo逻辑雷同 后续要整改 List atAippIds = chatList.stream() - .filter(data -> !Objects.equals(data.getAippId(), originAippId)) .map(ChatDto::getAippId) + .filter(aippId -> !Objects.equals(aippId, originAippId)) .collect(Collectors.toList()); - RangedResultSet metas = metaService.list(this.buildAippIdFilter(atAippIds), true, 0, atAippIds.size(), - context); - if (!metas.getResults().isEmpty()) { - List meta = metas.getResults(); - Map metaMap = meta.stream().collect(Collectors.toMap(Meta::getId, Function.identity())); - chatList.stream().forEach(data -> setChatAppInfoWithMetaMap(metaMap, data)); - } - } - private void setChatAppInfoWithMetaMap(Map metaMap, ChatDto chat) { - if (!metaMap.containsKey(chat.getAippId())) { - return; - } - Meta meta = metaMap.get(chat.getAippId()); - chat.setAppName(meta.getName()); - Object icon = meta.getAttributes().get("meta_icon"); - if (icon instanceof String) { - chat.setAppIcon((String) icon); + RangedResultSet resultSet = this.appTaskService.getTasks(AppTask.asQueryEntity(0, atAippIds.size()) + .latest() + .addAppSuiteIds(atAippIds) + .build(), context); + if (!resultSet.isEmpty()) { + Map taskMap = resultSet.getResults().stream() + .collect(Collectors.toMap(t -> t.getEntity().getAppSuiteId(), Function.identity())); + chatList.stream().filter(c -> taskMap.containsKey(c.getAippId())).forEach(c -> { + AppTask task = taskMap.get(c.getAippId()); + c.setAppName(task.getEntity().getName()); + c.setAppIcon(task.getEntity().getIcon()); + }); } } - private MetaFilter buildAippIdFilter(List aippIds) { - MetaFilter filter = new MetaFilter(); - filter.setMetaIds(aippIds); - return filter; - } - @Override public RangedResultSet queryChatList(QueryChatRequest body, OperationContext context) { - QueryChatRequest request = null; + QueryChatRequest request; try { - request = this.buildQueryHistoryChatRequest(body, context); + request = this.buildQueryHistoryChatRequest(body); } catch (AippTaskNotFoundException e) { throw new AippException(OBTAIN_HISTORY_CONVERSATION_FAILED); } @@ -377,13 +369,8 @@ public Void deleteChat(String chatId, String appId, OperationContext context) { this.aippChatMapper.deleteChat(chatId); return null; } - String metaId; - try { - metaId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - } catch (AippTaskNotFoundException e) { - throw new AippException(AippErrCode.APP_NOT_FOUND); - } - this.aippChatMapper.deleteAppByAippId(metaId); + String appSuiteId = this.appVersionRepository.getAppSuiteIdByAppId(appId); + this.aippChatMapper.deleteAppByAippId(appSuiteId); return null; } @@ -436,115 +423,13 @@ public List queryChatInfo(QueryChatInfoRequest queryChatInfoRequ return queryChatRsps.stream().map(this::buildChatInfoRspDto).collect(Collectors.toList()); } - private ChatInfoRspDto buildChatInfoRspDto(QueryChatRsp queryChatRsp) { - return ChatInfoRspDto.builder().chatId(queryChatRsp.getChatId()).build(); - } - @Override - public QueryChatRsp restartChat(String currentInstanceId, Map additionalContext, - OperationContext context) { - String path = this.aippLogService.getParentPath(currentInstanceId); - AippLogVO aippLogVO = AippLogVO.builder().path(path).build(); - String parentInstanceId = aippLogVO.getAncestors().get(0); - if (StringUtils.isEmpty(parentInstanceId)) { - throw new IllegalArgumentException( - StringUtils.format("The instance id {0} does not match ant parentInstanceId.", currentInstanceId)); - } - List chatIds = this.aippChatMapper.selectChatIdByInstanceId(parentInstanceId); - if (chatIds.isEmpty()) { - throw new IllegalArgumentException( - StringUtils.format("The instance id {0} does not match any chat id.", parentInstanceId)); - } - List chatList = this.aippChatMapper.selectChatListByChatIds(chatIds); - String chatId; - String chatType = this.getChatType(chatList.size()); - String restartMode = ObjectUtils.cast( - additionalContext.getOrDefault(AippConst.RESTART_MODE, RestartModeEnum.OVERWRITE.getMode())); - additionalContext.put(AippConst.RESTART_MODE, restartMode); - CreateChatRequest body = this.buildChatBody(parentInstanceId, additionalContext); - if (chatType == NORMAL_CHAT) { - chatId = chatList.get(0).getChatId(); - } else if (chatType == FROM_OTHER_CHAT) { - chatId = this.buildChatBodyWhenAtApp(chatList, body, chatIds); - } else { - throw new IllegalArgumentException( - StringUtils.format("The chat ids {0} match illegal num of chat sessions.", chatIds)); - } - if (StringUtils.equals(RestartModeEnum.OVERWRITE.getMode(), restartMode)) { - this.aippChatMapper.deleteWideRelationshipByInstanceId(parentInstanceId); - this.aippLogService.deleteInstanceLog(parentInstanceId); - } - try { - return this.updateChat(chatId, body, context); - } catch (AippTaskNotFoundException e) { - throw new AippException(RE_CHAT_FAILED); - } - } - - private String getChatType(int chatNum) { - if (chatNum == 1) { - return NORMAL_CHAT; - } else if (chatNum == 2) { - return FROM_OTHER_CHAT; - } else { - return StringUtils.EMPTY; - } + public void saveChatInfo(ChatCreateEntity chatCreateEntity, OperationContext context) { + this.appChatRepository.saveChat(chatCreateEntity, context); } - private CreateChatRequest buildChatBody(String instanceId, Map additionalContext) { - // 构造updateChat需要的body - List filterLogTypes = new ArrayList<>( - Arrays.asList(AippInstLogType.HIDDEN_MSG.name(), AippInstLogType.HIDDEN_FORM.name())); - List aippInstLogs = this.aippLogService.queryAndFilterLogsByLogType(instanceId, filterLogTypes); - List questionAippInstLogs = aippInstLogs.stream() - .filter(item -> StringUtils.equals(item.getLogType(), AippInstLogType.QUESTION.name()) - || StringUtils.equals(item.getLogType(), AippInstLogType.HIDDEN_QUESTION.name())) - .collect(Collectors.toList()); - AippInstLog questionAippInstLog = questionAippInstLogs.get(0); - Map initContext = new HashMap<>(); - String question = ObjectUtils.cast(JsonUtils.parseObject(questionAippInstLog.getLogData()).get("msg")); - additionalContext.put(AippConst.BS_AIPP_QUESTION_KEY, question); - initContext.put(AippConst.BS_INIT_CONTEXT_KEY, additionalContext); - return CreateChatRequest.builder() - .aippId(questionAippInstLog.getAippId()) - .aippVersion(questionAippInstLog.getVersion()) - .initContext(initContext) - .build(); - } - - private String buildChatBodyWhenAtApp(List chatList, CreateChatRequest body, List chatIds) { - String chatId; - QueryChatRsp firstChat = chatList.get(0); - QueryChatRsp secondChat = chatList.get(1); - Map firstChatMap = JsonUtils.parseObject(firstChat.getAttributes()); - Map secondChatMap = JsonUtils.parseObject(secondChat.getAttributes()); - String firstOriginApp = ObjectUtils.cast(firstChatMap.get("originApp")); - String secondOriginApp = ObjectUtils.cast(secondChatMap.get("originApp")); - if (!this.validateChatSessionWhenAtApp(firstOriginApp, secondOriginApp)) { - throw new IllegalArgumentException( - StringUtils.format("The chat ids {0} chat sessions are illegal.", chatIds)); - } else if (firstOriginApp != null) { - chatId = this.buildChatBodyWithOriginApp(firstOriginApp, firstChatMap, firstChat, secondChat, body); - } else { - chatId = this.buildChatBodyWithOriginApp(secondOriginApp, secondChatMap, secondChat, firstChat, body); - } - return chatId; - } - - private boolean validateChatSessionWhenAtApp(String firstOriginApp, String secondOriginApp) { - if ((firstOriginApp != null && secondOriginApp != null) || (firstOriginApp == null - && secondOriginApp == null)) { - return false; - } - return true; - } - - private String buildChatBodyWithOriginApp(String originApp, Map chatMap, QueryChatRsp chat, - QueryChatRsp originChat, CreateChatRequest body) { - body.setOriginApp(originApp); - body.setOriginAppVersion(ObjectUtils.cast(chatMap.get("originAppVersion"))); - body.setChatId(chat.getChatId()); - return originChat.getChatId(); + private ChatInfoRspDto buildChatInfoRspDto(QueryChatRsp queryChatRsp) { + return ChatInfoRspDto.builder().chatId(queryChatRsp.getChatId()).build(); } private String getChatName(Map initContext) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowRuntimeInfoServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowRuntimeInfoServiceImpl.java index 85dd51e74c..eccb3e0112 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowRuntimeInfoServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowRuntimeInfoServiceImpl.java @@ -7,18 +7,17 @@ package modelengine.fit.jober.aipp.service.impl; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.Instance; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; -import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; import modelengine.fit.jober.aipp.service.AippFlowRuntimeInfoService; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; import modelengine.fit.jober.entity.consts.NodeTypes; import modelengine.fit.runtime.entity.NodeInfo; import modelengine.fit.runtime.entity.RuntimeData; @@ -39,31 +38,28 @@ */ @Component public class AippFlowRuntimeInfoServiceImpl implements AippFlowRuntimeInfoService { - private final MetaService metaService; - - private final MetaInstanceService metaInstanceService; - private final AppBuilderRuntimeInfoRepository runtimeInfoRepository; + private final AppTaskInstanceService appTaskInstanceService; + private final AppTaskService appTaskService; - public AippFlowRuntimeInfoServiceImpl(MetaService metaService, MetaInstanceService metaInstanceService, - AppBuilderRuntimeInfoRepository runtimeInfoRepository) { - this.metaService = metaService; - this.metaInstanceService = metaInstanceService; + public AippFlowRuntimeInfoServiceImpl(AppBuilderRuntimeInfoRepository runtimeInfoRepository, + AppTaskInstanceService appTaskInstanceService, AppTaskService appTaskService) { this.runtimeInfoRepository = runtimeInfoRepository; + this.appTaskInstanceService = appTaskInstanceService; + this.appTaskService = appTaskService; } @Override public Optional getRuntimeData(String aippId, String version, String instanceId, OperationContext context) { - Meta meta = MetaUtils.getAnyMeta(this.metaService, aippId, version, context); - if (meta == null) { - throw new AippException(AippErrCode.APP_NOT_FOUND_WHEN_DEBUG); - } - String versionId = meta.getVersionId(); - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, - this.metaInstanceService); - String traceId = instDetail.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); - + AppTask task = this.appTaskService.getLatest(aippId, version, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND_WHEN_DEBUG, + StringUtils.format("App task not found, appSuiteId:{0}, version: {1}.", aippId, version))); + String versionId = task.getEntity().getTaskId(); + AppTaskInstance instance = this.appTaskInstanceService.getInstance(versionId, instanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + String traceId = instance.getEntity().getFlowTranceId(); List runtimeInfoList = this.runtimeInfoRepository.selectByTraceId(traceId); if (CollectionUtils.isEmpty(runtimeInfoList)) { return Optional.empty(); @@ -72,8 +68,8 @@ public Optional getRuntimeData(String aippId, String version, Strin AppBuilderRuntimeInfo start = runtimeInfoList.stream() .filter(r -> r.getNodeType().equals(NodeTypes.START.getType())) .findFirst() - .orElseThrow( - () -> new IllegalStateException(StringUtils.format("No START node info in runtime info."))); + .orElseThrow(() -> + new IllegalStateException(StringUtils.format("No START node info in runtime info."))); AppBuilderRuntimeInfo end = runtimeInfoList.get(runtimeInfoList.size() - 1); RuntimeData runtimeData = new RuntimeData(); runtimeData.setStartTime(start.getStartTime()); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowServiceImpl.java index ec71717c56..c4837f74a4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippFlowServiceImpl.java @@ -6,23 +6,15 @@ package modelengine.fit.jober.aipp.service.impl; -import modelengine.fit.dynamicform.entity.FormMetaItem; -import modelengine.fit.dynamicform.entity.FormMetaQueryParameter; -import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.ACTIVE; +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.INACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; + +import lombok.AllArgsConstructor; import modelengine.fit.jade.waterflow.FlowsService; import modelengine.fit.jade.waterflow.dto.FlowInfo; -import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; -import modelengine.fit.jade.waterflow.entity.FlowNodeFormInfo; -import modelengine.fit.jane.Undefinable; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaDeclarationInfo; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.property.MetaPropertyDeclarationInfo; -import modelengine.fit.jane.task.util.Entities; -import modelengine.fit.jober.WaterFlowService; import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; @@ -31,66 +23,34 @@ import modelengine.fit.jober.aipp.condition.AippQueryCondition; import modelengine.fit.jober.aipp.condition.PaginationCondition; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.convertor.FormMetaConvertor; -import modelengine.fit.jober.aipp.convertor.MetaConvertor; -import modelengine.fit.jober.aipp.convertor.TaskPropertyConvertor; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; -import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.TaskDomainEntity; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDetailDto; import modelengine.fit.jober.aipp.dto.AippDto; -import modelengine.fit.jober.aipp.dto.AippNodeForms; import modelengine.fit.jober.aipp.dto.AippOverviewDto; import modelengine.fit.jober.aipp.dto.AippOverviewRspDto; import modelengine.fit.jober.aipp.dto.AippVersionDto; -import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; -import modelengine.fit.jober.aipp.enums.AippTypeEnum; -import modelengine.fit.jober.aipp.enums.AppCategory; import modelengine.fit.jober.aipp.enums.JaneCategory; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; -import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fit.jober.aipp.service.AippFlowService; -import modelengine.fit.jober.aipp.service.AippRunTimeService; import modelengine.fit.jober.aipp.util.AippStringUtils; -import modelengine.fit.jober.aipp.util.FormUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; -import modelengine.fit.jober.aipp.util.VersionUtils; -import modelengine.fit.jober.common.ErrorCodes; import modelengine.fit.jober.common.RangedResultSet; import modelengine.fit.jober.common.exceptions.ConflictException; import modelengine.fit.jober.common.exceptions.JobberException; -import modelengine.fit.jober.entity.consts.NodeTypes; import modelengine.fitframework.annotation.Component; -import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; import modelengine.fitframework.model.Tuple; -import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; -import modelengine.fel.tool.service.ToolService; -import modelengine.jade.store.entity.transfer.AppData; -import modelengine.jade.store.entity.transfer.AppPublishData; -import modelengine.jade.store.entity.transfer.PluginData; -import modelengine.jade.store.entity.transfer.PluginToolData; -import modelengine.jade.store.service.AppService; -import modelengine.jade.store.service.PluginService; -import modelengine.jade.store.service.support.DeployStatus; +import modelengine.jade.store.service.ToolService; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; @@ -102,64 +62,13 @@ * @since 2023-12-12 */ @Component +@AllArgsConstructor public class AippFlowServiceImpl implements AippFlowService { - /** - * 预览aipp version uuid后缀长度 - */ - public static final int PREVIEW_UUID_LEN = 6; - private static final Logger log = Logger.get(AippFlowServiceImpl.class); - - private static final int RETRY_PREVIEW_TIMES = 5; - private static final String DEFAULT_VERSION = "1.0.0"; - private static final String OLD_VERSION_ID = "00000000000000000000000000000000"; - - private static final String APP_TYPE = "app"; - - private static final String WATERFLOW_TYPE = "waterflow"; - - private static final String DEPLOYED = "DEPLOYED"; - - private static final String APP_TYPE_TAG_PREFIX = "APP_TYPE_"; - - private final AppBuilderFormRepository formRepository; - private final FlowsService flowsService; - - private final MetaService metaService; - - private final AippFlowDefinitionService flowDefinitionService; - - private final AippRunTimeService aippRunTimeService; - - private final AppBuilderAppFactory factory; - - private final AppBuilderAppMapper appBuilderAppMapper; - - private final AppService appService; - - private final PluginService pluginService; - - private final ToolService toolService; - - public AippFlowServiceImpl(@Fit FlowsService flowsService, @Fit MetaService metaService, - @Fit AippFlowDefinitionService flowDefinitionService, @Fit AippRunTimeService aippRunTimeService, - @Fit AppBuilderFormRepository formRepository, AppBuilderAppMapper appBuilderAppMapper, - AppBuilderAppFactory factory, @Fit AppService appService, @Fit PluginService pluginService, - @Fit ToolService toolService) { - this.flowsService = flowsService; - this.metaService = metaService; - this.flowDefinitionService = flowDefinitionService; - this.aippRunTimeService = aippRunTimeService; - this.formRepository = formRepository; - this.appBuilderAppMapper = appBuilderAppMapper; - this.factory = factory; - this.appService = appService; - this.pluginService = pluginService; - this.toolService = toolService; - } + private final AppTaskService appTaskService; /** * 查询aipp详情 @@ -171,34 +80,30 @@ public AippFlowServiceImpl(@Fit FlowsService flowsService, @Fit MetaService meta */ @Override public Rsp queryAippDetail(String aippId, String version, OperationContext context) { - Meta meta = MetaUtils.getAnyMeta(metaService, aippId, version, context); - log.info("queryAippDetail aipp {} version {}, meta attr {}", aippId, version, meta.getAttributes()); - String flowConfigId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)); - - FlowInfo rsp; + AppTask task = this.appTaskService.getLatest(aippId, version, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", aippId, version))); + String flowConfigId = task.getEntity().getFlowConfigId(); try { - rsp = this.flowsService.getFlows(flowConfigId, version, context); // 是否要改? + FlowInfo rsp = this.flowsService.getFlows(flowConfigId, version, context); // 是否要改? + AippDetailDto detail = new AippDetailDto(); + detail.setCreatedAt(task.getEntity().getCreationTime()); + detail.setUpdatedAt(task.getEntity().getLastModificationTime()); + detail.setUpdater(task.getEntity().getLastModifier()); + detail.setAippId(task.getEntity().getAppSuiteId()); + detail.setFlowViewData(JsonUtils.parseObject(rsp.getConfigData())); + detail.setVersion(version); + detail.setStatus(task.getEntity().getStatus()); + detail.setIcon(task.getEntity().getIcon()); + Optional.ofNullable(task.getEntity().getDescription()).ifPresent(detail::setDescription); + Optional.ofNullable(task.getEntity().getPublishTime()) + .map(LocalDateTime::parse) + .ifPresent(detail::setPublishAt); + return Rsp.ok(detail); } catch (JobberException e) { - log.error("queryAippDetail failed, aipp {} version {}, meta attr {}", - aippId, - version, - meta.getAttributes()); + log.error("queryAippDetail failed, task {}", task.getEntity().toString()); throw new AippException(context, AippErrCode.OBTAIN_APP_ORCHESTRATION_INFO_FAILED); } - AippDetailDto detail = MetaConvertor.INSTANCE.toAippDetailDto(meta); - detail.setFlowViewData(JsonUtils.parseObject(rsp.getConfigData())); - detail.setVersion(version); - detail.setStatus(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_META_STATUS_KEY))); - detail.setIcon(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_META_ICON_KEY))); - if (meta.getAttributes().containsKey(AippConst.ATTR_DESCRIPTION_KEY)) { - detail.setDescription(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_DESCRIPTION_KEY))); - } - if (meta.getAttributes().containsKey(AippConst.ATTR_PUBLISH_TIME_KEY)) { - detail.setPublishAt(LocalDateTime.parse(ObjectUtils.cast(meta.getAttributes() - .get(AippConst.ATTR_PUBLISH_TIME_KEY)))); - } - - return Rsp.ok(detail); } /** @@ -213,86 +118,48 @@ public Rsp queryAippDetail(String aippId, String version, Operati public PageResponse listAipp(AippQueryCondition cond, PaginationCondition page, OperationContext context) { log.info("listAipp cond{} page{}", cond, page); - MetaFilter metaFilter = new MetaFilter(); - if (StringUtils.isNotBlank(cond.getName())) { - metaFilter.setNames(Collections.singletonList(cond.getName())); - } - if (StringUtils.isNotBlank(cond.getCreator())) { - metaFilter.setCreators(Collections.singletonList(cond.getCreator())); - } - metaFilter.setCategories(Collections.singletonList(JaneCategory.AIPP.name())); - metaFilter.setAttributes(Collections.singletonMap(AippConst.ATTR_AIPP_TYPE_KEY, - Collections.singletonList(AippTypeEnum.NORMAL.name()))); - String sortEncode = MetaUtils.formatSorter(cond.getSort(), cond.getOrder()); - metaFilter.setOrderBys(Collections.singletonList(sortEncode)); - // 容老数据,当前把没有 aipp_type 的数据也进行获取。等数据库刷完数据后,更改逻辑 - RangedResultSet metaRes = - this.metaService.list(metaFilter, true, page.getOffset(), page.getPageSize(), context); - List overviewDtoList = metaRes.getResults().stream().map(item -> { - this.handleOldData(item); - AippOverviewRspDto dto = MetaConvertor.INSTANCE.toAippOverviewRspDto(item); - String status = ObjectUtils.cast(item.getAttributes().get(AippConst.ATTR_META_STATUS_KEY)); - - dto.setStatus(status); - - dto.setVersion(item.getVersion()); // 兼容没有基线版本的1.0.0版本草稿 - if (this.isDraft(item, status)) { - dto.setVersion(ObjectUtils.cast(item.getAttributes().get(AippConst.ATTR_BASELINE_VERSION_KEY))); - - dto.setDraftVersion(item.getVersion()); - } - - if (item.getAttributes().containsKey(AippConst.ATTR_PUBLISH_TIME_KEY)) { - dto.setPublishAt(LocalDateTime.parse(ObjectUtils.cast(item.getAttributes() - .get(AippConst.ATTR_PUBLISH_TIME_KEY)))); + RangedResultSet resultSet = this.appTaskService.getTasks( + AppTask.asQueryEntity(page.getOffset(), page.getPageSize()) + .addName(cond.getName()) + .addCreator(cond.getCreator()) + .addCategory(JaneCategory.AIPP.name()) + .putQueryAttribute(AippConst.ATTR_AIPP_TYPE_KEY, NORMAL.name()) + .addOrderBy(cond.getSort(), cond.getOrder()) + .build(), context); + + List overviewDtoList = resultSet.getResults().stream().map(task -> { + AippOverviewRspDto dto = new AippOverviewRspDto(); + dto.setCreatedAt(task.getEntity().getCreationTime()); + dto.setUpdatedAt(task.getEntity().getLastModificationTime()); + dto.setUpdater(task.getEntity().getLastModifier()); + dto.setAippId(task.getEntity().getAppSuiteId()); + dto.setStatus(task.getEntity().getStatus()); + dto.setVersion(task.getEntity().getVersion()); // 兼容没有基线版本的1.0.0版本草稿 + if (task.isDraft()) { + dto.setVersion(task.getEntity().getBaseLineVersion()); + dto.setDraftVersion(task.getEntity().getVersion()); } + String publishTime = task.getEntity().getPublishTime(); + Optional.ofNullable(publishTime).map(LocalDateTime::parse).ifPresent(dto::setPublishAt); return dto; }).sorted(Comparator.comparing(AippOverviewDto::getUpdatedAt).reversed()).collect(Collectors.toList()); - return new PageResponse<>(metaRes.getRange().getTotal(), null, overviewDtoList); - } - - private void handleOldData(Meta item) { - if (Objects.equals(item.getId(), OLD_VERSION_ID)) { - item.setId(item.getVersionId()); - } - } - - private MetaFilter buildOldDataMetaFilter(MetaFilter metaFilter) { - MetaFilter oldFilter = new MetaFilter(metaFilter.getMetaIds(), - metaFilter.getVersionIds(), - metaFilter.getNames(), - metaFilter.getCategories(), - metaFilter.getCreators(), - metaFilter.getOrderBys(), - metaFilter.getVersions(), - metaFilter.getAttributes()); - oldFilter.setMetaIds(Collections.singletonList(OLD_VERSION_ID)); - oldFilter.setAttributes(Collections.emptyMap()); - return oldFilter; - } - - private boolean isDraft(Meta item, String status) { - return item.getAttributes().get(AippConst.ATTR_BASELINE_VERSION_KEY) != null && !Objects.equals( - AippMetaStatusEnum.getAippMetaStatus(status), - AippMetaStatusEnum.ACTIVE); + return new PageResponse<>(resultSet.getRange().getTotal(), null, overviewDtoList); } /** * 查询指定aipp的版本列表 * * @param aippId aippId - * @param context 操作上下文 + * @param ctx 操作上下文 * @return aipp 版本概况 */ @Override - public List listAippVersions(String aippId, OperationContext context) { - return MetaUtils.getAllPublishedMeta(this.metaService, aippId, context) - .stream() - .map(meta -> new AippVersionDto(meta.getVersion(), - ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_META_STATUS_KEY)), - meta.getCreator(), - meta.getCreationTime())) + public List listAippVersions(String aippId, OperationContext ctx) { + List tasks = this.appTaskService.getTaskList(aippId, NORMAL.name(), ACTIVE.getCode(), ctx); + return tasks.stream() + .map(t -> new AippVersionDto(t.getEntity().getVersion(), t.getEntity().getStatus(), + t.getEntity().getCreator(), t.getEntity().getCreationTime())) .collect(Collectors.toList()); } @@ -307,21 +174,17 @@ public List listAippVersions(String aippId, OperationContext con @Override public void deleteAipp(String aippId, String version, OperationContext context) throws AippForbiddenException { log.info("deleting aipp {} version {}", aippId, version); - Meta meta = MetaUtils.getAnyMeta(metaService, aippId, version, context); - Map attr = meta.getAttributes(); - if (!AippMetaStatusEnum.INACTIVE.getCode().equals(attr.get(AippConst.ATTR_META_STATUS_KEY))) { - log.error("not allow to delete an active aipp, aippId {} version {} status {}", - aippId, - version, - attr.getOrDefault(AippConst.ATTR_META_STATUS_KEY, "null")); + AppTask task = this.appTaskService.getLatest(aippId, version, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", aippId, version))); + if (task.isActive()) { + log.error("not allow to delete an active aipp, aippId {} version {} status {}", aippId, version, + task.getEntity().getStatus()); throw new AippForbiddenException(context, AippErrCode.DELETE_AIPP_FORBIDDEN); } - try { - int ret = - this.flowsService.deleteFlows(ObjectUtils.cast(attr.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)), - meta.getVersion(), - context); + int ret = this.flowsService.deleteFlows(task.getEntity().getFlowConfigId(), task.getEntity().getVersion(), + context); if (ret != 0) { log.error("delete aipp {} version {} failed, ret {}", aippId, version, ret); } @@ -329,34 +192,7 @@ public void deleteAipp(String aippId, String version, OperationContext context) log.error("delete aipp failed, aipp {} version {}", aippId, version); throw new AippException(context, AippErrCode.APP_DELETE_FAILED); } - this.metaService.delete(meta.getVersionId(), context); - } - - private MetaDeclarationInfo buildInitialMetaDeclaration(AippDto aippDto, AippCreateDto baselineInfo, - FlowInfo flowInfo, String aippType) { - MetaDeclarationInfo declaration = new MetaDeclarationInfo(); - declaration.setCategory(Undefinable.defined(JaneCategory.AIPP.name())); - declaration.setName(Undefinable.defined(aippDto.getName())); - declaration.setVersion(Undefinable.defined(aippDto.getVersion())); - - String description = aippDto.getDescription(); - declaration.putAttribute(AippConst.ATTR_FLOW_CONFIG_ID_KEY, flowInfo.getFlowId()); - declaration.putAttribute(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()); - declaration.putAttribute(AippConst.ATTR_DESCRIPTION_KEY, description == null ? "aipp 编排应用" : description); - declaration.putAttribute(AippConst.ATTR_META_ICON_KEY, aippDto.getIcon()); - declaration.putAttribute(AippConst.ATTR_AIPP_TYPE_KEY, aippType); - declaration.putAttribute(AippConst.ATTR_APP_ID_KEY, aippDto.getAppId()); - if (baselineInfo != null) { - declaration.putAttribute(AippConst.ATTR_BASELINE_VERSION_KEY, baselineInfo.getVersion()); - declaration.setBasicMetaTemplateId(Undefinable.defined(baselineInfo.getAippId())); - } - - List props = AippConst.STATIC_META_ITEMS.stream() - .map(FormMetaConvertor.INSTANCE::toMetaPropertyDeclarationInfo) - .collect(Collectors.toList()); - declaration.setProperties(Undefinable.defined(props)); - - return declaration; + this.appTaskService.deleteTaskById(task.getEntity().getTaskId(), context); } /** @@ -451,58 +287,39 @@ private AippCreateDto saveAipp(AippDto aippDto, AippCreateDto baselineInfo, Oper e.getMessage()); throw new AippException(context, AippErrCode.APP_PUBLISH_FAILED); } - MetaDeclarationInfo declarationInfo = - this.buildInitialMetaDeclaration(aippDto, baselineInfo, flowInfo, AippTypeEnum.NORMAL.name()); - log.debug("create aipp, declaration attr info {}", declarationInfo.getAttributes().getValue()); try { - Meta meta = this.metaService.create(declarationInfo, context); - return AippCreateDto.builder().aippId(meta.getId()).version(meta.getVersion()).build(); + AppTask createArgs = AppTask.asCreateEntity() + .fetch(aippDto) + .fetch(baselineInfo) + .setFlowConfigId(flowInfo.getFlowId()) + .setAippType(NORMAL.name()) + .build(); + log.debug("create aipp, task info {}", createArgs.getEntity().toString()); + AppTask appTask = this.appTaskService.createTask(createArgs, context); + return AippCreateDto.builder() + .aippId(appTask.getEntity().getAppSuiteId()) + .version(appTask.getEntity().getVersion()) + .build(); } catch (ConflictException e) { log.error("create aipp failed, error: {}", e.getMessage()); throw new AippParamException(context, AippErrCode.AIPP_NAME_IS_DUPLICATE); } } - private void putAttrIfNotBlank(Map attr, String key, String value) { - if (StringUtils.isNotBlank(value)) { - attr.put(key, value); - } + private void updateMetaDeclaration(String metaVersionId, String version, AippDto aippDto, OperationContext ctx) { + TaskDomainEntity updateEntity = AppTask.asUpdateEntity(metaVersionId) + .setName(aippDto.getName()) + .setDescription(aippDto.getDescription()) + .setIcon(aippDto.getIcon()) + .setVersion(version) + .fetch(aippDto.getFlowViewData()); + log.debug("patch meta, update entity {}", updateEntity); + this.appTaskService.updateTask(updateEntity.build(), ctx); } - private void updateAttribute(Map attr, AippDto aippDto) { - putAttrIfNotBlank(attr, AippConst.ATTR_DESCRIPTION_KEY, aippDto.getDescription()); - putAttrIfNotBlank(attr, AippConst.ATTR_META_ICON_KEY, aippDto.getIcon()); - - Map flowView = aippDto.getFlowViewData(); - if (flowView != null && !flowView.isEmpty()) { - attr.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, - flowView.getOrDefault(AippConst.FLOW_CONFIG_ID_KEY, attr.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY))); - attr.put(AippConst.ATTR_VERSION_KEY, - flowView.getOrDefault(AippConst.FLOW_CONFIG_VERSION_KEY, attr.get(AippConst.ATTR_VERSION_KEY))); - } - } - - private void updateMetaDeclaration(String metaVersionId, String version, AippDto aippDto, Map attr, - OperationContext context) { - MetaDeclarationInfo declaration = new MetaDeclarationInfo(); - if (StringUtils.isNotBlank(aippDto.getName())) { - declaration.setName(Undefinable.defined(aippDto.getName())); - declaration.setVersion(Undefinable.defined(version)); // 底层task更新name的时候必须带上version - } - updateAttribute(attr, aippDto); - declaration.setAttributes(Undefinable.defined(attr)); - log.debug("patch meta, metaVersionId {} name {} attr {}", - metaVersionId, - declaration.getName().getDefined() ? declaration.getName().getValue() : "undefined", - declaration.getAttributes().getDefined() ? declaration.getAttributes().getValue() : "undefined"); - this.metaService.patch(metaVersionId, declaration, context); - } - - private void validateUpdate(String aippId, Map attr, String name, OperationContext context) { - if (!AippMetaStatusEnum.INACTIVE.getCode().equals(attr.get(AippConst.ATTR_META_STATUS_KEY))) { - log.error("not allow to update an active aipp, aippId {} status {}", - aippId, - attr.getOrDefault(AippConst.ATTR_META_STATUS_KEY, "null")); + private void validateUpdate(String aippId, AppTask task, String name, OperationContext context) { + if (task.isActive()) { + log.error("not allow to update an active aipp, aippId {} status {}", aippId, task.getEntity().getStatus()); throw new AippForbiddenException(context, AippErrCode.UPDATE_AIPP_FORBIDDEN); } if (StringUtils.isBlank(name)) { @@ -526,255 +343,41 @@ public AippCreateDto update(AippDto aippDto, OperationContext context) String aippId = aippDto.getId(); String version = aippDto.getVersion(); log.info("update aipp {} name {}", aippId, aippDto.getName()); - Meta meta = MetaUtils.getAnyMeta(this.metaService, aippId, version, context); - if (meta == null) { + Optional taskOp = this.appTaskService.getLatest(aippId, version, context); + if (taskOp.isEmpty()) { return this.updateNewVersionAipp(aippDto, context, aippId, version); } - Map attr = meta.getAttributes(); - validateUpdate(aippId, attr, aippDto.getName(), context); - updateMetaDeclaration(meta.getVersionId(), meta.getVersion(), aippDto, attr, context); + AppTask task = taskOp.get(); + validateUpdate(aippId, task, aippDto.getName(), context); + this.updateMetaDeclaration(task.getEntity().getTaskId(), task.getEntity().getVersion(), aippDto, context); // 更新流程 if (aippDto.getFlowViewData() == null || aippDto.getFlowViewData().isEmpty()) { - return AippCreateDto.builder().aippId(aippId).version(meta.getVersion()).build(); + return AippCreateDto.builder().aippId(aippId).version(task.getEntity().getVersion()).build(); } try { - this.flowsService.updateFlows(ObjectUtils.cast(attr.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)), - ObjectUtils.cast(attr.get(AippConst.ATTR_VERSION_KEY)), + this.flowsService.updateFlows(task.getEntity().getFlowConfigId(), + task.getEntity().getAttributeVersion(), JsonUtils.toJsonString(aippDto.getFlowViewData()), context); } catch (JobberException e) { log.error("update aipp failed, aipp {} name {}", aippId, aippDto.getName()); throw new AippException(context, AippErrCode.APP_UPDATE_FAILED); } - return AippCreateDto.builder().aippId(aippId).version(meta.getVersion()).build(); + return AippCreateDto.builder().aippId(aippId).version(task.getEntity().getVersion()).build(); } private AippCreateDto updateNewVersionAipp(AippDto aippDto, OperationContext context, String aippId, String version) { - Meta lastDraftMeta = MetaUtils.getLastDraftMeta(this.metaService, aippId, context); - String flowId = lastDraftMeta.getAttributes() - .getOrDefault(AippConst.ATTR_FLOW_CONFIG_ID_KEY, StringUtils.EMPTY) - .toString(); + AppTask task = this.appTaskService.getLatestCreate(aippId, NORMAL.name(), INACTIVE.getCode(), context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, aippType: {}, status: {}.", aippId, + NORMAL.name(), INACTIVE.getCode()))); + + String flowId = Optional.ofNullable(task.getEntity().getFlowConfigId()).orElse(StringUtils.EMPTY); this.upgradeAippHandle(aippDto, AippCreateDto.builder().aippId(aippId).build(), context, flowId, version); return this.update(aippDto, context); } - private AippCreateDto createPreviewAipp(String baselineVersion, AippDto aippDto, OperationContext context) { - Map flowViewData = aippDto.getFlowViewData(); - String flowId = ObjectUtils.cast(flowViewData.get(AippConst.FLOW_CONFIG_ID_KEY)); - String previewVersion = ObjectUtils.cast(flowViewData.get(AippConst.FLOW_CONFIG_VERSION_KEY)); - // 创建、发布流程定义 - FlowInfo flowInfo = this.flowsService.publishFlowsWithoutElsa(flowId, - previewVersion, - JsonUtils.toJsonString(flowViewData), - context); - // 预览时,aipp 的 version 用的是 flowInfo 的 version,是否合理待确认 - aippDto.setVersion(flowInfo.getVersion()); - MetaDeclarationInfo declarationInfo = this.buildInitialMetaDeclaration(aippDto, - AippCreateDto.builder().aippId(aippDto.getId()).version(baselineVersion).build(), - flowInfo, - AippTypeEnum.PREVIEW.name()); - AppBuilderApp app = this.factory.create(aippDto.getAppId()); - List aippNodeForms = buildAippNodeForms(flowInfo, app.getFormProperties()); - // 追加attribute - Map attr = declarationInfo.getAttributes().getValue(); - appendAttribute(attr, aippNodeForms, flowInfo.getFlowDefinitionId()); - - log.debug("create preview aipp, declaration attr info {}", attr); - Meta meta = metaService.create(declarationInfo, context); - return AippCreateDto.builder().aippId(meta.getId()).version(previewVersion).build(); - } - - private void appendAttribute(Map attr, List aippNodeForms, String flowDefinitionId) { - for (AippNodeForms node : aippNodeForms) { - if (node.getMetaInfo().isEmpty()) { - continue; - } - if (NodeTypes.START.getType().equalsIgnoreCase(node.getType())) { - attr.put(AippConst.ATTR_START_FORM_ID_KEY, node.getMetaInfo().get(0).getFormId()); - attr.put(AippConst.ATTR_START_FORM_VERSION_KEY, node.getMetaInfo().get(0).getVersion()); - } - if (NodeTypes.END.getType().equalsIgnoreCase(node.getType())) { - attr.put(AippConst.ATTR_END_FORM_ID_KEY, node.getMetaInfo().get(0).getFormId()); - attr.put(AippConst.ATTR_END_FORM_VERSION_KEY, node.getMetaInfo().get(0).getVersion()); - } - } - attr.put(AippConst.ATTR_FLOW_DEF_ID_KEY, flowDefinitionId); - attr.put(AippConst.ATTR_PUBLISH_TIME_KEY, LocalDateTime.now().toString()); - attr.put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.ACTIVE.getCode()); - } - - private MetaPropertyDeclarationInfo trimPropsName(MetaPropertyDeclarationInfo info) { - String newName = info.getName().getValue().trim(); - info.setName(Undefinable.defined(newName)); - return info; - } - - private List getMetaPropertyDeclarationInfos(List aippNodeForms) { - List formProps = aippNodeForms.stream() - .flatMap(node -> node.getMetaInfo().stream()) - .flatMap(metaList -> metaList.getFormMetaItems().stream()) - .map(FormMetaConvertor.INSTANCE::toMetaPropertyDeclarationInfo) - .filter(item -> StringUtils.isNotBlank(item.getName().getValue())) - .map(this::trimPropsName) - .collect(Collectors.toList()); - - // 检查name不能重复、且不能和初始变量重复 - validateProps(formProps); - return formProps; - } - - /** - * 预览aipp - * - * @param baselineVersion aipp 的基线版本 - * @param aippDto aipp定义 - * @param context 操作上下文 - * @return 创建预览aipp的id和version - * @throws AippException 预览aipp异常 - */ - @Override - public AippCreateDto previewAipp(String baselineVersion, AippDto aippDto, OperationContext context) - throws AippException { - List metaList = MetaUtils.getAllMetasByAppId(this.metaService, aippDto.getAppId(), context); - if (!metaList.isEmpty()) { - Meta meta = metaList.get(0); - if (MetaUtils.isPublished(meta)) { - return AippCreateDto.builder().aippId(meta.getId()).version(meta.getVersion()).build(); - } - } - FlowDefinitionResult definitionResult = this.getSameFlowDefinition(aippDto); - if (definitionResult != null) { - RangedResultSet metas = - this.metaService.list(this.buildFlowDefinitionFilter(definitionResult), true, 0, 1, context); - if (!metas.getResults().isEmpty()) { - Meta meta = metas.getResults().get(0); - return AippCreateDto.builder().aippId(meta.getId()).version(meta.getVersion()).build(); - } - } - // 过滤预览版本 - if (AippStringUtils.isPreview(baselineVersion)) { - throw new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, "version is preview"); - } - // 设置预览版本 - int retryTimes = RETRY_PREVIEW_TIMES; - String previewVersion; - String errorMsg; - int errorCode; - do { - previewVersion = VersionUtils.buildPreviewVersion(baselineVersion); - aippDto.getFlowViewData().put(AippConst.FLOW_CONFIG_VERSION_KEY, previewVersion); - try { - return this.createPreviewAipp(baselineVersion, aippDto, context); - } catch (JobberException e) { - if (e.getCode() != ErrorCodes.FLOW_ALREADY_EXIST.getErrorCode()) { - errorMsg = e.getMessage(); - errorCode = e.getCode(); - break; - } - errorMsg = e.getMessage(); - errorCode = e.getCode(); - log.warn("create preview aipp failed, times {} aippId {} version {}, error {}", - RETRY_PREVIEW_TIMES - retryTimes, - aippDto.getId(), - previewVersion, - e.getMessage()); - } - } while (retryTimes-- > 0); - log.error("Failed to preview aipp.[errorMsg={}]", errorMsg); - throw this.handleException(context, errorCode); - } - - private AippException handleException(OperationContext context, int code) { - switch (ErrorCodes.getErrorCodes(code)) { - case INVALID_FLOW_NODE_SIZE: - return new AippException(context, AippErrCode.INVALID_FLOW_NODE_SIZE); - case INVALID_START_NODE_EVENT_SIZE: - return new AippException(context, AippErrCode.INVALID_START_NODE_EVENT_SIZE); - case INVALID_EVENT_CONFIG: - case INVALID_STATE_NODE_EVENT_SIZE: - return new AippException(context, AippErrCode.INVALID_EVENT_CONFIG); - default: - return new AippException(context, AippErrCode.INVALID_FLOW_CONFIG); - } - } - - private MetaFilter buildFlowDefinitionFilter(FlowDefinitionResult definitionResult) { - MetaFilter filter = new MetaFilter(); - Map> attributes = new HashMap<>(); - attributes.put("flow_definition_id", Collections.singletonList(definitionResult.getFlowDefinitionId())); - attributes.put("flow_config_id", Collections.singletonList(definitionResult.getMetaId())); - filter.setAttributes(attributes); - return filter; - } - - private FlowDefinitionResult getSameFlowDefinition(AippDto aippDto) { - Map flowViewData = aippDto.getFlowViewData(); - String metaId = String.valueOf(flowViewData.getOrDefault(AippConst.FLOW_CONFIG_ID_KEY, StringUtils.EMPTY)); - String version = - String.valueOf(flowViewData.getOrDefault(AippConst.FLOW_CONFIG_VERSION_KEY, StringUtils.EMPTY)); - List flowDefinitions = - this.flowDefinitionService.getFlowDefinitionByMetaIdAndPartVersion(metaId, version + "-", null); - String parsedGraphData = - this.flowDefinitionService.getParsedGraphData(JsonUtils.toJsonString(aippDto.getFlowViewData()), - version); - Map aippFlowDefinitionMapping = this.buildFlowDefinition(parsedGraphData); - return flowDefinitions.stream().limit(1).filter(definition -> { - Map map = this.buildFlowDefinition(definition.getGraph()); - return this.compareMaps(map, aippFlowDefinitionMapping); - }).findAny().orElse(null); - } - - private Map buildFlowDefinition(String flowDefinition) { - Map parsedFlowDefinitionMapping = JsonUtils.parseObject(flowDefinition); - - // 这边 name 和 version 不需要比较 - parsedFlowDefinitionMapping.remove(AippConst.FLOW_CONFIG_NAME); - parsedFlowDefinitionMapping.remove(AippConst.FLOW_CONFIG_VERSION_KEY); - return parsedFlowDefinitionMapping; - } - - /** - * 比较两个map是否相等 - * - * @param map1 第一个map - * @param map2 第二个map - * @return 如果两个map相等,返回true,否则返回false - */ - public boolean compareMaps(Map map1, Map map2) { - if (map1 == map2) { - return true; - } - if (map1 == null || map2 == null) { - return false; - } - if (map1.size() != map2.size()) { - return false; - } - for (Map.Entry entry : map1.entrySet()) { - String key = entry.getKey(); - Object value1 = entry.getValue(); - Object value2 = map2.get(key); - if (!map2.containsKey(key) || !this.isSameObject(value1, value2)) { - return false; - } - } - return true; - } - - private boolean isSameObject(Object obj1, Object obj2) { - if (obj1 == obj2) { - return true; - } - if (obj1 == null || obj2 == null) { - return false; - } - if (obj1 instanceof Map && obj2 instanceof Map) { - return compareMaps(ObjectUtils.cast(obj1), ObjectUtils.cast(obj2)); - } - return Objects.equals(obj1, obj2); - } - /** * 退出预览aipp的清理 * @@ -789,33 +392,14 @@ public void cleanPreviewAipp(String previewAippId, String previewVersion, Operat throw new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, "version is not preview"); } CompletableFuture.runAsync(() -> { - Meta previewMeta = MetaUtils.getAnyMeta(metaService, previewAippId, previewVersion, context); - cleanResourceForPreview(previewMeta, previewAippId, previewVersion, context); + AppTask previewTask = this.appTaskService.getLatest(previewAippId, previewVersion, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", previewAippId, + previewVersion))); + previewTask.cleanResource(context); }); } - private void cleanResourceForPreview(Meta previewMeta, String previewAippId, String previewVersion, - OperationContext context) { - if (previewMeta.getAttributes() - .getOrDefault(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()) - .equals(AippMetaStatusEnum.ACTIVE.getCode())) { - this.aippRunTimeService.terminateAllPreviewInstances(previewAippId, - previewMeta.getVersionId(), - true, - context); - } - String flowId = previewMeta.getAttributes().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY).toString(); - if (!StringUtils.isBlank(flowId)) { - try { - this.flowsService.deleteFlowsWithoutElsa(flowId, previewVersion, context); - } catch (JobberException e) { - log.error("delete flow failed, flowId: {} previewVersion: {}", flowId, previewVersion); - throw new AippException(context, AippErrCode.APP_PUBLISH_FAILED); - } - } - this.metaService.delete(previewMeta.getVersionId(), context); - } - private boolean isValidUpgradeVersion(String oldVersion, String newVersion) { final String delimiter = "\\."; if (StringUtils.isBlank(oldVersion) || StringUtils.isBlank(newVersion)) { @@ -865,11 +449,12 @@ public AippCreateDto upgrade(String baselineVersion, AippDto aippDto, OperationC AippErrCode.INPUT_PARAM_IS_INVALID, AippConst.FLOW_CONFIG_VERSION_KEY); } - Meta latestMeta = MetaUtils.getLastNormalMeta(this.metaService, aippId, context); - String latestMetaStatus = latestMeta.getAttributes() - .getOrDefault(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()) - .toString(); - String flowId = latestMeta.getAttributes().getOrDefault(AippConst.ATTR_FLOW_CONFIG_ID_KEY, "").toString(); + AppTask task = this.appTaskService.getLatestCreate(aippId, NORMAL.name(), context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, aippType: {}.", aippId, + NORMAL.name()))); + + String flowId = Optional.ofNullable(task.getEntity().getFlowConfigId()).orElse(StringUtils.EMPTY); Validation.notBlank(flowId, () -> { throw new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, @@ -877,7 +462,7 @@ public AippCreateDto upgrade(String baselineVersion, AippDto aippDto, OperationC }); String newFlowVersion = aippDto.getFlowViewData().getOrDefault(AippConst.FLOW_CONFIG_VERSION_KEY, DEFAULT_VERSION).toString(); - if (this.isUpgradeVersion(newAippVersion, latestMeta, latestMetaStatus)) { + if (task.isUpgrade(newAippVersion)) { this.upgradeAippHandle(aippDto, AippCreateDto.builder().aippId(aippId).version(baselineVersion).build(), context, @@ -886,341 +471,4 @@ public AippCreateDto upgrade(String baselineVersion, AippDto aippDto, OperationC } return this.update(aippDto, context); } - - // 如果是第一个草稿版本,或者新的草稿版本与之前版本号不一致,都需要升级版本操作。 - private boolean isUpgradeVersion(String newAippVersion, Meta latestMeta, String latestMetaStatus) { - return latestMetaStatus.equals(AippMetaStatusEnum.ACTIVE.getCode()) || !Objects.equals(newAippVersion, - latestMeta.getVersion()); - } - - private void validateProps(List props) { - Set staticKeySet = - AippConst.STATIC_META_ITEMS.stream().map(FormMetaItem::getKey).collect(Collectors.toSet()); - if (staticKeySet.size() != AippConst.STATIC_META_ITEMS.size()) { - log.error("The initial meta item key cant be repeated."); - throw new AippException(AippErrCode.AIPP_PROPS_KEY_DUPLICATE); - } - Iterator iter = props.iterator(); - while (iter.hasNext()) { - MetaPropertyDeclarationInfo prop = iter.next(); - if (prop.getName().getDefined() && staticKeySet.contains(prop.getName().getValue())) { - iter.remove(); - log.warn("The form field repeat {}", prop.getName().getValue()); - continue; - } - staticKeySet.add(prop.getName().getValue()); - } - } - - private List buildPatchProps(List props, Meta meta) { - if (meta.getProperties() == null) { - return props; - } - List propsPatch = meta.getProperties() - .stream() - .map(TaskPropertyConvertor.INSTANCE::toMetaPropertyDeclarationInfo) - .collect(Collectors.toList()); - propsPatch.addAll(props); - - return propsPatch; - } - - private MetaDeclarationInfo buildPublishMetaDeclaration(String aippId, List aippNodeForms, - String flowDefinitionId, Meta meta, AippDto aippDto, String uniqueName) { - // 解析表单属性字段 - List props = getMetaPropertyDeclarationInfos(aippNodeForms); - - // 追加aipp meta属性字段 - MetaDeclarationInfo declaration = new MetaDeclarationInfo(); - - // 追加/更新 aipp attribute字段 - Map attrPatch = meta.getAttributes(); - appendAttribute(attrPatch, aippNodeForms, flowDefinitionId); - updateAttribute(attrPatch, aippDto); - attrPatch.put(AippConst.ATTR_PUBLISH_DESCRIPTION, aippDto.getPublishedDescription()); - attrPatch.put(AippConst.ATTR_PUBLISH_UPDATE_LOG, aippDto.getPublishedUpdateLog()); - attrPatch.put(AippConst.ATTR_UNIQUE_NAME, uniqueName); - declaration.setAttributes(Undefinable.defined(attrPatch)); - declaration.setName(Undefinable.defined(meta.getName())); - declaration.setVersion(Undefinable.defined(meta.getVersion())); - - log.debug("patch meta, aippId {} name {} attr {}", - aippId, - declaration.getName().getDefined() ? declaration.getName().getValue() : "undefined", - declaration.getAttributes().getDefined() ? declaration.getAttributes().getValue() : "undefined"); - return declaration; - } - - private List buildAippNodeForms(FlowInfo flowInfo, List formProperties) { - if (flowInfo.getFlowNodes() == null) { - return Collections.emptyList(); - } - return flowInfo.getFlowNodes().stream().filter(item -> item.getFlowNodeForm() != null).map(item -> { - FlowNodeFormInfo form = item.getFlowNodeForm(); - List parameter = - Collections.singletonList(new FormMetaQueryParameter(form.getFormId(), form.getVersion())); - return AippNodeForms.builder() - .type(item.getType()) - .metaInfo(FormUtils.buildFormMetaInfos(parameter, formProperties)) - .build(); - }).collect(Collectors.toList()); - } - - private void rollbackAipp(String versionId, FlowInfo flowInfo, OperationContext context) { - try { - if (flowInfo != null) { - this.flowDefinitionService.deleteFlows(flowInfo.getFlowDefinitionId(), context); - } - this.metaService.delete(versionId, context); - } catch (AippException e) { - log.error("rollbackAipp failed, versionId {}, e = {}", versionId, e); - } - } - - private FlowInfo publishFlow(AippDto aippDto, Map attr, OperationContext context) { - String flowConfigId = ObjectUtils.cast(attr.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)); - String version = ObjectUtils.cast(attr.get(AippConst.ATTR_VERSION_KEY)); - FlowInfo flowInfo; - try { - flowInfo = this.flowsService.publishFlows(flowConfigId, - version, - JsonUtils.toJsonString(aippDto.getFlowViewData()), - context); - } catch (JobberException e) { - AippErrCode retCode = (e.getCode() == ErrorCodes.FLOW_ALREADY_EXIST.getErrorCode()) - ? AippErrCode.FLOW_ALREADY_EXIST - : AippErrCode.APP_PUBLISH_FAILED; - throw new AippException(context, retCode); - } - return flowInfo; - } - - @Override - public Rsp publish(AippDto aippDto, AppBuilderApp app, OperationContext context) - throws AippException { - String aippId = aippDto.getId(); - String version = aippDto.getVersion(); - CompletableFuture.runAsync(() -> MetaUtils.getAllPreviewMeta(metaService, aippId, context) - .forEach(meta -> cleanResourceForPreview(meta, aippId, meta.getVersion(), context))); - - Meta meta = MetaUtils.getLastNormalMeta(metaService, aippId, context); - Map attr = meta.getAttributes(); - String originalDraftVersion = meta.getVersion(); - if (!this.isValidUpgradeVersion(originalDraftVersion, version)) { - log.error("old version {} is larger than new one {}", originalDraftVersion, version); - throw new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, "version"); - } - attr.put(AippConst.ATTR_VERSION_KEY, version); - aippDto.getFlowViewData().put(AippConst.ATTR_VERSION_KEY, version); - meta.setVersion(version); - log.info("publish aipp {} name {} attr {}", aippId, aippDto.getName(), attr); - - validateUpdate(aippId, attr, aippDto.getName(), context); - // 发布流程 - FlowInfo flowInfo = null; - try { - flowInfo = publishFlow(aippDto, attr, context); - // 查询表单 元数据 - List aippNodeForms = buildAippNodeForms(flowInfo, app.getFormProperties()); - - // 往 store 发布 - String uniqueName = this.publishToStore(aippDto, context, flowInfo); - - // 发布aipp - MetaDeclarationInfo declaration = buildPublishMetaDeclaration(aippId, - aippNodeForms, - flowInfo.getFlowDefinitionId(), - meta, - aippDto, - uniqueName); - this.metaService.patch(meta.getVersionId(), declaration, context); - return Rsp.ok(AippCreateDto.builder() - .aippId(aippId) - .version(meta.getVersion()) - .toolUniqueName(uniqueName) - .build()); - } catch (AippException e) { - log.error("publish aipp {} failed.", aippId, e); - rollbackAipp(meta.getVersionId(), flowInfo, context); - throw e; - } - } - - private String publishToStore(AippDto aippDto, OperationContext context, FlowInfo flowInfo) { - AppPublishData appData = this.buildItemData(aippDto, context, flowInfo); - String uniqueName = ""; - if (StringUtils.equalsIgnoreCase(aippDto.getType(), APP_TYPE)) { - uniqueName = this.appService.publishApp(appData); - } else if (StringUtils.equalsIgnoreCase(aippDto.getType(), WATERFLOW_TYPE)) { - if (appData.getUniqueName() == null) { - AppData.fillAppData(appData); - PluginData pluginData = this.buildPluginData(appData); - this.pluginService.addPlugin(pluginData); - uniqueName = appData.getUniqueName(); - } else { - // 修复store切换四层模型后未修改完全的问题 - AppData.fillAppData(appData); - PluginData pluginData = this.buildPluginData(appData); - uniqueName = this.toolService.upgradeTool(pluginData.getPluginToolDataList().get(0)); - } - } else { - throw new AippException(AippErrCode.ILLEGAL_AIPP_TYPE); - } - this.appBuilderAppMapper.updateAppWithStoreId(uniqueName, aippDto.getAppId(), aippDto.getVersion()); - return uniqueName; - } - - private PluginData buildPluginData(AppData appData) { - PluginData pluginData = new PluginData(); - pluginData.setDeployStatus(DeployStatus.RELEASED.name()); - pluginData.setCreator(appData.getCreator()); - pluginData.setModifier(appData.getModifier()); - pluginData.setPluginName(appData.getName()); - pluginData.setExtension(new HashMap<>()); - pluginData.setPluginId(Entities.generateId() + Entities.generateId()); - PluginToolData pluginToolData = new PluginToolData(); - pluginToolData.setCreator(appData.getCreator()); - pluginToolData.setModifier(appData.getModifier()); - pluginToolData.setName(appData.getName()); - pluginToolData.setDescription(appData.getDescription()); - pluginToolData.setSchema(appData.getSchema()); - pluginToolData.setRunnables(appData.getRunnables()); - pluginToolData.setSource(appData.getSource()); - pluginToolData.setIcon(appData.getIcon()); - pluginToolData.setTags(appData.getTags()); - pluginToolData.setVersion(appData.getVersion()); - pluginToolData.setLikeCount(appData.getLikeCount()); - pluginToolData.setDownloadCount(appData.getDownloadCount()); - pluginToolData.setPluginId(pluginData.getPluginId()); - if (appData.getUniqueName() != null) { - pluginToolData.setUniqueName(appData.getUniqueName()); - } - // 修复store切换四层模型后未修改完全的问题 - pluginToolData.setDefName(appData.getDefName()); - pluginToolData.setDefGroupName(appData.getDefGroupName()); - pluginToolData.setGroupName(appData.getGroupName()); - pluginData.setPluginToolDataList(Collections.singletonList(pluginToolData)); - pluginData.setDefinitionGroupDataList(Arrays.asList(AppData.toDefGroup(appData))); - pluginData.setToolGroupDataList(Arrays.asList(AppData.toToolGroup(appData))); - return pluginData; - } - - private AppPublishData buildItemData(AippDto aippDto, OperationContext context, FlowInfo flowInfo) { - AppCategory appCategory = AppCategory.findByType(aippDto.getType()) - .orElseThrow(() -> new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID)); - AppPublishData itemData = new AppPublishData(); - itemData.setCreator(context.getOperator()); - itemData.setModifier(context.getOperator()); - itemData.setIcon(aippDto.getIcon()); - itemData.setName(aippDto.getName()); - itemData.setDescription(aippDto.getDescription()); - itemData.setAppCategory(aippDto.getAppCategory()); - itemData.setVersion(aippDto.getVersion()); - itemData.setUniqueName(aippDto.getUniqueName()); - itemData.setSchema(this.buildToolSchema(appCategory, context, aippDto, flowInfo)); - itemData.setSource(appCategory.getSource()); - itemData.setTags(new HashSet() { - { - add(appCategory.getTag()); - add(buildAppTypeTag(aippDto)); - } - }); - itemData.setRunnables(this.buildRunnables(aippDto)); - return itemData; - } - - private Map buildToolSchema(AppCategory appCategory, OperationContext context, AippDto aippDto, - FlowInfo flowInfo) { - return MapBuilder.get() - .put("name", aippDto.getName()) - .put("description", aippDto.getDescription()) - .put("parameters", this.buildParameters(aippDto, context, flowInfo, appCategory)) - .put("order", Arrays.asList("tenantId", "aippId", "version", "inputParams")) - .put("return", buildReturn()) - .put("manualIntervention", Objects.equals(appCategory, AppCategory.WATER_FLOW)) - .build(); - } - - private Map buildParameters(AippDto aippDto, OperationContext context, FlowInfo flowInfo, - AppCategory appCategory) { - Map parameterMap = new HashMap<>(); - parameterMap.put("type", "object"); - parameterMap.put("properties", this.buildPropertiesMap(aippDto, context, appCategory, flowInfo)); - parameterMap.put("required", Arrays.asList("tenantId", "aippId", "version", "inputParams")); - return parameterMap; - } - - private Map buildRunnables(AippDto aippDto) { - Map runnablesMap = new HashMap<>(); - runnablesMap.put("FIT", - MapBuilder.get() - .put("genericableId", WaterFlowService.GENERICABLE_WATER_FLOW_INVOKER) - .put("fitableId", "water.flow.invoke") - .build()); - Map app = MapBuilder.get() - .put("appId", aippDto.getAppId()) - .put("aippId", aippDto.getId()) - .put("version", aippDto.getVersion()) - .put("appCategory", aippDto.getAppCategory()) - .build(); - runnablesMap.put("APP", app); - return runnablesMap; - } - - private Map buildPropertiesMap(AippDto aippDto, OperationContext context, AppCategory appCategory, - FlowInfo flowInfo) { - Map propertiesMap = new HashMap<>(); - propertiesMap.put("tenantId", - MapBuilder.get() - .put("type", "string") - .put("description", "the tenant id of the waterFlow tool") - .put("default", context.getTenantId()) - .build()); - propertiesMap.put("aippId", - MapBuilder.get() - .put("type", "string") - .put("description", "the aipp id of the waterFlow tool") - .put("default", aippDto.getId()) - .build()); - propertiesMap.put("version", - MapBuilder.get() - .put("type", "string") - .put("description", "the aipp version of the waterFlow tool") - .put("default", aippDto.getVersion()) - .build()); - propertiesMap.put("inputParams", this.buildInputParamsSchema(flowInfo)); - return propertiesMap; - } - - private Map buildInputParamsSchema(FlowInfo flowInfo) { - Map propertiesMapOfInputParam = new HashMap<>(); - List required = new ArrayList<>(); - List order = new ArrayList<>(); - flowInfo.getInputParamsByName("input").forEach(inputParam -> { - String name = inputParam.getOrDefault("name", StringUtils.EMPTY).toString(); - String type = inputParam.getOrDefault("type", StringUtils.EMPTY).toString(); - String description = inputParam.getOrDefault("description", StringUtils.EMPTY).toString(); - propertiesMapOfInputParam.put(name, - MapBuilder.get().put("type", type).put("description", description).build()); - if (ObjectUtils.cast(inputParam.getOrDefault("isRequired", false))) { - required.add(name); - } - order.add(name); - }); - return MapBuilder.get() - .put("type", "object") - .put("properties", propertiesMapOfInputParam) - .put("required", required) - .put("order", order) - .build(); - } - - private Map buildReturn() { - // 返参的具体属性信息暂不填充,需要考虑多end节点的情况 - return MapBuilder.get().put("type", "object").put("properties", new HashMap<>()).build(); - } - - private String buildAppTypeTag(AippDto aippDto) { - return APP_TYPE_TAG_PREFIX + StringUtils.toUpperCase(aippDto.getAppType()); - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogServiceImpl.java index 8be48baa6a..0a4538b4a1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogServiceImpl.java @@ -7,14 +7,15 @@ package modelengine.fit.jober.aipp.service.impl; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.multiversion.instance.Instance; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; import modelengine.fit.jober.aipp.dto.aipplog.AippLogQueryCondition; @@ -31,12 +32,13 @@ import modelengine.fit.jober.aipp.util.AippLogUtils; import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; import modelengine.fit.jober.common.RangedResultSet; + +import lombok.AllArgsConstructor; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.CollectionUtils; +import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; @@ -63,31 +65,17 @@ * @since 2024-01-08 */ @Component +@AllArgsConstructor public class AippLogServiceImpl implements AippLogService { private static final Logger log = Logger.get(AippLogServiceImpl.class); private final AippLogMapper aippLogMapper; - private final AippChatMapper aippChatMapper; - - private final MetaInstanceService metaInstanceService; - private final UploadedFileManageService uploadedFileManageService; - - private final MetaService metaService; - private final AopAippLogService aopAippLogService; - - public AippLogServiceImpl(AippLogMapper aippLogMapper, MetaInstanceService metaInstanceService, - UploadedFileManageService uploadedFileManageService, MetaService metaService, AippChatMapper aippChatMapper, - AopAippLogService aopAippLogService) { - this.aippLogMapper = aippLogMapper; - this.aippChatMapper = aippChatMapper; - this.metaInstanceService = metaInstanceService; - this.uploadedFileManageService = uploadedFileManageService; - this.metaService = metaService; - this.aopAippLogService = aopAippLogService; - } + private final AppTaskInstanceService appTaskInstanceService; + private final AppTaskService appTaskService; + private final AippLogRepository aippLogRepository; private AippInstLog completeFormDataJson(AippInstLog instanceLog, OperationContext context) { if (AippInstLogType.FORM.name().equals(instanceLog.getLogType())) { @@ -95,12 +83,10 @@ private AippInstLog completeFormDataJson(AippInstLog instanceLog, OperationConte if (form == null) { return instanceLog; } - Map newLogData = new HashMap() { - { - put("formData", form.getFormData()); - put("formAppearance", form.getFormAppearance()); - } - }; + Map newLogData = MapBuilder.get() + .put("formData", form.getFormData()) + .put("formAppearance", form.getFormAppearance()) + .build(); instanceLog.setLogData(JsonUtils.toJsonString(newLogData)); } return instanceLog; @@ -125,34 +111,29 @@ public List queryAippRecentInstLog(String appId, String type // 只对最后一个记录查询状态 if (!recentLogData.isEmpty()) { AippInstLogDataDto lastLogData = recentLogData.get(recentLogData.size() - 1); - Meta meta = MetaUtils.getAnyMeta(metaService, lastLogData.getAippId(), lastLogData.getVersion(), context); - if (meta == null) { + String appSuiteId = lastLogData.getAippId(); + String version = lastLogData.getVersion(); + Optional taskOp = this.appTaskService.getLatest(appSuiteId, version, context); + if (taskOp.isEmpty()) { return Collections.emptyList(); } - String versionId = meta.getVersionId(); - RangedResultSet instances = MetaInstanceUtils.getOneInstance(versionId, - lastLogData.getInstanceId(), - context, - metaInstanceService); - if (instances.getRange().getTotal() == 0) { + String versionId = taskOp.get().getEntity().getTaskId(); + Optional instanceOp = this.appTaskInstanceService.getInstance(versionId, + lastLogData.getInstanceId(), context); + if (instanceOp.isEmpty()) { return Collections.emptyList(); } - String lastLogStatus = instances.getResults() - .get(0) - .getInfo() - .getOrDefault(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.RUNNING.name()); - lastLogData.setStatus(lastLogStatus); + lastLogData.setStatus(instanceOp.get().getEntity().getStatus().orElse(MetaInstStatusEnum.RUNNING.name())); } return recentLogData; } private List getMetaIds(String appId, OperationContext context, String aippType) { - return MetaUtils.getAllMetasByAppId(this.metaService, appId, aippType, context) + return this.appTaskService.getTasksByAppId(appId, aippType, context) .stream() .filter(Objects::nonNull) - .map(Meta::getId) - .distinct() - .collect(Collectors.toList()); + .map(t -> t.getEntity().getAppSuiteId()) + .toList(); } private List queryAippRecentInstLog(List aippIds, String aippType, Integer count, @@ -162,20 +143,6 @@ private List queryAippRecentInstLog(List aippIds, St return this.queryAndSortLogs(instanceIds, context); } - @Override - public List queryAippRecentInstLog(String aippId, String aippType, Integer count, - OperationContext context) { - List instanceIds = aippLogMapper.selectRecentInstanceId(aippId, aippType, count, context.getAccount()); - return this.queryAndSortLogs(instanceIds, context); - } - - @Override - public List queryChatRecentInstLog(String aippId, String aippType, Integer count, - OperationContext context, String chatId) { - List instanceIds = aippChatMapper.selectFormerInstanceByChat(chatId, count); - return this.queryAndSortLogs(instanceIds, context); - } - @Override public List queryAppRecentChatLog(String appId, String aippType, OperationContext context) { List chatIds = aippChatMapper.selectChatByAppId(appId, aippType, 1); @@ -188,12 +155,12 @@ public List queryAppRecentChatLog(String appId, String aippT private List getAippLogWithAppInfo(List logData, String appId, OperationContext context) { // 获取被@应用的头像、名称 - List originAippId = MetaUtils.getAllMetasByAppId(this.metaService, appId, context) + List originAippId = this.appTaskService.getTasksByAppId(appId, context) .stream() .filter(Objects::nonNull) - .map(Meta::getId) + .map(t -> t.getEntity().getAppSuiteId()) .distinct() - .collect(Collectors.toList()); + .toList(); List atAippIds = logData.stream() .map(AippInstLogDataDto::getAippId) .filter(aippId -> !originAippId.contains(aippId)) @@ -201,49 +168,42 @@ private List getAippLogWithAppInfo(List if (CollectionUtils.isEmpty(atAippIds)) { return logData; } - RangedResultSet metas = metaService.list(this.buildAippIdFilter(atAippIds), true, 0, atAippIds.size(), - context); - if (CollectionUtils.isEmpty(metas.getResults())) { + RangedResultSet resultSet = this.appTaskService.getTasks( + AppTask.asQueryEntity(0, atAippIds.size()).latest().addAppSuiteIds(atAippIds).build(), context); + if (resultSet.isEmpty()) { return logData; } - Map metaMap = - metas.getResults().stream().collect(Collectors.toMap(Meta::getId, Function.identity())); - return logData.stream() - .peek(aippInstLogDataDto -> setLogDataWithIcon(aippInstLogDataDto, metaMap)) - .collect(Collectors.toList()); - } - - private void setLogDataWithIcon(AippInstLogDataDto aippInstLogDataDto, Map metaMap) { - if (!metaMap.containsKey(aippInstLogDataDto.getAippId())) { - return; - } - Meta metaData = metaMap.get(aippInstLogDataDto.getAippId()); - Object metaIcon = metaData.getAttributes().get("meta_icon"); - if (metaIcon instanceof String) { - aippInstLogDataDto.setAppIcon((String) metaIcon); - } - aippInstLogDataDto.setAppName(metaData.getName()); - } - - private MetaFilter buildAippIdFilter(List aippIds) { - MetaFilter filter = new MetaFilter(); - filter.setMetaIds(aippIds); - return filter; + Map taskMap = resultSet.getResults() + .stream() + .collect(Collectors.toMap(t -> t.getEntity().getAppSuiteId(), Function.identity())); + return logData.stream().peek(l -> { + if (!taskMap.containsKey(l.getAippId())) { + return; + } + AppTask task = taskMap.get(l.getAippId()); + l.setAppName(task.getEntity().getName()); + l.setAppIcon(task.getEntity().getIcon()); + }).collect(Collectors.toList()); } private List queryAndSortLogs(List instanceIds, OperationContext context) { - return queryRecentLogByInstanceIds(instanceIds, context).values() - .stream() - .filter(CollectionUtils::isNotEmpty) - .map((List rawLogs) -> AippInstLogDataDto.fromAippInstLogList(rawLogs, - context, - this.metaInstanceService)) + return instanceIds.stream() + .map(id -> { + String metaVersionId = appTaskInstanceService.getTaskId(id); + return this.appTaskInstanceService.getInstance(metaVersionId, id, context); + }) + .filter(Optional::isPresent) + .map(Optional::get) + .map(AppTaskInstance::toLogDataDto) + .filter(Optional::isPresent) + .map(Optional::get) .sorted(Comparator.comparing(AippInstLogDataDto::getCreateAt)) - .collect(Collectors.toList()); + .toList(); } @Override - public List queryChatRecentChatLog(String chatId, String appId, OperationContext context) { + public List queryChatRecentChatLog(String chatId, String appId, + OperationContext context) { List instanceIds = aippChatMapper.selectInstanceByChat(chatId, 10); List logData = queryAndSortLogs(instanceIds, context); return this.getAippLogWithAppInfo(logData, appId, context); @@ -254,14 +214,7 @@ public List queryRecentLogsSinceResume(String aippId, String OperationContext context) { List instanceIds = aippLogMapper.selectRecentAfterResume(aippId, aippType, context.getAccount()); // 该功能未上线,待测试 - return queryRecentLogByInstanceIds(instanceIds, context).values() - .stream() - .filter(CollectionUtils::isNotEmpty) - .map((List rawLogs) -> AippInstLogDataDto.fromAippInstLogList(rawLogs, - context, - this.metaInstanceService)) - .sorted(Comparator.comparing(AippInstLogDataDto::getCreateAt)) - .collect(Collectors.toList()); + return this.queryAndSortLogs(instanceIds, context); } private Map> queryRecentLogByInstanceIds(List instanceIds, @@ -306,8 +259,10 @@ private LocalDateTime getLocalDateTime(String timeString) { @Override public List queryInstanceLogSince(String instanceId, String timeString) { LocalDateTime sinceTime = Optional.ofNullable(timeString).map(this::getLocalDateTime).orElse(null); - AippLogQueryCondition sqlCondition = - AippLogQueryCondition.builder().instanceId(instanceId).afterAt(sinceTime).build(); + AippLogQueryCondition sqlCondition = AippLogQueryCondition.builder() + .instanceId(instanceId) + .afterAt(sinceTime) + .build(); return aippLogMapper.selectWithCondition(sqlCondition) .stream() .filter(AippLogServiceImpl::isNeededLog) @@ -380,35 +335,22 @@ public void deleteAippInstLog(String appId, String type, OperationContext contex if (!instanceIdList.isEmpty()) { // check最后的实例是不是还在运行 String instanceId = instanceIdList.get(0); - String versionId = this.metaInstanceService.getMetaVersionId(instanceId); - RangedResultSet instances = - MetaInstanceUtils.getOneInstance(versionId, instanceId, context, this.metaInstanceService); - if (instances.getRange().getTotal() == 0) { - return; - } - String lastLogStatus = instances.getResults() - .get(0) - .getInfo() - .getOrDefault(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.RUNNING.name()); - String instanceIdExclude = null; - if (lastLogStatus.equals(MetaInstStatusEnum.RUNNING.name())) { - instanceIdExclude = instanceIdList.get(0); - } else { - this.uploadedFileManageService.cleanAippFiles(metaIds); - } - this.aippLogMapper.delete(metaIds, aippType, context.getAccount(), instanceIdExclude); + String taskId = this.appTaskInstanceService.getTaskId(instanceId); + this.appTaskInstanceService.getInstance(taskId, instanceId, context).ifPresent(appTaskInstance -> { + String instanceIdExclude = null; + if (appTaskInstance.isRunning()) { + instanceIdExclude = instanceId; + } else { + this.uploadedFileManageService.cleanAippFiles(metaIds); + } + this.aippLogMapper.delete(metaIds, aippType, context.getAccount(), instanceIdExclude); + }); } } - /** - * 删除指定aipp预览的历史记录 - * - * @param previewAippId 指定aipp的id - * @param context 登录信息 - */ @Override public void deleteAippPreviewLog(String previewAippId, OperationContext context) { - this.aippLogMapper.deleteByType(previewAippId, AippTypeEnum.PREVIEW.name(), context.getAccount(), null); + this.aippLogRepository.deleteAippPreviewLog(previewAippId, context); } /** @@ -421,18 +363,19 @@ public void deleteAippPreviewLog(String previewAippId, OperationContext context) */ @Override public String insertLog(String logType, AippLogData logData, Map businessData) { - String aippId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_ID_KEY)); - String instId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_INST_ID_KEY)); - String parentInstId = ObjectUtils.cast(businessData.get(AippConst.PARENT_INSTANCE_ID)); - String version = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_VERSION_KEY)); - String aippType = ObjectUtils.cast(businessData.get(AippConst.ATTR_AIPP_TYPE_KEY)); + RunContext runContext = new RunContext(businessData, new OperationContext()); + String aippId = runContext.getAppSuiteId(); + String instId = runContext.getTaskInstanceId(); + String parentInstId = runContext.getParentInstanceId(); + String version = runContext.getAppVersion(); + String aippType = runContext.getAippType(); String account = DataUtils.getOpContext(businessData).getAccount(); if (!AippLogUtils.validFormMsg(logData, logType)) { return null; } - String path = buildPath(instId, parentInstId); - String chatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String atChatId = ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID)); + String path = this.buildPath(instId, parentInstId); + String chatId = runContext.getOriginChatId(); + String atChatId = runContext.getAtChatId(); return this.aopAippLogService.insertLog(AippLogCreateDto.builder() .aippId(aippId) .version(version) @@ -447,7 +390,6 @@ public String insertLog(String logType, AippLogData logData, Map .isEnableLog(this.isEnableLog(businessData)) .build()); } - private Boolean isEnableLog(Map businessData) { // 兼容老数据,老数据没有这个开关的时候(enableLog为null)默认返回true。 // 有开关后(enableLog为null),返回enableLog的值 @@ -455,18 +397,6 @@ private Boolean isEnableLog(Map businessData) { || ObjectUtils.cast(businessData.get(AippConst.BS_LLM_ENABLE_LOG)); } - /** - * 插入MSG类型的历史记录 - * - * @param msg MSG日志内容 - * @param flowData 流程执行上下文数据。 - */ - @Override - public void insertMsgLog(String msg, List> flowData) { - AippLogData logData = AippLogData.builder().msg(msg).build(); - insertLog(AippInstLogType.MSG.name(), logData, DataUtils.getBusiness(flowData)); - } - /** * 插入ERROR类型的历史记录 * @@ -510,7 +440,7 @@ public void updateLog(Long logId, String newLogType, String newLogData) throws I log.error("logId is null"); throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); } - this.aippLogMapper.updateDataAndType(logId, newLogType, newLogData); + this.aippLogRepository.updateDataAndType(logId, newLogType, newLogData); } @Override @@ -518,23 +448,16 @@ public String getParentPath(String parentInstId) { if (parentInstId == null) { return ""; } - return aippLogMapper.getParentPath(parentInstId); + return this.aippLogRepository.getParentPath(parentInstId); } - /** - * 根据父Instance的路径构建当前Instance的路径。 - * - * @param instId 表示当前instance的id的 {@link String}。 - * @param parentInstId 表示父instance的id的 {@link String}。 - * @return 表示当前instId的路径的 {@link String}。 - */ @Override public String buildPath(String instId, String parentInstId) { String path; if (parentInstId == null) { path = AippLogUtils.PATH_DELIMITER + instId; } else { - String parentPath = getParentPath(parentInstId); + String parentPath = this.getParentPath(parentInstId); path = StringUtils.isEmpty(parentPath) ? AippLogUtils.PATH_DELIMITER + instId : String.join(AippLogUtils.PATH_DELIMITER, parentPath, instId); @@ -542,28 +465,20 @@ public String buildPath(String instId, String parentInstId) { return path; } - @Override - public void deleteInstanceLog(String instanceId) { - if (StringUtils.isEmpty(instanceId)) { - log.error("Instance id is null or empty."); - throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); - } - this.aippLogMapper.deleteInstanceLog(instanceId); - } - @Override public List queryAippRecentInstLogAfterSplice(String aippId, String aippType, Integer count, - OperationContext context) { - List instanceIds = aippLogMapper.selectRecentInstanceId(aippId, aippType, count, context.getAccount()); + OperationContext context) { + List instanceIds = + aippLogMapper.selectRecentInstanceId(aippId, aippType, count, context.getAccount()); // 该功能未上线,待测试 return queryRecentLogByInstanceIds(instanceIds, context).values() - .stream() - .filter(CollectionUtils::isNotEmpty) + .stream() + .filter(CollectionUtils::isNotEmpty) .map((List rawLogs) -> AippInstLogDataDto.fromAippInstLogListAfterSplice(rawLogs, - this.metaInstanceService, + this.appTaskInstanceService, context)) - .sorted(Comparator.comparing(AippInstLogDataDto::getCreateAt)) - .collect(Collectors.toList()); + .sorted(Comparator.comparing(AippInstLogDataDto::getCreateAt)) + .collect(Collectors.toList()); } @Override @@ -579,27 +494,6 @@ public List queryBatchAndFilterFullLogsByLogType(List insta .collect(Collectors.toList()); } - @Override - public List queryAndFilterLogsByLogType(String instanceId, List filterLogTypes) { - if (StringUtils.isEmpty(instanceId)) { - log.error("Instance id is null or empty."); - throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); - } - return this.aippLogMapper.getLogsByInstanceId(instanceId) - .stream() - .filter(log -> !filterLogTypes.contains(log.getLogType())) - .collect(Collectors.toList()); - } - - @Override - public List queryLogsByInstanceIdAndLogTypes(String instanceId, List logTypes) { - if (StringUtils.isEmpty(instanceId)) { - log.error("When queryLogsByInstanceIdAndLogTypes input instance id is empty."); - throw new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID); - } - return this.aippLogMapper.getLogsByInstanceIdAndLogTypes(instanceId, logTypes); - } - @Override public void deleteLogs(List logIds) { if (CollectionUtils.isEmpty(logIds)) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogStreamServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogStreamServiceImpl.java index 90c553c15e..ab5a3ff912 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogStreamServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippLogStreamServiceImpl.java @@ -6,19 +6,19 @@ package modelengine.fit.jober.aipp.service.impl; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.enums.StreamMsgType; import modelengine.fit.jober.aipp.service.AippLogStreamService; import modelengine.fit.jober.aipp.service.AppChatSseService; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; import modelengine.fit.jober.aipp.util.SensitiveFilterTools; import modelengine.fit.jober.aipp.vo.AippLogVO; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; + import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.ObjectUtils; @@ -39,22 +39,19 @@ @Component public class AippLogStreamServiceImpl implements AippLogStreamService { private static final List OUTPUT_WITH_MSG_WHITE_LIST = Arrays.asList(AippInstLogType.MSG.name(), - AippInstLogType.ERROR.name(), AippInstLogType.META_MSG.name(), StreamMsgType.KNOWLEDGE.value()); - - private final MetaService metaService; - - private final MetaInstanceService metaInstanceService; + AippInstLogType.ERROR.name(), + AippInstLogType.META_MSG.name(), + StreamMsgType.KNOWLEDGE.value()); private final AppChatSseService appChatSseService; - private final SensitiveFilterTools sensitiveFilterTools; + private final AppTaskInstanceService appTaskInstanceService; - public AippLogStreamServiceImpl(MetaService metaService, MetaInstanceService metaInstanceService, - AppChatSseService appChatSseService, SensitiveFilterTools sensitiveFilterTools) { - this.metaService = metaService; - this.metaInstanceService = metaInstanceService; + public AippLogStreamServiceImpl(AppChatSseService appChatSseService, + SensitiveFilterTools sensitiveFilterTools, AppTaskInstanceService appTaskInstanceService) { this.appChatSseService = appChatSseService; this.sensitiveFilterTools = sensitiveFilterTools; + this.appTaskInstanceService = appTaskInstanceService; } @Override @@ -73,13 +70,15 @@ public void send(AippLogVO log) { private AppChatRsp buildData(AippLogVO log) { String instanceId = log.getInstanceId(); - Instance instance = MetaInstanceUtils.getInstanceDetailByInstanceId(instanceId, null, metaInstanceService); + AppTaskInstance instance = this.appTaskInstanceService.getInstanceById(instanceId, null) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); // 在当前某些情况下,会出现插入log日志,但是不修改instance状态的情况. // 参考modelengine.fit.jober.aipp.fitable.agent.AippFlowAgent.fetchAgentErrorMsgToMain String status = log.getLogType().equals(AippInstLogType.ERROR.name()) ? FlowTraceStatus.ERROR.name() - : instance.getInfo().get(AippConst.INST_STATUS_KEY); + : instance.getEntity().getStatus().orElse(null); AppChatRsp.Answer answer = this.buildAnswer(log); Map extensionMap = new HashMap<>(); @@ -96,9 +95,8 @@ private AppChatRsp buildData(AippLogVO log) { } private AppChatRsp.Answer buildAnswer(AippLogVO log) { - AppChatRsp.Answer.AnswerBuilder builder = AppChatRsp.Answer.builder() - .type(log.getLogType()) - .msgId(log.getMsgId()); + AppChatRsp.Answer.AnswerBuilder builder = + AppChatRsp.Answer.builder().type(log.getLogType()).msgId(log.getMsgId()); if (OUTPUT_WITH_MSG_WHITE_LIST.contains(StringUtils.toUpperCase(log.getLogType()))) { Object msg = JsonUtils.parseObject(log.getLogData()).get("msg"); if (msg instanceof String) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippModelServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippModelServiceImpl.java index f37ffc61cf..15d004c5ae 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippModelServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippModelServiceImpl.java @@ -26,9 +26,9 @@ import modelengine.fitframework.annotation.Component; import modelengine.fitframework.exception.ClientException; import modelengine.fitframework.flowable.Choir; +import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.StringUtils; -import modelengine.fitframework.log.Logger; import java.util.Map; @@ -43,15 +43,11 @@ public class AippModelServiceImpl implements AippModelService { private static final Logger log = Logger.get(AippModelServiceImpl.class); private static final String INPUT = "input"; - private static final String TEMPLATE_GROUP = "template"; - private static final String TEMPLATE_ATTRIBUTE = "template"; private final ChatModel modelService; - private final AippModelCenter aippModelCenter; - private final AippSystemConfigRepository aippSystemConfigRepository; public AippModelServiceImpl(ChatModel modelService, AippModelCenter aippModelCenter, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippRunTimeServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippRunTimeServiceImpl.java index 7ce79bdd1e..b7edab829b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippRunTimeServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippRunTimeServiceImpl.java @@ -7,94 +7,65 @@ package modelengine.fit.jober.aipp.service.impl; import static modelengine.fit.jober.aipp.common.exception.AippExceptionHandler.LOCALES; -import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_FLOW_DEF_ID_KEY; -import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INPUT_KEY; -import static modelengine.fitframework.util.ObjectUtils.cast; -import static modelengine.fitframework.util.ObjectUtils.nullIf; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.util.UsefulUtils.doIfNull; +import com.alibaba.druid.support.json.JSONUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import modelengine.fit.dynamicform.DynamicFormService; import modelengine.fit.dynamicform.entity.DynamicFormDetailEntity; import modelengine.fit.dynamicform.entity.FormMetaQueryParameter; -import modelengine.fit.http.client.HttpClassicClientFactory; import modelengine.fit.jade.waterflow.FlowInstanceService; -import modelengine.fit.jade.waterflow.FlowsService; -import modelengine.fit.jade.waterflow.dto.FlowInfo; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.common.enums.DirectionEnum; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; -import modelengine.fit.jane.meta.multiversion.instance.MetaInstanceFilter; -import modelengine.fit.jane.task.domain.type.DateTimeConverter; +import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippForbiddenException; import modelengine.fit.jober.aipp.condition.AippInstanceQueryCondition; +import modelengine.fit.jober.aipp.condition.PaginationCondition; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; -import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; -import modelengine.fit.jober.aipp.dto.AippInstanceCreateDto; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.TaskInstanceDecorator; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.AippInstanceDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppStartDto; -import modelengine.fit.jober.aipp.dto.AppInputParam; -import modelengine.fit.jober.aipp.dto.MemoryConfigDto; import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; -import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; -import modelengine.fit.jober.aipp.entity.AippInstLog; import modelengine.fit.jober.aipp.entity.AippLogData; import modelengine.fit.jober.aipp.entity.ChatSession; -import modelengine.fit.jober.aipp.entity.StartChatParam; import modelengine.fit.jober.aipp.enums.AippInstLogType; -import modelengine.fit.jober.aipp.enums.AippTypeEnum; -import modelengine.fit.jober.aipp.enums.MetaInstSortKeyEnum; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; import modelengine.fit.jober.aipp.enums.RestartModeEnum; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; -import modelengine.fit.jober.aipp.genericable.entity.AippCreate; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AippRunTimeService; import modelengine.fit.jober.aipp.service.AopAippLogService; import modelengine.fit.jober.aipp.service.AppChatSessionService; import modelengine.fit.jober.aipp.service.AppChatSseService; import modelengine.fit.jober.aipp.service.RuntimeInfoService; -import modelengine.fit.jober.aipp.util.AippLogUtils; -import modelengine.fit.jober.aipp.util.AppUtils; import modelengine.fit.jober.aipp.util.CacheUtils; -import modelengine.fit.jober.aipp.util.FlowUtils; -import modelengine.fit.jober.aipp.util.FormUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; import modelengine.fit.jober.aipp.vo.MetaVo; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.RangedResultSet; import modelengine.fit.jober.common.exceptions.JobberException; -import modelengine.fit.jober.entity.FlowInstanceResult; -import modelengine.fit.jober.entity.task.TaskProperty; -import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; -import modelengine.fit.waterflow.entity.FlowStartInfo; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; -import modelengine.fitframework.annotation.Value; -import modelengine.fitframework.broker.client.BrokerClient; -import modelengine.fitframework.broker.client.filter.route.FitableIdFilter; -import modelengine.fitframework.exception.FitException; import modelengine.fitframework.flowable.Choir; import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; -import modelengine.fitframework.model.Tuple; +import modelengine.fitframework.transaction.Transactional; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; import modelengine.jade.authentication.context.UserContextHolder; -import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.common.locale.LocaleUtil; -import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; @@ -107,8 +78,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -121,99 +90,39 @@ @Component public class AippRunTimeServiceImpl implements AippRunTimeService, modelengine.fit.jober.aipp.genericable.AippRunTimeService { - private static final String DEFAULT_QUESTION = "请解析以下文件。"; - private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ROOT); - private static final Logger log = Logger.get(AippRunTimeServiceImpl.class); - - private static final String DOWNLOAD_FILE_ORIGIN = "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?"; - - private static final String NAME_KEY = "name"; - - private static final String VALUE_KEY = "value"; - - private static final String TYPE_KEY = "type"; - private static final String PARENT_CALLBACK_ID = "modelengine.fit.jober.aipp.fitable.LLMComponentCallback"; + private static final List MEMORY_MSG_TYPE_WHITE_LIST = Arrays.asList(AippInstLogType.MSG.name(), + AippInstLogType.FORM.name(), + AippInstLogType.META_MSG.name()); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ROOT); - private static final String UI_WORD_KEY = "aipp.service.impl.AippRunTimeServiceImpl"; - - private static final List MEMORY_MSG_TYPE_WHITE_LIST = - Arrays.asList(AippInstLogType.MSG.name(), AippInstLogType.FORM.name(), AippInstLogType.META_MSG.name()); - - private final MetaService metaService; - - private final MetaInstanceService metaInstanceService; - + private final DynamicFormService dynamicFormService; private final FlowInstanceService flowInstanceService; - private final AippLogService aippLogService; - - private final BrokerClient client; - - private final FlowsService flowsService; - - private final String appEngineUrl; - private final AopAippLogService aopAippLogService; - - private final AppChatSseService appChatSSEService; - - private final AippLogService logService; - private final AppChatSessionService appChatSessionService; - - private final HttpClassicClientFactory httpClientFactory; - - private final AppBuilderAppFactory appFactory; - - private final LocaleService localeService; - private final RuntimeInfoService runtimeInfoService; - - public AippRunTimeServiceImpl(@Fit MetaService metaService, @Fit MetaInstanceService metaInstanceService, - @Fit FlowInstanceService flowInstanceService, @Fit AippLogService aippLogService, BrokerClient client, - @Fit FlowsService flowsService, @Value("${app-engine.endpoint}") String appEngineUrl, - @Fit AopAippLogService aopAippLogService, @Fit AppChatSseService appChatSSEService, - @Fit AippLogService logService, AppChatSessionService appChatSessionService, - @Fit HttpClassicClientFactory httpClientFactory, @Fit AppBuilderAppFactory appFactory, - @Fit LocaleService localeService, @Fit RuntimeInfoService runtimeInfoService) { - this.metaService = metaService; - this.metaInstanceService = metaInstanceService; + private final AppTaskInstanceService appTaskInstanceService; + private final AppTaskService appTaskService; + private final AppVersionService appVersionService; + private final AppChatSseService appChatSseService; + + public AippRunTimeServiceImpl(@Fit DynamicFormService dynamicFormService, + @Fit FlowInstanceService flowInstanceService, @Fit AippLogService aippLogService, + @Fit AopAippLogService aopAippLogService, AppChatSessionService appChatSessionService, + @Fit RuntimeInfoService runtimeInfoService, AppTaskInstanceService appTaskInstanceService, + AppTaskService appTaskService, AppVersionService appVersionService, AppChatSseService appChatSseService) { + this.dynamicFormService = dynamicFormService; this.flowInstanceService = flowInstanceService; this.aippLogService = aippLogService; - this.client = client; - this.flowsService = flowsService; - this.appEngineUrl = appEngineUrl; this.aopAippLogService = aopAippLogService; this.appChatSessionService = appChatSessionService; - this.httpClientFactory = httpClientFactory; - this.appChatSSEService = appChatSSEService; - this.logService = logService; - this.appFactory = appFactory; this.runtimeInfoService = runtimeInfoService; - this.localeService = localeService; - } - - private static void setExtraBusinessData(OperationContext context, Map businessData, Meta meta, - String instId) { - businessData.put(AippConst.BS_AIPP_ID_KEY, meta.getId()); - businessData.put(AippConst.BS_AIPP_VERSION_KEY, meta.getVersion()); - businessData.put(AippConst.BS_META_VERSION_ID_KEY, meta.getVersionId()); - businessData.put(AippConst.ATTR_AIPP_TYPE_KEY, - meta.getAttributes().getOrDefault(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.NORMAL.name())); - businessData.put(AippConst.BS_AIPP_INST_ID_KEY, instId); - businessData.put(AippConst.BS_HTTP_CONTEXT_KEY, JsonUtils.toJsonString(context)); - } - - private InstanceDeclarationInfo genMetaInstInitialInfo(Map businessData, String creator) { - return InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_NAME_KEY, businessData.getOrDefault(AippConst.INST_NAME_KEY, "无标题")) - .putInfo(AippConst.INST_CREATOR_KEY, creator) - .putInfo(AippConst.INST_CREATE_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.RUNNING.name()) - .putInfo(AippConst.INST_PROGRESS_KEY, "0") - .build(); + this.appTaskInstanceService = appTaskInstanceService; + this.appTaskService = appTaskService; + this.appVersionService = appVersionService; + this.appChatSseService = appChatSseService; } /** @@ -229,8 +138,17 @@ private InstanceDeclarationInfo genMetaInstInitialInfo(Map busin @Fitable("default") public String createAippInstance(String aippId, String version, Map initContext, OperationContext context) { - Meta meta = MetaUtils.getAnyMeta(metaService, aippId, version, context); - return createInstanceHandle(initContext, context, meta); + AppTask task = this.appTaskService.getLatest(aippId, version, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", aippId, version))); + + // 启动任务. + RunContext ctx = new RunContext(ObjectUtils.cast(initContext.get(AippConst.BS_INIT_CONTEXT_KEY)), context); + ctx.initStartParams(); + doIfNull(ctx.getRestartMode(), () -> ctx.setRestartMode(RestartModeEnum.OVERWRITE.getMode())); + ctx.setStartTime(LocalDateTime.now()); + task.run(ctx); + return ctx.getTaskInstanceId(); } /** @@ -246,184 +164,72 @@ public String createAippInstance(String aippId, String version, Map initContext, OperationContext context) { - Meta meta = CacheUtils.getMetaByAppId(metaService, appId, isDebug, context); - String metaInstId = this.createAippInstance(meta.getId(), meta.getVersion(), initContext, context); - return metaInstanceService.list(Collections.singletonList(metaInstId), 0, 1, context) - .getResults() - .get(0) - .getInfo() - .get("flow_trans_id"); + AppTask task = CacheUtils.getAppTaskByAppId(this.appVersionService, appId, isDebug, context); + + // 启动任务. + RunContext ctx = new RunContext(ObjectUtils.cast(initContext.get(AippConst.BS_INIT_CONTEXT_KEY)), context); + ctx.initStartParams(); + doIfNull(ctx.getRestartMode(), () -> ctx.setRestartMode(RestartModeEnum.OVERWRITE.getMode())); + ctx.setStartTime(LocalDateTime.now()); + task.run(ctx); + return ctx.getFlowTraceId(); + } + + @Override + public MetaVo queryLatestMetaVoByAppId(String appId, boolean isDebug, OperationContext context) { + AppTask task = CacheUtils.getAppTaskByAppId(this.appVersionService, appId, isDebug, context); + return MetaVo.builder().id(task.getEntity().getAppSuiteId()).version(task.getEntity().getVersion()).build(); } @Override @Fitable("default") public Boolean isInstanceRunning(String instanceId, OperationContext context) { - String versionId = this.metaInstanceService.getMetaVersionId(instanceId); - - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, metaInstanceService); - Map instInfo = instDetail.getInfo(); + Optional instanceOptional = appTaskInstanceService.getInstanceById(instanceId, context); + if (instanceOptional.isEmpty()) { + return false; + } + Map instInfo = instanceOptional.get().getEntity().getInfos(); if (!instInfo.containsKey(AippConst.INST_STATUS_KEY)) { return false; } - return MetaInstStatusEnum.getMetaInstStatus(instInfo.get(AippConst.INST_STATUS_KEY)) + return MetaInstStatusEnum.getMetaInstStatus(ObjectUtils.cast(instInfo.get(AippConst.INST_STATUS_KEY))) == MetaInstStatusEnum.RUNNING; } - /** - * 启动一个运行Aipp - * - * @param appId appId - * @param question 会话问题 - * @param businessData 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @param context 操作上下文 - * @param isDebug 是否为debug会话 - * @return 实例id - */ - @Override - public Tuple createInstanceByApp(String appId, String question, Map businessData, - OperationContext context, boolean isDebug) { - Meta meta = CacheUtils.getMetaByAppId(metaService, appId, isDebug, context); - return this.createInstanceHandle(question, businessData, meta, context, isDebug); - } - - @Override - public MetaVo queryLatestMetaVoByAppId(String appId, boolean isDebug, OperationContext context) { - Meta meta = CacheUtils.getMetaByAppId(metaService, appId, isDebug, context); - return MetaVo.builder().id(meta.getId()).version(meta.getVersion()).build(); - } - @Override - public Choir startFlowWithUserSelectMemory(String metaInstId, Map initContext, + public Choir startFlowWithUserSelectMemory(String taskInstanceId, Map initContext, OperationContext context, boolean isDebug) { - String versionId = this.metaInstanceService.getMetaVersionId(metaInstId); - Meta meta = this.metaService.retrieve(versionId, context); Map businessData = ObjectUtils.cast(initContext.get(AippConst.BS_INIT_CONTEXT_KEY)); - businessData.put("startNodeInputParams", JSONObject.parse(JSONObject.toJSONString(businessData))); - String flowDefinitionId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_FLOW_DEF_ID_KEY)); - setExtraBusinessData(context, businessData, meta, metaInstId); - String path = this.logService.getParentPath(metaInstId); - String parentInstanceId = - (StringUtils.isNotEmpty(path)) ? path.split(AippLogUtils.PATH_DELIMITER)[1] : metaInstId; - businessData.putIfAbsent(AippConst.PARENT_INSTANCE_ID, parentInstanceId); - businessData.putIfAbsent(AippConst.PARENT_CALLBACK_ID, PARENT_CALLBACK_ID); - businessData.putIfAbsent(AippConst.CONTEXT_USER_ID, context.getOperator()); - - Locale locale = getLocale(); - String appId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - + String taskId = this.appTaskInstanceService.getTaskId(taskInstanceId); + AppTaskInstance instance = this.appTaskInstanceService.getInstance(taskId, taskInstanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task[{0}] instance[{1}] not found.", taskId, taskInstanceId))); + + AppTask task = this.appTaskService.getTaskById(taskId, context) + .orElseThrow(() -> new AippException(AippErrCode.TASK_NOT_FOUND, taskId)); + Locale locale = this.getLocale(); return Choir.create(emitter -> { - try { - this.appChatSessionService.addSession(parentInstanceId, - new ChatSession<>(emitter, appId, isDebug, locale)); - this.startFlow(versionId, flowDefinitionId, metaInstId, businessData, context); - this.sendReadyStatus(metaInstId, businessData); - } catch (AippException e) { - this.updateInstanceStatusError(versionId, metaInstId, context); - // 更新日志类型为HIDDEN_FORM - aippLogService.insertLog(AippInstLogType.ERROR.name(), - AippLogData.builder().msg(e.getMessage()).build(), - businessData); - } + ChatSession chatSession = new ChatSession<>(emitter, task.getEntity().getAppId(), isDebug, locale); + this.appChatSessionService.addSession(instance.getParentInstanceId(), chatSession); + RunContext runContext = new RunContext(businessData, context); + runContext.initStartParams(); + runContext.setAppSuiteId(task.getEntity().getAppSuiteId()); + runContext.setAppVersion(task.getEntity().getVersion()); + runContext.setAippType(Optional.ofNullable(task.getEntity().getAippType()).orElse(NORMAL.name())); + runContext.setTaskId(task.getEntity().getTaskId()); + runContext.setTaskInstanceId(taskInstanceId); + runContext.setHttpContext(JSONUtils.toJSONString(context)); + runContext.setParentInstanceId(instance.getParentInstanceId()); + runContext.setCallbackId(PARENT_CALLBACK_ID); + runContext.setUserId(context.getOperator()); + runContext.setAppTask(task); + TaskInstanceDecorator.create(instance) + .chat(this.appChatSessionService, this.appChatSseService) + .exceptionLog(this.appTaskInstanceService, this.aippLogService) + .run(runContext, chatSession); }); } - /** - * 启动一个最新版本的Aipp - * - * @param aippId aippId - * @param initContext 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @param context 操作上下文 - * @return 实例响应 - */ - @Override - public AippInstanceCreateDto createAippInstanceLatest(String aippId, Map initContext, - OperationContext context) { - Meta meta = MetaUtils.getLastPublishedMeta(metaService, aippId, context); - String instId = createInstanceHandle(initContext, context, meta); - return AippInstanceCreateDto.builder() - .instanceId(instId) - .version(meta.getVersion()) - .versionId(meta.getVersionId()) - .build(); - } - - private String createInstanceHandle(Map initContext, OperationContext context, Meta meta) { - Map businessData = (Map) initContext.get(AippConst.BS_INIT_CONTEXT_KEY); - businessData.put("startNodeInputParams", - JSONObject.parse(JSONObject.toJSONString(initContext.get(AippConst.BS_INIT_CONTEXT_KEY)))); - String restartMode = ObjectUtils.cast(businessData.getOrDefault(AippConst.RESTART_MODE, - RestartModeEnum.OVERWRITE.getMode())); - businessData.put(AippConst.RESTART_MODE, restartMode); - // 记录启动时间 - businessData.put(AippConst.INSTANCE_START_TIME, LocalDateTime.now()); - - // 创建meta实例 - String metaVersionId = meta.getVersionId(); - Instance metaInst = this.metaInstanceService.createMetaInstance(metaVersionId, - genMetaInstInitialInfo(businessData, context.getOperator()), - context); - String metaInstId = metaInst.getId(); - setExtraBusinessData(context, businessData, meta, metaInstId); - - log.info("[perf] [{}] createInstanceHandle persistAippLog start, metaInstId={}", - System.currentTimeMillis(), - metaInstId); - // 持久化日志 - String flowDefinitionId = ObjectUtils.cast(meta.getAttributes().get(ATTR_FLOW_DEF_ID_KEY)); - this.persistAippLog(businessData, flowDefinitionId, context); - log.info("[perf] [{}] createInstanceHandle persistAippLog end, metaInstId={}", - System.currentTimeMillis(), - metaInstId); - - // 持久化aipp实例表单记录 - this.persistAippFormLog(meta, businessData); - log.info("[perf] [{}] createInstanceHandle persistAippFormLog end, metaInstId={}", - System.currentTimeMillis(), - metaInstId); - - // 记录上下文 - this.recordContext(context, meta, businessData, metaInst); - log.info("[perf] [{}] createInstanceHandle recordContext end, metaInstId={}", - System.currentTimeMillis(), - metaInstId); - - // 添加memory - this.startFlowWithMemoryOrNot(businessData, meta, context, metaInst); - log.info("[perf] [{}] createInstanceHandle startFlowWithMemoryOrNot end, metaInstId={}", - System.currentTimeMillis(), - metaInstId); - return metaInstId; - } - - private Tuple createInstanceHandle(String question, Map businessData, Meta meta, - OperationContext context, boolean isDebug) { - businessData.put("startNodeInputParams", JSONObject.parse(JSONObject.toJSONString(businessData))); - - businessData.put(AippConst.RESTART_MODE, - businessData.getOrDefault(AippConst.RESTART_MODE, RestartModeEnum.OVERWRITE.getMode())); - // 记录启动时间 - businessData.put(AippConst.INSTANCE_START_TIME, LocalDateTime.now()); - - // 创建meta实例 - Instance metaInst = this.metaInstanceService.createMetaInstance(meta.getVersionId(), - genMetaInstInitialInfo(businessData, context.getOperator()), - context); - AippRunTimeServiceImpl.setExtraBusinessData(context, businessData, meta, metaInst.getId()); - - // 持久化日志 - businessData.put(AippConst.BS_AIPP_QUESTION_KEY, question); - - Locale locale = getLocale(); - String flowDefinitionId = ObjectUtils.cast(meta.getAttributes().get(ATTR_FLOW_DEF_ID_KEY)); - List appChatInfo = AppUtils.getAndRemoveAppChatInfo(); - String appid = ObjectUtils.cast(appChatInfo.get(0)); - - return Tuple.duet(metaInst.getId(), Choir.create(emitter -> { - ChatSession chatEmitter = new ChatSession<>(emitter, appid, isDebug, locale); - startChat(new StartChatParam(businessData, meta, context, metaInst, flowDefinitionId, chatEmitter)); - })); - } - private Locale getLocale() { Locale locale = Locale.getDefault(); if (UserContextHolder.get() != null && StringUtils.isNotEmpty(UserContextHolder.get().getLanguage())) { @@ -433,295 +239,6 @@ private Locale getLocale() { return locale; } - private void startChat(StartChatParam param) { - try { - this.appChatSessionService.addSession(param.metaInst().getId(), param.chatSession()); - - log.info("[perf] [{}] startChat persistAippLog start, metaInstId={}", - System.currentTimeMillis(), - param.metaInst().getId()); - this.persistAippLog(param.businessData(), param.flowDefinitionId(), param.context()); - - log.info("[perf] [{}] startChat persistAippLog end, metaInstId={}", - System.currentTimeMillis(), - param.metaInst().getId()); - // 持久化aipp实例表单记录 - this.persistAippFormLog(param.meta(), param.businessData()); - log.info("[perf] [{}] startChat persistAippFormLog end, metaInstId={}", - System.currentTimeMillis(), - param.metaInst().getId()); - - // 记录上下文 - this.recordContext(param.context(), param.meta(), param.businessData(), param.metaInst()); - log.info("[perf] [{}] startChat recordContext end, metaInstId={}", - System.currentTimeMillis(), - param.metaInst().getId()); - - this.startFlowWithMemoryOrNot(param.businessData(), param.meta(), param.context(), param.metaInst()); - log.info("[perf] [{}] startChat startFlowWithMemoryOrNot end, metaInstId={}", - System.currentTimeMillis(), - param.metaInst().getId()); - } catch (Exception e) { - log.error("Error occurs when starting a chat:", e); - this.updateInstanceStatusError(param.meta().getVersionId(), param.metaInst().getId(), param.context()); - String msg = this.localeService.localize(UI_WORD_KEY); - aippLogService.insertLog(AippInstLogType.ERROR.name(), - AippLogData.builder().msg(msg).build(), - param.businessData()); - } - } - - private void startFlowWithMemoryOrNot(Map businessData, Meta meta, OperationContext context, - Instance metaInst) { - this.appChatSSEService.send(metaInst.getId(), - AppChatRsp.builder() - .instanceId(metaInst.getId()) - .status(FlowTraceStatus.READY.name()) - .atChatId(ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID))) - .chatId(ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID))) - .build()); - String flowDefinitionId = cast(meta.getAttributes().get(AippConst.ATTR_FLOW_DEF_ID_KEY)); - List> memoryConfigs = this.getMemoryConfigs(meta, flowDefinitionId, context); - boolean isMemorySwitch = this.getMemorySwitch(memoryConfigs, businessData); - String memoryType = this.getMemoryType(memoryConfigs); - if (!isMemorySwitch && !StringUtils.equalsIgnoreCase("UserSelect", memoryType)) { - businessData.put(AippConst.BS_AIPP_MEMORIES_KEY, new ArrayList<>()); - } - if (!isMemorySwitch) { - this.startFlow(meta.getVersionId(), flowDefinitionId, metaInst.getId(), businessData, context); - this.sendReadyStatus(metaInst.getId(), businessData); - return; - } - if (!StringUtils.equalsIgnoreCase("UserSelect", memoryType)) { - String memoryChatId = ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID)); - String aippType = ObjectUtils.cast(meta.getAttributes() - .getOrDefault(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.NORMAL.name())); - businessData.put(AippConst.BS_AIPP_MEMORIES_KEY, - this.getMemories(meta.getId(), - memoryType, - memoryChatId, - memoryConfigs, - aippType, - businessData, - context)); - this.startFlow(meta.getVersionId(), flowDefinitionId, metaInst.getId(), businessData, context); - this.sendReadyStatus(metaInst.getId(), businessData); - } else { - MemoryConfigDto dto = this.buildMemoryConfigDto(businessData, metaInst.getId(), "UserSelect"); - this.appChatSSEService.sendToAncestorLastData(metaInst.getId(), dto); - } - } - - private void sendReadyStatus(String metaInstId, Map businessData) { - this.appChatSSEService.send(metaInstId, - AppChatRsp.builder() - .instanceId(metaInstId) - .status(FlowTraceStatus.READY.name()) - .atChatId(ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID))) - .chatId(ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID))) - .build()); - } - - private void recordContext(OperationContext context, Meta meta, Map businessData, - Instance metaInst) { - businessData.put(AippConst.CONTEXT_APP_ID, meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - businessData.put(AippConst.CONTEXT_INSTANCE_ID, metaInst.getId()); - businessData.put(AippConst.CONTEXT_USER_ID, context.getOperator()); - if (businessData.containsKey(AippConst.BS_AIPP_FILE_DESC_KEY)) { - List> fileDescription = - ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_FILE_DESC_KEY)); - List fileUrls = - fileDescription.stream().map(fileDesc -> fileDesc.get("file_url")).collect(Collectors.toList()); - businessData.put(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY, fileUrls); - businessData.put(AippConst.BS_AIPP_FILE_DOWNLOAD_KEY, fileUrls.get(0)); - } - } - - private void persistAippFormLog(Meta meta, Map businessData) { - String formId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_START_FORM_ID_KEY)); - String formVersion = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_START_FORM_VERSION_KEY)); - String appId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - AppBuilderApp app = this.appFactory.create(appId); - if (StringUtils.isNotEmpty(formId) && StringUtils.isNotEmpty(formVersion)) { - AippLogData logData = - FormUtils.buildLogDataWithFormData(app.getFormProperties(), formId, formVersion, businessData); - aippLogService.insertLog(AippInstLogType.FORM.name(), logData, businessData); - } - } - - private boolean getMemorySwitch(List> memoryConfig, Map businessData) { - if (memoryConfig == null || memoryConfig.isEmpty()) { - return false; - } - Map memorySwitchConfig = memoryConfig.stream() - .filter(config -> StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), - AippConst.MEMORY_SWITCH_KEY)) - .findFirst() - .orElse(null); - if (memorySwitchConfig == null) { - return false; - } - Boolean shouldUseMemory = ObjectUtils.cast(businessData.getOrDefault(AippConst.BS_AIPP_USE_MEMORY_KEY, - memorySwitchConfig.get(VALUE_KEY))); - businessData.put(AippConst.BS_AIPP_USE_MEMORY_KEY, shouldUseMemory); - return shouldUseMemory; - } - - private String getMemoryType(List> memoryConfigs) { - if (memoryConfigs == null || memoryConfigs.isEmpty()) { - return StringUtils.EMPTY; - } - Map typeConfig = memoryConfigs.stream() - .filter(config -> StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), TYPE_KEY)) - .findFirst() - .orElseThrow(() -> new AippException(AippErrCode.PARSE_MEMORY_CONFIG_FAILED)); - return ObjectUtils.cast(typeConfig.get(VALUE_KEY)); - } - - private List> getMemoryConfigs(Meta meta, String flowDefinitionId, OperationContext context) { - FlowInfo flowInfo; - try { - flowInfo = MetaUtils.isPublished(meta) ? CacheUtils.getPublishedFlowWithCache(this.flowsService, - flowDefinitionId, - context) : this.flowsService.getFlows(flowDefinitionId, context); - } catch (JobberException e) { - log.error("get flow failed, flowDefinitionId {}", flowDefinitionId); - throw new AippException(context, AippErrCode.OBTAIN_APP_ORCHESTRATION_INFO_FAILED); - } - return flowInfo.getInputParamsByName(AippConst.MEMORY_CONFIG_KEY); - } - - private MemoryConfigDto buildMemoryConfigDto(Map initContext, String instanceId, String memory) { - return MemoryConfigDto.builder().initContext(initContext).instanceId(instanceId).memory(memory).build(); - } - - private void startFlow(String metaVersionId, String flowDefinitionId, String metaInstId, - Map businessData, OperationContext context) { - FlowStartInfo parameter = new FlowStartInfo(context.getOperator(), null, businessData); - - FlowInstanceResult flowInst; - try { - flowInst = flowInstanceService.startFlow(flowDefinitionId, parameter, context); - } catch (JobberException e) { - log.error("start flow failed, flowDefinitionId: {}", flowDefinitionId); - throw new AippException(context, AippErrCode.APP_CHAT_WAIT_RESPONSE_ERROR); - } - - InstanceDeclarationInfo info = - InstanceDeclarationInfo.custom().putInfo(AippConst.INST_FLOW_INST_ID_KEY, flowInst.getId()).build(); - // 记录流程实例id到meta实例 - this.metaInstanceService.patchMetaInstance(metaVersionId, metaInstId, info, context); - } - - private void persistAippLog(Map businessData, String flowDefinitionId, OperationContext context) { - String question = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_QUESTION_KEY)); - List> fileDescList = businessData.containsKey(AippConst.BS_AIPP_FILE_DESC_KEY) - ? ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_FILE_DESC_KEY)) - : Collections.emptyList(); - // 持久化日志 - if (CollectionUtils.isEmpty(fileDescList)) { - if (this.isIncreamentMode(businessData)) { - // 如果是处于增长式的重新对话中,插入 hidden_question - aippLogService.insertLog(AippInstLogType.HIDDEN_QUESTION.name(), - AippLogData.builder().msg(question).build(), - businessData); - } else { - // 插入question日志 - Map infos = this.buildLogInfos(businessData, flowDefinitionId, context); - aippLogService.insertLog(AippInstLogType.QUESTION.name(), - AippLogData.builder().msg(question).infos(infos).build(), - businessData); - } - } else { - JSONObject msgJsonObj = new JSONObject(); - msgJsonObj.put("question", question); - msgJsonObj.put("files", fileDescList); - aippLogService.insertLog(AippInstLogType.QUESTION_WITH_FILE.name(), - AippLogData.builder().msg(msgJsonObj.toJSONString()).build(), - businessData); - businessData.put(AippConst.BS_AIPP_QUESTION_KEY, question); - } - } - - private Map buildLogInfos(Map businessData, String flowDefinitionId, - OperationContext context) { - List names = FlowUtils.getAppInputParams(this.flowsService, flowDefinitionId, context) - .stream() - .map(AppInputParam::getName) - .collect(Collectors.toList()); - if (CollectionUtils.isEmpty(names)) { - return new HashMap<>(); - } - Map inputParams = new HashMap<>(); - businessData.entrySet() - .stream() - .filter(data -> names.contains(data.getKey())) - .forEach(data -> inputParams.put(data.getKey(), data.getValue())); - Map infos = new HashMap<>(); - infos.put(BUSINESS_INPUT_KEY, inputParams); - return infos; - } - - private boolean isIncreamentMode(Map businessData) { - return StringUtils.equals(RestartModeEnum.INCREMENT.getMode(), - ObjectUtils.cast(businessData.get(AippConst.RESTART_MODE))); - } - - private boolean isChildInstance(Map businessData) { - String parentInstanceId = ObjectUtils.cast(businessData.get(AippConst.PARENT_INSTANCE_ID)); - String parentCallbackId = ObjectUtils.cast(businessData.get(AippConst.PARENT_CALLBACK_ID)); - return StringUtils.isNotEmpty(parentInstanceId) && StringUtils.isNotEmpty(parentCallbackId); - } - - private boolean checkInstanceStatus(String aippId, Instance instDetail, Function handler) { - Map instInfo = instDetail.getInfo(); - if (!instInfo.containsKey(AippConst.INST_STATUS_KEY)) { - log.error("aipp {} inst{} status key not found.", aippId, instDetail.getId()); - return false; - } - String status = instInfo.get(AippConst.INST_STATUS_KEY); - return handler.apply(status); - } - - private List> getMemories(String aippId, String memoryType, String chatId, - List> memoryConfigs, String aippType, Map businessData, - OperationContext context) { - if (memoryConfigs == null || memoryConfigs.isEmpty()) { - return this.getConversationTurns(aippId, aippType, 5, context, chatId); - } - Map valueConfig = memoryConfigs.stream() - .filter(config -> StringUtils.equals(ObjectUtils.cast(config.get(NAME_KEY)), VALUE_KEY)) - .findFirst() - .orElseThrow(() -> new AippException(AippErrCode.PARSE_MEMORY_CONFIG_FAILED)); - switch (memoryType) { - case "ByConversationTurn": - Integer turnNum = Integer.parseInt(ObjectUtils.cast(valueConfig.get(VALUE_KEY))); - return getConversationTurns(aippId, aippType, turnNum, context, chatId); - case "NotUseMemory": - // 兼容旧应用 - businessData.put(AippConst.BS_AIPP_USE_MEMORY_KEY, false); - return new ArrayList<>(); - case "Customizing": - // 如何定义这个genericable接口,入参为一个map? - String fitableId = ObjectUtils.cast(valueConfig.get(VALUE_KEY)); - // 目前flow graph中并没有params的配置,暂时用一个空map - Map params = new HashMap<>(); - return getCustomizedLogs(fitableId, params, aippId, aippType, context); - default: - return getConversationTurns(aippId, aippType, 5, context, chatId); - } - } - - private List> getConversationTurns(String aippId, String aippType, Integer count, - OperationContext context, String chatId) { - List logs; - if (chatId != null) { - logs = aippLogService.queryChatRecentInstLog(aippId, aippType, count, context, chatId); - return getLogMaps(logs); - } - return new ArrayList<>(); - } - /** * 将日志数据转换为前端可展示的格式 * @@ -740,11 +257,11 @@ public static List> getLogMaps(List logs List answers = log.getInstanceLogBodies() .stream() .filter(item -> MEMORY_MSG_TYPE_WHITE_LIST.contains(StringUtils.toUpperCase(item.getLogType()))) - .collect(Collectors.toList()); + .toList(); List files = log.getInstanceLogBodies() .stream() .filter(l -> l.getLogType().equals(AippInstLogType.FILE.name())) - .collect(Collectors.toList()); + .toList(); if (!answers.isEmpty()) { AippInstLogDataDto.AippInstanceLogBody logBody = answers.get(answers.size() - 1); logMap.put("answer", getLogData(logBody.getLogData(), logBody.getLogType())); @@ -759,7 +276,7 @@ public static List> getLogMaps(List logs } private static String getLogData(String logData, String logType) { - Map logInfo = ObjectUtils.>cast(JSON.parse(logData)); + Map logInfo = ObjectUtils.cast(JSON.parse(logData)); if (!StringUtils.isEmpty(logInfo.get("form_args"))) { return logInfo.get("form_args"); } @@ -774,25 +291,6 @@ private static String getLogData(String logData, String logType) { return msg; } - private List> getCustomizedLogs(String fitableId, Map params, String aippId, - String aippType, OperationContext context) { - final String genericableId = "68dc66a6185cf64c801e55c97fc500e4"; - if (fitableId == null) { - log.warn("no fitable id in customized log selection."); - return Collections.emptyList(); - } - List> logs; - try { - logs = this.client.getRouter(genericableId) - .route(new FitableIdFilter(fitableId)) - .invoke(params, aippId, aippType, context); - } catch (FitException t) { - log.error("Error occurred when get history logs, error: {}", t.getMessage()); - throw new AippException(AippErrCode.GET_HISTORY_LOG_FAILED); - } - return logs; - } - /** * 删除应用实例 * @@ -803,45 +301,24 @@ private List> getCustomizedLogs(String fitableId, Map handler = status -> MetaInstStatusEnum.getMetaInstStatus(status).getValue() - > MetaInstStatusEnum.RUNNING.getValue(); - if (!checkInstanceStatus(aippId, instDetail, handler)) { + AppTask task = this.appTaskService.getLatest(aippId, version, context) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{}, version: {}.", aippId, version))); + String taskId = task.getEntity().getTaskId(); + AppTaskInstance instance = this.appTaskInstanceService.getInstance(taskId, instanceId, null) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + if (!instance.isRunning()) { log.error("aipp {} version {} inst{}, not allow terminate.", aippId, version, instanceId); throw new AippForbiddenException(context, AippErrCode.DELETE_INSTANCE_FORBIDDEN); } - metaInstanceService.deleteMetaInstance(versionId, instanceId, context); + this.appTaskInstanceService.delete(taskId, instanceId, context); } - private MetaInstanceFilter genInstFilter(AippInstanceQueryCondition cond) { - MetaInstanceFilter filter = new MetaInstanceFilter(); - - String sortEncode = String.format(Locale.ROOT, - "%s(info.%s)", - DirectionEnum.getDirection(nullIf(cond.getOrder(), DirectionEnum.DESCEND.name())).getValue(), - MetaInstSortKeyEnum.getInstSortKey(nullIf(cond.getSort(), MetaInstSortKeyEnum.START_TIME.name())) - .getKey()); - List orderBy = Collections.singletonList(sortEncode); - - filter.setOrderBy(orderBy); - Map> infos = new HashMap<>(); - if (StringUtils.isNotBlank(cond.getCreator())) { - infos.put(AippConst.INST_CREATOR_KEY, Collections.singletonList(cond.getCreator())); - } - if (StringUtils.isNotBlank(cond.getAippInstanceName())) { - infos.put(AippConst.INST_NAME_KEY, Collections.singletonList(cond.getAippInstanceName())); - } - filter.setInfos(infos); - return filter; - } - - private AippInstanceDto buildAippInstanceDtoFromEntityList(Instance instance, + private AippInstanceDto buildAippInstanceDtoFromEntityList(AppTaskInstance instance, List> entityMaps, OperationContext context) { - String formId = instance.getInfo().get(AippConst.INST_CURR_FORM_ID_KEY); - String version = instance.getInfo().get(AippConst.INST_CURR_FORM_VERSION_KEY); + String formId = instance.getEntity().getFormId(); + String version = instance.getEntity().getFormVersion(); DynamicFormDetailEntity entity = null; if (this.checkParameter(formId, version) && !entityMaps.isEmpty()) { entity = entityMaps.stream().filter(Objects::nonNull).filter(map -> { @@ -850,16 +327,15 @@ private AippInstanceDto buildAippInstanceDtoFromEntityList(Instance instance, version); }).map(Map::values).flatMap(Collection::stream).findFirst().orElse(null); } - Map info = instance.getInfo(); return AippInstanceDto.builder() .aippInstanceId(instance.getId()) .tenantId(context.getTenantId()) - .aippInstanceName(info.get(AippConst.INST_NAME_KEY)) - .status(info.get(AippConst.INST_STATUS_KEY)) + .aippInstanceName(instance.getEntity().getName()) + .status(instance.getEntity().getStatus().orElse(null)) .formMetadata(entity == null ? null : entity.getData()) - .formArgs(info) - .startTime(info.get(AippConst.INST_CREATE_TIME_KEY)) - .endTime(info.getOrDefault(AippConst.INST_FINISH_TIME_KEY, null)) + .formArgs(instance.getEntity().getStringInfos()) + .startTime(instance.getEntity().getCreateTime()) + .endTime(instance.getEntity().getFinishTime(null)) .aippInstanceLogs(null) .build(); } @@ -871,49 +347,6 @@ private boolean checkParameter(String formId, String version) { return StringUtils.isNotEmpty(version) && !Objects.equals(version, AippConst.INVALID_FORM_VERSION_ID); } - private Map buildInfo(List props, Map businessData) { - Map info = new HashMap<>(); - businessData.forEach((targetKey, targetValue) -> { - if (props.stream().anyMatch(item -> item.getName().equals(targetKey))) { - info.put(targetKey, targetValue); - } - }); - return info; - } - - private void updateAippLog(List formProperties, String aippId, String instanceId, - OperationContext context, Map businessData) { - Instance oldInstDetail = MetaInstanceUtils.getInstanceDetail(aippId, instanceId, context, metaInstanceService); - String formId = oldInstDetail.getInfo().get(AippConst.INST_CURR_FORM_ID_KEY); - String formVersion = oldInstDetail.getInfo().get(AippConst.INST_CURR_FORM_VERSION_KEY); - AippLogData newLogData = FormUtils.buildLogDataWithFormData(formProperties, formId, formVersion, businessData); - Long logId = this.getLodId(instanceId, context); - aippLogService.updateLog(logId, JsonUtils.toJsonString(newLogData)); - } - - private void updateAippInstance(String aippId, List props, String instanceId, - OperationContext context, Map businessData) { - this.updateAippInstance(aippId, props, instanceId, context, businessData, (info) -> {}); - } - - private void updateAippInstance(String aippId, List props, String instanceId, - OperationContext context, Map businessData, Consumer> afterInfoBuild) { - Map info = this.buildInfo(props, businessData); - afterInfoBuild.accept(info); - log.debug("build info {} businessData {}", info, businessData); - InstanceDeclarationInfo instanceDeclarationInfo = InstanceDeclarationInfo.custom().info(info).build(); - this.metaInstanceService.patchMetaInstance(aippId, instanceId, instanceDeclarationInfo, context); - } - - private Long getLodId(String instanceId, OperationContext context) { - AippInstLog oldAippInstLog = aippLogService.queryLastInstanceFormLog(instanceId); - if (oldAippInstLog == null) { - log.error("instanceId {} log is null", instanceId); - throw new AippException(context, AippErrCode.AIPP_INSTANCE_LOG_IS_NULL); - } - return oldAippInstLog.getLogId(); - } - /** * 更新表单数据,并恢复实例任务执行 * @@ -925,81 +358,22 @@ private Long getLodId(String instanceId, OperationContext context) { * @return 返回一个Choir对象,用于流式处理 */ @Override + @Transactional public Choir resumeAndUpdateAippInstance(String instanceId, Map formArgs, Long logId, OperationContext context, boolean isDebug) { - String metaVersionId = this.metaInstanceService.getMetaVersionId(instanceId); - Meta meta = this.metaService.retrieve(metaVersionId, context); - String versionId = meta.getVersionId(); - - // 更新表单数据 - Map businessData = ObjectUtils.cast(formArgs.get(AippConst.BS_DATA_KEY)); - setExtraBusinessData(context, businessData, meta, instanceId); - - // 获取旧的实例数据 - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, metaInstanceService); - // 获取人工节点开始时间戳 [记录人工节点时延] - String smartFormTimeStr = instDetail.getInfo().get(AippConst.INST_SMART_FORM_TIME_KEY); - LocalDateTime smartFormTime = StringUtils.isBlank(smartFormTimeStr) - ? LocalDateTime.now() - : LocalDateTime.parse(smartFormTimeStr, FORMATTER); - long resumeDuration = - Long.parseLong(StringUtils.blankIf(instDetail.getInfo().get(AippConst.INST_RESUME_DURATION_KEY), "0")); - Duration duration = Duration.between(smartFormTime, LocalDateTime.now()); - businessData.put(AippConst.INST_RESUME_DURATION_KEY, String.valueOf(resumeDuration + duration.toMillis())); - String createTime = instDetail.getInfo().get(AippConst.INST_CREATE_TIME_KEY); - if (StringUtils.isNotEmpty(createTime)) { - businessData.put(AippConst.INSTANCE_START_TIME, DateTimeConverter.INSTANCE.fromExternal(createTime)); - } - - // 持久化aipp实例表单记录 - String formId = instDetail.getInfo().get(AippConst.INST_CURR_FORM_ID_KEY); - String formVersion = instDetail.getInfo().get(AippConst.INST_CURR_FORM_VERSION_KEY); - String appId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - AppBuilderApp app = this.appFactory.create(appId); - AippLogData logData = - FormUtils.buildLogDataWithFormData(app.getFormProperties(), formId, formVersion, businessData); - - // 设置表单的渲染数据和填充数据 - logData.setFormAppearance(ObjectUtils.cast(formArgs.get(AippConst.FORM_APPEARANCE_KEY))); - logData.setFormData(ObjectUtils.cast(formArgs.get(AippConst.FORM_DATA_KEY))); - this.aippLogService.updateLog(logId, AippInstLogType.HIDDEN_FORM.name(), JsonUtils.toJsonString(logData)); - - // 更新实例并清空当前表单数据 - this.updateAippInstance(versionId, meta.getProperties(), instanceId, context, businessData, this::clearFormId); - - String flowTraceId = instDetail.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); - Validation.notNull(flowTraceId, "flowTraceId can not be null"); - String flowDefinitionId = ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_FLOW_DEF_ID_KEY)); - - Locale locale = getLocale(); + String taskId = this.appTaskInstanceService.getTaskId(instanceId); + AppTask appTask = this.appTaskService.getTaskById(taskId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task[{0}] not found.", taskId))); + Locale locale = LocaleUtil.getLocale(); return Choir.create(emitter -> { - try { - this.appChatSessionService.addSession(instanceId, new ChatSession<>(emitter, appId, isDebug, locale)); - this.flowInstanceService.resumeFlow(flowDefinitionId, flowTraceId, formArgs, context); - } catch (JobberException e) { - log.error("resume flow failed, flowDefinitionId:{}, flowTraceId:{}, formArgs:{}", - flowDefinitionId, - flowTraceId, - formArgs); - throw new AippException(context, AippErrCode.RESUME_CHAT_FAILED); - } catch (AippException e) { - this.updateInstanceStatusError(versionId, instanceId, context); - aippLogService.insertLog(AippInstLogType.ERROR.name(), - AippLogData.builder().msg(e.getMessage()).build(), - businessData); - } + this.appChatSessionService.addSession(instanceId, + new ChatSession<>(emitter, appTask.getEntity().getAppId(), isDebug, locale)); + appTask.resume(instanceId, logId, formArgs, context); }); } - private void updateInstanceStatusError(String versionId, String instanceId, OperationContext context) { - InstanceDeclarationInfo declarationInfo = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.ERROR.name()) - .build(); - this.metaInstanceService.patchMetaInstance(versionId, instanceId, declarationInfo, context); - } - @Override public String terminateInstance(String instanceId, Map msgArgs, Long logId, OperationContext context) { @@ -1013,11 +387,6 @@ public String terminateInstance(String instanceId, Map msgArgs, return message; } - private void clearFormId(Map info) { - info.put(AippConst.INST_CURR_FORM_ID_KEY, AippConst.INVALID_FORM_ID); - info.put(AippConst.INST_CURR_FORM_VERSION_KEY, AippConst.INVALID_FORM_VERSION_ID); - } - /** * 终止aipp实例 * @@ -1028,40 +397,46 @@ private void clearFormId(Map info) { */ @Override public String terminateInstance(String instanceId, Map msgArgs, OperationContext context) { - String versionId = this.metaInstanceService.getMetaVersionId(instanceId); - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, metaInstanceService); - Function handler = this::statusCheckHandler; - Meta meta = this.metaService.retrieve(versionId, context); - String aippId = meta.getId(); - if (!checkInstanceStatus(aippId, instDetail, handler)) { + String versionId = this.appTaskInstanceService.getTaskId(instanceId); + AppTaskInstance instance = this.appTaskInstanceService.getInstance(versionId, instanceId, null) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + + AppTask appTask = this.appTaskService.getTaskById(versionId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task[{0}] not found.", versionId))); + String aippId = appTask.getEntity().getAppSuiteId(); + if (instance.is(MetaInstStatusEnum.READY, MetaInstStatusEnum.TERMINATED)) { log.error("aipp {} inst{}, not allow terminate.", aippId, instanceId); throw new AippException(context, AippErrCode.TERMINATE_INSTANCE_FORBIDDEN); } - String statusStr = instDetail.getInfo().get(AippConst.INST_STATUS_KEY); - if (MetaInstStatusEnum.getMetaInstStatus(statusStr) == MetaInstStatusEnum.RUNNING) { - String flowTraceId = instDetail.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); + if (instance.isRunning()) { + String flowTraceId = instance.getEntity().getFlowTranceId(); Validation.notNull(flowTraceId, "flowTraceId can not be null"); try { - this.flowInstanceService.terminateFlows(null, flowTraceId, Collections.emptyMap(), context); + this.flowInstanceService.terminateFlows( + null, + flowTraceId, + Collections.emptyMap(), + context); } catch (JobberException e) { log.error("terminate flow failed, flowTraceId:{}.", flowTraceId); throw new AippException(context, AippErrCode.TERMINATE_INSTANCE_FAILED); } // 更新实例状态 - InstanceDeclarationInfo info = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.TERMINATED.name()) + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(versionId, instanceId) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.TERMINATED.name()) .build(); - this.metaInstanceService.patchMetaInstance(versionId, instanceId, info, context); + this.appTaskInstanceService.update(updateEntity, context); } String message = this.getTerminateMessage(msgArgs); - String version = meta.getVersion(); + String version = appTask.getEntity().getVersion(); this.aopAippLogService.insertLog(AippLogCreateDto.builder() .aippId(aippId) .version(version) - .aippType(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY))) - .aippType(ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY))) + .aippType(appTask.getEntity().getAippType()) .instanceId(instanceId) .logType(AippInstLogType.MSG.name()) .logData(JsonUtils.toJsonString(AippLogData.builder().msg(message).build())) @@ -1076,12 +451,6 @@ private String getTerminateMessage(Map msgArgs) { .toString() : "已终止对话"; } - private boolean statusCheckHandler(String status) { - short instStatus = MetaInstStatusEnum.getMetaInstStatus(status).getValue(); - return Stream.of(MetaInstStatusEnum.READY.getValue(), MetaInstStatusEnum.TERMINATED.getValue()) - .noneMatch(value -> value == instStatus); - } - /** * 终止aipp全部实例 * @@ -1093,52 +462,25 @@ private boolean statusCheckHandler(String status) { @Override public void terminateAllPreviewInstances(String aippId, String versionId, boolean isDeleteLog, OperationContext context) { - final int limit = 15; - Stream instances = MetaUtils.getAllFromRangedResult(limit, - offset -> metaInstanceService.list(versionId, offset, limit, context)); - Function handler = status -> MetaInstStatusEnum.getMetaInstStatus(status).getValue() - == MetaInstStatusEnum.RUNNING.getValue(); - instances - // 只停止正在运行的 - .filter(instance -> checkInstanceStatus("versionId: " + versionId, instance, handler)) - .forEach(instance -> { - String flowTraceId = instance.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); - Validation.notNull(flowTraceId, "flowTraceId can not be null"); - flowInstanceService.terminateFlows(null, flowTraceId, Collections.emptyMap(), context); + Stream instanceStream = this.appTaskInstanceService.getInstanceStreamByTaskId(versionId, 15, + context); + + // 只停止正在运行的 + instanceStream.filter(AppTaskInstance::isRunning).forEach(instance -> { + String flowTraceId = instance.getEntity().getFlowTranceId(); + Validation.notNull(flowTraceId, "flowTraceId can not be null"); + flowInstanceService.terminateFlows(null, flowTraceId, Collections.emptyMap(), context); - // 更新实例状态 - InstanceDeclarationInfo info = InstanceDeclarationInfo.custom() - .putInfo(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.TERMINATED.name()) - .putInfo(AippConst.INST_FINISH_TIME_KEY, LocalDateTime.now()) - .build(); - metaInstanceService.patchMetaInstance(versionId, instance.getId(), info, context); - }); + // 更新实例状态. + AppTaskInstance updateEntity = AppTaskInstance.asUpdate(versionId, instance.getId()) + .setFinishTime(LocalDateTime.now()) + .setStatus(MetaInstStatusEnum.TERMINATED.name()) + .build(); + this.appTaskInstanceService.update(updateEntity, context); + }); if (isDeleteLog) { aippLogService.deleteAippPreviewLog(aippId, context); } } - - @Override - public AppBuilderAppStartDto startInstance(AppBuilderAppDto appDto, Map initContext, - OperationContext context) { - AippCreate aippCreate; - final String genericableId = "modelengine.fit.jober.aipp.service.app.debug"; - final String fitableId = "default"; - this.validateApp(appDto.getId()); - try { - aippCreate = - this.client.getRouter(genericableId).route(new FitableIdFilter(fitableId)).invoke(appDto, context); - } catch (FitException t) { - String errorMsg = t.getMessage(); - log.error("Error occurred when create debug aipp, error: {}", errorMsg); - throw new AippException(AippErrCode.CREATE_DEBUG_AIPP_FAILED, errorMsg); - } - String instanceId = createAippInstance(aippCreate.getAippId(), aippCreate.getVersion(), initContext, context); - return AppBuilderAppStartDto.builder().instanceId(instanceId).aippCreate(aippCreate).build(); - } - - private void validateApp(String appId) { - this.appFactory.create(appId); - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippStreamServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippStreamServiceImpl.java index dd39b4b8b8..b8ca915827 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippStreamServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AippStreamServiceImpl.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.service.impl; -import modelengine.fit.http.websocket.Session; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AippStreamService; import modelengine.fit.jober.aipp.util.AippLogUtils; import modelengine.fit.jober.aipp.util.JsonUtils; + +import modelengine.fit.http.websocket.Session; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.StringUtils; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AopAippLogServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AopAippLogServiceImpl.java index de2284243e..69ae8b8d53 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AopAippLogServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AopAippLogServiceImpl.java @@ -13,6 +13,7 @@ import modelengine.fit.jober.aipp.mapper.AippLogMapper; import modelengine.fit.jober.aipp.service.AopAippLogService; import modelengine.fit.jober.aipp.util.SensitiveFilterTools; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderAppServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderAppServiceImpl.java index d7e0ac9967..b03f4ff0f9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderAppServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderAppServiceImpl.java @@ -6,152 +6,68 @@ package modelengine.fit.jober.aipp.service.impl; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_DELETE_FAILED; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_PUBLISH_FAILED; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_UPDATE_FAILED; import static modelengine.fit.jober.aipp.common.exception.AippErrCode.INVALID_PATH_ERROR; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.OBTAIN_APP_CONFIGURATION_FAILED; import static modelengine.fit.jober.aipp.common.exception.AippErrCode.OBTAIN_APP_ORCHESTRATION_INFO_FAILED; import static modelengine.fit.jober.aipp.common.exception.AippErrCode.QUERY_PUBLICATION_HISTORY_FAILED; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.TASK_NOT_FOUND; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.UPDATE_APP_CONFIGURATION_FAILED; -import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_APP_ID_KEY; -import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_UNIQUE_NAME; -import static modelengine.fit.jober.aipp.service.impl.UploadedFileMangeServiceImpl.IRREMOVABLE; -import static modelengine.fit.jober.aipp.service.impl.UploadedFileMangeServiceImpl.REMOVABLE; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONException; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.TypeReference; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.api.trace.Span; -import modelengine.fit.jade.aipp.model.dto.ModelAccessInfo; -import modelengine.fit.jade.aipp.model.dto.ModelListDto; -import modelengine.fit.jade.aipp.model.service.AippModelCenter; -import modelengine.fit.jade.waterflow.AippFlowDefinitionService; -import modelengine.fit.jade.waterflow.FlowsService; -import modelengine.fit.jade.waterflow.service.FlowDefinitionService; +import lombok.RequiredArgsConstructor; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.common.enums.DirectionEnum; import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jane.task.util.Entities; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippParamException; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; import modelengine.fit.jober.aipp.condition.AppQueryCondition; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; -import modelengine.fit.jober.aipp.domain.AppBuilderConfig; -import modelengine.fit.jober.aipp.domain.AppBuilderConfigProperty; -import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; -import modelengine.fit.jober.aipp.domain.AppBuilderForm; -import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.converters.ConverterFactory; import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.app.App; +import modelengine.fit.jober.aipp.domains.app.AppFactory; +import modelengine.fit.jober.aipp.domains.app.service.AppDomainService; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.PublishContext; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.AppTaskUtils; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppMetadataDto; import modelengine.fit.jober.aipp.dto.AppBuilderConfigDto; -import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormDto; import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; -import modelengine.fit.jober.aipp.dto.AppTypeDto; import modelengine.fit.jober.aipp.dto.PublishedAppResDto; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; -import modelengine.fit.jober.aipp.dto.export.AppExportApp; -import modelengine.fit.jober.aipp.dto.export.AppExportConfig; -import modelengine.fit.jober.aipp.dto.export.AppExportDto; -import modelengine.fit.jober.aipp.dto.export.AppExportFlowGraph; import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; -import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; -import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; -import modelengine.fit.jober.aipp.enums.AippTypeEnum; -import modelengine.fit.jober.aipp.enums.AppCategory; -import modelengine.fit.jober.aipp.enums.AppState; -import modelengine.fit.jober.aipp.enums.AppTypeEnum; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.factory.AppTemplateFactory; import modelengine.fit.jober.aipp.factory.CheckerFactory; import modelengine.fit.jober.aipp.genericable.entity.AippCreate; -import modelengine.fit.jober.aipp.mapper.AippChatMapper; -import modelengine.fit.jober.aipp.mapper.AippLogMapper; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; -import modelengine.fit.jober.aipp.service.AippChatService; -import modelengine.fit.jober.aipp.service.AippFlowService; import modelengine.fit.jober.aipp.service.AppBuilderAppService; -import modelengine.fit.jober.aipp.service.AppTypeService; import modelengine.fit.jober.aipp.service.Checker; import modelengine.fit.jober.aipp.service.UploadedFileManageService; -import modelengine.fit.jober.aipp.util.AippFileUtils; -import modelengine.fit.jober.aipp.util.AppImExportUtil; import modelengine.fit.jober.aipp.util.ConvertUtils; -import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; import modelengine.fit.jober.aipp.util.RandomPathUtils; -import modelengine.fit.jober.aipp.util.TemplateUtils; -import modelengine.fit.jober.aipp.util.VersionUtils; -import modelengine.fit.jober.aipp.validation.AppUpdateValidator; import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fitable; -import modelengine.fitframework.annotation.Value; -import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; -import modelengine.fitframework.model.Tuple; -import modelengine.fitframework.transaction.DataAccessException; import modelengine.fitframework.transaction.Transactional; -import modelengine.fitframework.util.CollectionUtils; -import modelengine.fitframework.util.FileUtils; -import modelengine.fitframework.util.IoUtils; -import modelengine.fitframework.util.MapBuilder; -import modelengine.fitframework.util.MapUtils; -import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; -import modelengine.jade.app.engine.base.service.UsrAppCollectionService; import modelengine.jade.knowledge.KnowledgeCenterService; -import modelengine.jade.knowledge.dto.KnowledgeDto; -import modelengine.jade.store.entity.transfer.PluginToolData; -import modelengine.jade.store.service.AppService; -import modelengine.jade.store.service.PluginService; -import modelengine.jade.store.service.PluginToolService; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -162,150 +78,31 @@ * @since 2024-04-17 */ @Component +@RequiredArgsConstructor public class AppBuilderAppServiceImpl implements AppBuilderAppService, modelengine.fit.jober.aipp.genericable.AppBuilderAppService { private static final Logger log = Logger.get(AppBuilderAppServiceImpl.class); - private static final int PATH_LENGTH = 16; - - private static final int RETRY_CREATE_TIMES = 5; - - private static final int MODEL_LIST_SERVICE_NAME = 0; - - private static final int MODEL_LIST_TAG = 1; - - private static final String APP_BUILDER_DEFAULT_MODEL_NAME = "#app_builder_default_model_name#"; - - private static final String APP_BUILDER_DEFAULT_SERVICE_NAME = "#app_builder_default_service_name#"; - - private static final String APP_BUILDER_DEFAULT_TAG = "#app_builder_default_tag#"; - private static final String APP_BUILDER_DEFAULT_KNOWLEDGE_SET = "#app_builder_default_knowledge_set#"; - private static final String DEFAULT_APP_VERSION = "1.0.0"; - - private static final String VERSION_FORMAT = "{0}.{1}.{2}"; - - private static final String PUBLISH_UPDATE_DESCRIPTION_KEY = "publishedDescription"; - - private static final String PUBLISH_UPDATE_LOG_KEY = "publishedUpdateLog"; - - private static final int VERSION_LENGTH = 8; - - private static final String FORM_PROPERTY_GROUP_NULL = "null"; - - private static final int RETRY_PATH_GENERATION_TIMES = 3; - - private static final String[] TEMPLATE_DEFAULT_ATTRIBUTE_KEYS = {"icon", "description", "greeting", "app_type"}; - - private static final String APP_ATTR_DESCRIPTION = "description"; - - private static final String APP_ATTR_ICON = "icon"; - - private static final String APP_ATTR_GREETING = "greeting"; - - private static final String APP_ATTR_STORE_ID = "store_id"; - - private static final String APP_ATTR_APP_TYPE = "app_type"; - - private static final String APP_ATTR_LATEST_VERSION = "latest_version"; - - private final AppBuilderAppFactory appFactory; - private final AppTemplateFactory templateFactory; - - private final AippFlowService aippFlowService; - - private final AppBuilderAppRepository appRepository; - - private final int nameLengthMaximum; - - private final MetaService metaService; - - private final AppUpdateValidator appUpdateValidator; - - private final MetaInstanceService metaInstanceService; - - private final UsrAppCollectionService usrAppCollectionService; - private final UploadedFileManageService uploadedFileManageService; - - private final String appNameFormat = "^[\\u4E00-\\u9FA5A-Za-z0-9][\\u4E00-\\u9FA5A-Za-z0-9-_]*$"; - - private final AippLogMapper aippLogMapper; - - private final FlowsService flowsService; - - private final AppService appService; - - private final AippModelCenter aippModelCenter; - - private final AippChatMapper aippChatMapper; - - private final PluginToolService pluginToolService; - - private final PluginService pluginService; - - private final Map exportMeta; - - private final AppTypeService appTypeService; - - private final FlowDefinitionService flowDefinitionService; - - private final AippFlowDefinitionService aippFlowDefinitionService; - - private final String contextRoot; - + private final AppTaskService appTaskService; + private final AppVersionService appVersionService; + private final AppDomainService appDomainService; + private final AppFactory appDomainFactory; + private final ConverterFactory converterFactory; private final KnowledgeCenterService knowledgeCenterService; - public AppBuilderAppServiceImpl(AppBuilderAppFactory appFactory, AippFlowService aippFlowService, - AppBuilderAppRepository appRepository, AppTemplateFactory templateFactory, - @Value("${validation.task.name.length.maximum:64}") int nameLengthMaximum, MetaService metaService, - UsrAppCollectionService usrAppCollectionService, AppUpdateValidator appUpdateValidator, - MetaInstanceService metaInstanceService, UploadedFileManageService uploadedFileManageService, - AippLogMapper aippLogMapper, FlowsService flowsService, AppService appService, - AippChatService aippChatService, AippModelCenter aippModelCenter, AippChatMapper aippChatMapper, - @Value("${export-meta}") Map exportMeta, AppTypeService appTypeService, - PluginToolService pluginToolService, PluginService pluginService, - FlowDefinitionService flowDefinitionService, AippFlowDefinitionService aippFlowDefinitionService, - @Value("${app-engine.contextRoot}") String contextRoot, KnowledgeCenterService knowledgeCenterService) { - this.nameLengthMaximum = nameLengthMaximum; - this.appFactory = appFactory; - this.templateFactory = templateFactory; - this.aippFlowService = aippFlowService; - this.appRepository = appRepository; - this.metaService = metaService; - this.usrAppCollectionService = usrAppCollectionService; - this.appUpdateValidator = appUpdateValidator; - this.metaInstanceService = metaInstanceService; - this.uploadedFileManageService = uploadedFileManageService; - this.aippLogMapper = aippLogMapper; - this.flowsService = flowsService; - this.appService = appService; - this.aippModelCenter = aippModelCenter; - this.aippChatMapper = aippChatMapper; - this.exportMeta = exportMeta; - this.appTypeService = appTypeService; - this.pluginToolService = pluginToolService; - this.pluginService = pluginService; - this.flowDefinitionService = flowDefinitionService; - this.aippFlowDefinitionService = aippFlowDefinitionService; - this.contextRoot = contextRoot; - this.knowledgeCenterService = knowledgeCenterService; - } - @Override @Fitable(id = "default") public AppBuilderAppDto query(String appId, OperationContext context) { - AppBuilderApp appBuilderApp = this.appFactory.create(appId); - AppBuilderAppDto appBuilderAppDto = this.buildFullAppDto(appBuilderApp); - try { - appBuilderAppDto.setBaselineCreateAt(this.getMetaByCreatAtDirection(appId, context, DirectionEnum.ASCEND) - .getCreationTime()); - appBuilderAppDto.setAippId(MetaUtils.getAippIdByAppId(this.metaService, appId, context)); - } catch (AippTaskNotFoundException e) { - throw new AippException(OBTAIN_APP_CONFIGURATION_FAILED); - } + AppBuilderAppDto appBuilderAppDto = this.appVersionService.getByAppId(appId) + .map(v -> this.converterFactory.convert(v, AppBuilderAppDto.class)) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND)); + AppVersion firstAppVersion = this.appVersionService.getFirstCreatedByAppSuiteId(appBuilderAppDto.getAippId()) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND)); + appBuilderAppDto.setBaselineCreateAt(firstAppVersion.getData().getCreateAt()); return appBuilderAppDto; } @@ -316,95 +113,27 @@ public AppBuilderAppDto queryByPath(String path) { log.error("Invalid path format path: {}.", path); throw new AippException(INVALID_PATH_ERROR); } - AppBuilderApp appBuilderApp = this.appFactory.createByPath(path); - return this.buildFullAppDto(appBuilderApp); + return this.appVersionService.getByPath(path) + .map(v -> this.converterFactory.convert(v, AppBuilderAppDto.class)) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND)); } @Override @Fitable(id = "default") @Transactional public Rsp publish(AppBuilderAppDto appDto, OperationContext contextOf) { - // 要加个save appDto到数据的逻辑 - this.validateApp(appDto.getId()); - AippDto aippDto = ConvertUtils.convertToAippDtoFromAppBuilderAppDto(appDto); - try { - this.validateVersion(aippDto, contextOf); - } catch (AippTaskNotFoundException e) { - throw new AippException(APP_PUBLISH_FAILED); - } - String id = appDto.getId(); - AppBuilderApp appBuilderApp = this.appFactory.create(id); - if (AppState.getAppState(appBuilderApp.getState()) == AppState.PUBLISHED) { + AppVersion appVersion = this.appVersionService.getByAppId(appDto.getId()) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App version[{0}] not found.", appDto.getId()))); + if (appVersion.isPublished()) { throw new AippException(AippErrCode.APP_HAS_PUBLISHED); } - appBuilderApp.setState(AppState.PUBLISHED.getName()); - // 添加校验,禁止更低版本手动输入 - this.validateVersionIsLatest(appBuilderApp.getVersion(), appDto.getVersion()); - appBuilderApp.setVersion(appDto.getVersion()); - AippCreateDto aippCreateDto = this.aippFlowService.create(aippDto, contextOf); - aippDto.setId(aippCreateDto.getAippId()); - AppBuilderSaveConfigDto saveConfigDto = AppBuilderSaveConfigDto.builder() - .graph(JsonUtils.toJsonString(appDto.getFlowGraph().getAppearance())) - .input(appDto.getConfigFormProperties()) - .build(); - this.saveConfig(id, saveConfigDto, contextOf); - Map appBuilderAppAttr = appBuilderApp.getAttributes(); - appBuilderAppAttr.put(PUBLISH_UPDATE_DESCRIPTION_KEY, aippDto.getPublishedDescription()); - appBuilderAppAttr.put(PUBLISH_UPDATE_LOG_KEY, aippDto.getPublishedUpdateLog()); - if (StringUtils.isEmpty(appBuilderApp.getPath())) { - String path = generateUniquePath(); - appBuilderApp.setPath(path); - } - this.appFactory.update(appBuilderApp); - if (appBuilderApp.getAttributes().containsKey("store_id")) { - aippDto.setUniqueName(appBuilderApp.getAttributes().get("store_id").toString()); - } - return this.aippFlowService.publish(aippDto, appBuilderApp, contextOf); - } - - /** - * 校验版本号是否比现有号更新 - * - * @param oldVersion 旧版本号 - * @param newVersion 新版本号 - */ - private void validateVersionIsLatest(String oldVersion, String newVersion) { - if (!VersionUtils.isValidVersion(oldVersion) || !VersionUtils.isValidVersion(newVersion)) { - throw new AippException(AippErrCode.INVALID_VERSION_NAME); - } - if (StringUtils.equals(oldVersion, newVersion)) { - // 在这里,与旧版本号相同的版本号被认为是最新的 - return; - } - String[] oldPart = oldVersion.split("\\."); - String[] newPart = newVersion.split("\\."); - for (int i = 0; i < oldPart.length; i++) { - int oldV = Integer.parseInt(oldPart[i]); - int newV = Integer.parseInt(newPart[i]); - if (oldV > newV) { - // 如果某个号比当前号小,则认为新版本比旧版本小,抛出错误 - throw new AippParamException(AippErrCode.NEW_VERSION_IS_LOWER); - } - if (newV > oldV) { - // 如果某个号比当前号大,则认为新版本比旧版本大,例如2.0.0比1.9.9大 - break; - } - } - } - - private void validateVersion(AippDto aippDto, OperationContext contextOf) throws AippTaskNotFoundException { - String aippId = MetaUtils.getAippIdByAppId(this.metaService, aippDto.getAppId(), contextOf); - MetaFilter metaFilter = new MetaFilter(); - metaFilter.setVersions(Collections.singletonList(aippDto.getVersion())); - metaFilter.setMetaIds(Collections.singletonList(aippId)); - Map> attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_AIPP_TYPE_KEY, Collections.singletonList(AippTypeEnum.NORMAL.type())); - attributes.put(AippConst.ATTR_META_STATUS_KEY, Collections.singletonList(AippMetaStatusEnum.ACTIVE.getCode())); - metaFilter.setAttributes(attributes); - RangedResultSet metas = this.metaService.list(metaFilter, true, 0, 1, contextOf); - if (!metas.getResults().isEmpty()) { - throw new AippException(AippErrCode.APP_VERSION_HAS_ALREADY); - } + appVersion.publish(new PublishContext(appDto, contextOf)); + return Rsp.ok(AippCreateDto.builder() + .aippId(appVersion.getData().getAppSuiteId()) + .version(appVersion.getData().getVersion()) + .toolUniqueName(appVersion.getData().getUniqueName()) + .build()); } @Override @@ -412,161 +141,73 @@ private void validateVersion(AippDto aippDto, OperationContext contextOf) throws public AippCreate debug(AppBuilderAppDto appDto, OperationContext contextOf) { AippDto aippDto = ConvertUtils.convertToAippDtoFromAppBuilderAppDto(appDto); // Rsp 得统一整改下 - return ConvertUtils.toAippCreate(this.aippFlowService.previewAipp(appDto.getVersion(), aippDto, contextOf)); + return ConvertUtils.toAippCreate(this.appVersionService.retrieval(aippDto.getAppId()) + .preview(appDto.getVersion(), aippDto, contextOf)); } @Override @Fitable(id = "default") public void updateFlow(String appId, OperationContext contextOf) { - AppBuilderApp app = this.appFactory.create(appId); - if (ObjectUtils.cast(app.getAttributes().getOrDefault(AippConst.ATTR_APP_IS_UPDATE, true))) { - AippDto aippDto = ConvertUtils.convertToAippDtoFromAppBuilderAppDto(this.buildFullAppDto(app)); - this.aippFlowService.previewAipp(app.getVersion(), aippDto, contextOf); - app.getAttributes().put(AippConst.ATTR_APP_IS_UPDATE, false); - this.appFactory.update(app); - } - } - - private Meta getMetaByCreatAtDirection(String appId, OperationContext context, DirectionEnum direction) - throws AippTaskNotFoundException { - String aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - MetaFilter filter = new MetaFilter(); - filter.setMetaIds(Collections.singletonList(aippId)); - String sortEncode = MetaUtils.formatSorter(AippSortKeyEnum.CREATE_AT.name(), direction.name()); - filter.setOrderBys(Collections.singletonList(sortEncode)); - List metas = MetaUtils.getListMetaHandle(this.metaService, filter, context); - if (metas.isEmpty()) { - log.error("Meta list can not be empty."); - throw new AippTaskNotFoundException(TASK_NOT_FOUND); + AppVersion appVersion = this.appVersionService.retrieval(appId); + if (appVersion.isUpdated()) { + AippDto aippDto = ConvertUtils.convertToAippDtoFromAppBuilderAppDto( + this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); + appVersion.preview(appVersion.getData().getVersion(), aippDto, contextOf); + appVersion.getAttributes().put(AippConst.ATTR_APP_IS_UPDATE, false); + this.appVersionService.update(appVersion); } - return metas.get(0); } @Override public AppBuilderAppDto queryLatestOrchestration(String appId, OperationContext context) { - this.validateApp(appId); - Meta latestMeta; - try { - latestMeta = this.getMetaByCreatAtDirection(appId, context, DirectionEnum.DESCEND); - } catch (AippTaskNotFoundException e) { - throw new AippException(OBTAIN_APP_ORCHESTRATION_INFO_FAILED); + AppVersion appVersion = this.appVersionService.retrieval(appId); + App app = this.appDomainFactory.create(appVersion.getData().getAppSuiteId()); + AppVersion latestVersion = app.getLatestVersion() + .orElseThrow(() -> new AippException(OBTAIN_APP_ORCHESTRATION_INFO_FAILED)); + if (latestVersion.isPublished()) { + AppVersion upgraded = this.appVersionService.upgrade(latestVersion.getData().getId(), + this.converterFactory.convert(latestVersion, AppBuilderAppCreateDto.class), context); + return this.converterFactory.convert(upgraded, AppBuilderAppDto.class); } - String copiedAppId = String.valueOf(latestMeta.getAttributes().get(ATTR_APP_ID_KEY)); - AppBuilderApp copiedApp = this.appFactory.create(copiedAppId); - copiedApp.setAppBuiltType(this.appRepository.selectWithId(copiedAppId).getAppBuiltType()); - if (MetaUtils.isPublished(latestMeta)) { - return this.create(copiedAppId, this.buildAppBuilderAppCreateDto(copiedApp), context, true); - } - return this.query(copiedAppId, context); - } - - /** - * 校验应用是否存在 - * - * @param appId 待校验的应用 id - */ - private AppBuilderApp validateApp(String appId) { - return this.appFactory.create(appId); + return this.query(latestVersion.getData().getId(), context); } @Override public AippCreate queryLatestPublished(String appId, OperationContext context) { - List metas; - try { - metas = this.getPublishedMetaList(appId, context); - } catch (AippTaskNotFoundException e) { - log.error("Failed to get published meta list."); - throw new AippException(OBTAIN_APP_CONFIGURATION_FAILED); - } - if (metas.isEmpty()) { - log.error("Meta list can not be empty."); - throw new AippParamException(TASK_NOT_FOUND); - } - Meta latestMeta = metas.get(0); - String latestAppId = ObjectUtils.cast(latestMeta.getAttributes().get(ATTR_APP_ID_KEY)); + AppVersion appVersion = this.appVersionService.retrieval(appId); + AppTask appTask = appVersion.getLatestPublishedTask(context); + String latestAppId = appTask.getEntity().getAppId(); return AippCreate.builder() - .version(latestMeta.getVersion()) - .aippId(latestMeta.getId()) + .version(appTask.getEntity().getVersion()) + .aippId(appVersion.getData().getAppSuiteId()) .appId(latestAppId) .build(); } - private List getPublishedMetaList(String appId, OperationContext context) throws AippTaskNotFoundException { - MetaFilter filter = new MetaFilter(); - String aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - filter.setMetaIds(Collections.singletonList(aippId)); - String sortEncode = MetaUtils.formatSorter(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()); - filter.setOrderBys(Collections.singletonList(sortEncode)); - Map> attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_AIPP_TYPE_KEY, Collections.singletonList(AippTypeEnum.NORMAL.name())); - attributes.put(AippConst.ATTR_META_STATUS_KEY, Collections.singletonList(AippMetaStatusEnum.ACTIVE.getCode())); - filter.setAttributes(attributes); - return MetaUtils.getListMetaHandle(this.metaService, filter, context); - } - - private AppBuilderAppCreateDto buildAppBuilderAppCreateDto(AppBuilderApp app) { - Map attributes = app.getAttributes(); - String description = this.getAttribute(attributes, APP_ATTR_DESCRIPTION); - String icon = this.getAttribute(attributes, APP_ATTR_ICON); - String greeting = this.getAttribute(attributes, APP_ATTR_GREETING); - String storeId = this.getAttribute(attributes, APP_ATTR_STORE_ID); - return AppBuilderAppCreateDto.builder() - .name(app.getName()) - .description(description) - .icon(icon) - .greeting(greeting) - .appType(app.getAppType()) - .type(app.getType()) - .storeId(storeId) - .appBuiltType(app.getAppBuiltType()) - .appCategory(app.getAppCategory()) - .build(); - } - @Override @Fitable(id = "default") public Optional getPropertyByName(String appId, String name) { - AppBuilderApp appBuilderApp = this.appFactory.create(appId); - List configFormPropertyDtos = - this.buildAppBuilderConfigFormProperties(appBuilderApp.getFormProperties()); - return configFormPropertyDtos.stream().filter(prop -> prop.getName().equals(name)).findFirst(); + AppVersion appVersion = this.appVersionService.retrieval(appId); + AppBuilderAppDto dto = this.converterFactory.convert(appVersion, AppBuilderAppDto.class); + List formPropertyDtoList = dto.getConfigFormProperties(); + return formPropertyDtoList.stream().filter(prop -> StringUtils.equals(prop.getName(), name)).findFirst(); } @Override @Fitable(id = "default") - public Rsp> list(AppQueryCondition cond, - OperationContext context, long offset, int limit) { + public Rsp> list(AppQueryCondition cond, OperationContext context, + long offset, int limit) { if (cond == null) { cond = new AppQueryCondition(); } cond.setCreateBy(context.getOperator()); - List result = this.appRepository.selectWithLatestApp(cond, context.getTenantId(), offset, limit) + RangedResultSet result = + this.appVersionService.pageListByTenantId(cond, context.getTenantId(), offset, limit); + List metaDtoList = result.getResults() .stream() - .map(this::buildAppMetaData) - .collect(Collectors.toList()); - long total = this.appRepository.countWithLatestApp(context.getTenantId(), cond); - return Rsp.ok(RangedResultSet.create(result, offset, limit, total)); - } - - private AppBuilderAppMetadataDto buildAppMetaData(AppBuilderApp app) { - List tags = new ArrayList<>(); - tags.add(app.getType().toUpperCase(Locale.ROOT)); - return AppBuilderAppMetadataDto.builder() - .id(app.getId()) - .name(app.getName()) - .type(app.getType()) - .state(app.getState()) - .appType(app.getAppType()) - .attributes(app.getAttributes()) - .version(app.getVersion()) - .createBy(app.getCreateBy()) - .updateBy(app.getUpdateBy()) - .createAt(app.getCreateAt()) - .updateAt(app.getUpdateAt()) - .appCategory(app.getAppCategory()) - .tags(tags) - .appBuiltType(app.getAppBuiltType()) - .build(); + .map(v -> this.converterFactory.convert(v, AppBuilderAppMetadataDto.class)) + .toList(); + return Rsp.ok(RangedResultSet.create(metaDtoList, offset, limit, result.getRange().getTotal())); } @Override @@ -574,194 +215,19 @@ private AppBuilderAppMetadataDto buildAppMetaData(AppBuilderApp app) { @Fitable(id = "default") public AppBuilderAppDto create(String appId, AppBuilderAppCreateDto dto, OperationContext context, boolean isUpgrade) { - if (dto != null && !isUpgrade) { - this.validateCreateApp(dto, context); - } - String[] firstModelInfo = this.getFirstModelInfo(context); - AppBuilderApp templateApp = this.appFactory.create(appId); - if (Objects.nonNull(dto)) { - templateApp.setAppCategory(dto.getAppCategory()); - templateApp.setAppBuiltType(dto.getAppBuiltType()); - } - KnowledgeDto firstKnowledge = this.knowledgeCenterService.getSupportKnowledges(context.getOperator()).get(0); - AppBuilderFlowGraph flowGraph = templateApp.getFlowGraph(); - flowGraph.setAppearance(flowGraph.getAppearance() - .replace(APP_BUILDER_DEFAULT_MODEL_NAME, firstModelInfo[MODEL_LIST_SERVICE_NAME]) - .replace(APP_BUILDER_DEFAULT_SERVICE_NAME, firstModelInfo[MODEL_LIST_SERVICE_NAME]) - .replace(APP_BUILDER_DEFAULT_TAG, firstModelInfo[MODEL_LIST_TAG]) - .replace(APP_BUILDER_DEFAULT_KNOWLEDGE_SET, firstKnowledge.getGroupId())); - return this.createAppWithTemplate(dto, templateApp, context, isUpgrade, AppTypeEnum.APP.name(), false); - } - - private AppBuilderAppDto createAppWithTemplate(AppBuilderAppCreateDto dto, AppBuilderApp templateApp, - OperationContext context, boolean isUpgrade, String appType, boolean isImport) { - // 根据模板app复制app,仅需修改所有id - // 优先copy下层内容,因为上层改变Id后,会影响下层对象的查询 - AppBuilderFlowGraph flowGraph = templateApp.getFlowGraph(); - flowGraph.setId(Entities.generateId()); - Map appearance; - try { - appearance = JSONObject.parseObject(flowGraph.getAppearance(), new TypeReference>() {}); - } catch (JSONException e) { - log.error("Import config failed, cause: {}", e); - throw new AippException(AippErrCode.IMPORT_CONFIG_FIELD_ERROR, "flowGraph.appearance"); - } - appearance.computeIfPresent("id", (key, value) -> flowGraph.getId()); - // 这里在创建应用时需要保证graph中的title+version唯一,否则在发布flow时会报错 - appearance.put("title", flowGraph.getId()); - // 动态修改graph中的model为可选model的第一个 - flowGraph.setAppearance(JSONObject.toJSONString(appearance)); - String version = this.buildVersion(templateApp, isUpgrade); - List formProperties = templateApp.getFormProperties(); - templateApp.setId(Entities.generateId()); - AppBuilderConfig config = resetConfig(formProperties, templateApp.getConfig()); - templateApp.setConfigId(config.getId()); - templateApp.setFlowGraphId(flowGraph.getId()); - templateApp.setType(appType); - templateApp.setTenantId(context.getTenantId()); - if (!isImport) { - templateApp.setState(AppState.INACTIVE.getName()); - } - String preVersion = templateApp.getVersion(); - templateApp.setVersion(version); - config.setAppId(templateApp.getId()); - if (Objects.nonNull(dto)) { - templateApp.setAttributes(this.createAppAttributes(dto, isUpgrade, preVersion)); - templateApp.setName(dto.getName()); - templateApp.setType(dto.getType()); - templateApp.setAppType(dto.getAppType()); + if (isUpgrade) { + return this.converterFactory.convert(this.appVersionService.upgrade(appId, dto, context), + AppBuilderAppDto.class); + } else { + return this.converterFactory.convert(this.appVersionService.create(appId, dto, context), + AppBuilderAppDto.class); } - resetOperatorAndTime(templateApp, LocalDateTime.now(), context.getOperator()); - this.saveNewAppBuilderApp(templateApp); - AppBuilderAppDto appDto = this.buildFullAppDto(templateApp); - AippCreateDto createDto = this.saveMeta(appDto, version, context); - appDto.setAippId(createDto.getAippId()); - return appDto; } @Override @Fitable(id = "default") public long getAppCount(String tenantId, AppQueryCondition cond) { - return this.appRepository.countWithLatestApp(tenantId, cond); - } - - private AippCreateDto saveMeta(AppBuilderAppDto appDto, String version, OperationContext context) { - AippDto aippDto = ConvertUtils.convertToAippDtoFromAppBuilderAppDto(appDto); - return this.aippFlowService.previewAipp(version, aippDto, context); - } - - private String buildVersion(AppBuilderApp app, boolean isUpgrade) { - // 当前只考虑升级,如果后续需要做基于应用创建新应用,则需要改动下面逻辑。 - if (!isUpgrade || !VersionUtils.isValidVersion(app.getVersion())) { - return DEFAULT_APP_VERSION; - } - String[] parts = app.getVersion().split("\\."); - parts[2] = String.valueOf(Integer.parseInt(parts[2]) + 1); - String newVersion = StringUtils.format(VERSION_FORMAT, parts[0], parts[1], parts[2]); - return newVersion.length() > VERSION_LENGTH ? app.getVersion() : newVersion; - } - - private void validateCreateApp(AppBuilderAppCreateDto dto, OperationContext context) { - String name = dto.getName(); - this.validateAppName(name, context); - AppQueryCondition queryCondition = - AppQueryCondition.builder().tenantId(context.getTenantId()).name(name).build(); - if (!this.appRepository.selectWithCondition(queryCondition).isEmpty()) { - log.error("Create aipp failed, [name={}, tenantId={}]", name, context.getTenantId()); - throw new AippException(context, AippErrCode.AIPP_NAME_IS_DUPLICATE); - } - if (dto.getDescription() != null) { - this.validateAppDescription(dto, context); - } - this.validateAppCategory(dto, context); - } - - private void validateAppDescription(AppBuilderAppCreateDto dto, OperationContext context) { - if (dto.getDescription().length() > 300) { - log.error("Create aipp failed, [name={}, tenantId={}], app description is larger than 300.", - dto.getName(), - context.getTenantId()); - throw new AippException(context, AippErrCode.APP_DESCRIPTION_LENGTH_OUT_OF_BOUNDS); - } - } - - private void validateAppCategory(AppBuilderAppCreateDto dto, OperationContext context) { - if (dto.getAppCategory() == null) { - log.error("Create aipp failed, [name={}, tenantId={}], app category is null.", - dto.getName(), - context.getTenantId()); - throw new AippException(context, AippErrCode.APP_CATEGORY_IS_NULL); - } - } - - /** - * 校验更新的app的合法性 - * - * @param appId app唯一标识 - * @param name app名称 - * @param context 操作上下文 - * @throws AippTaskNotFoundException 任务不存在异常 - */ - public void validateUpdateApp(String appId, String name, OperationContext context) - throws AippTaskNotFoundException { - // 这个静态校验名称不能为空、不能超长 - this.validateAppName(name, context); - - // 如果该app已经发布过了,那么将不再允许修改名称 - List metaList = this.getPublishedMetaList(appId, context); - if (CollectionUtils.isNotEmpty(metaList) && !StringUtils.equals(metaList.get(0).getName(), name)) { - throw new AippException(AippErrCode.APP_NAME_HAS_PUBLISHED); - } - - // 到这里,要么是metaList是空的,要么就是没有改名 - // 如果metaList不是空的,证明没有改名,不管 - if (CollectionUtils.isNotEmpty(metaList)) { - return; - } - // 如果mataList是空的,即没有发布过,那么名称可以修改为不和其它名称重复的名称 - AppQueryCondition queryCondition = - AppQueryCondition.builder().tenantId(context.getTenantId()).name(name).build(); - List appBuilderApps = this.appRepository.selectWithCondition(queryCondition); - if (appBuilderApps.isEmpty()) { - return; - } - if (appBuilderApps.size() > 1 || !Objects.equals(appBuilderApps.get(0).getId(), appId)) { - log.error("update aipp failed, [name={}, tenantId={}]", name, context.getTenantId()); - throw new AippException(context, AippErrCode.AIPP_NAME_IS_DUPLICATE); - } - } - - private void validateAppName(String name, OperationContext context) { - if (!name.matches(this.appNameFormat)) { - log.error("Create aipp failed: the name format is incorrect. [name={}]", name); - throw new AippParamException(context, AippErrCode.APP_NAME_IS_INVALID); - } - String trimName = StringUtils.trim(name); - if (StringUtils.isEmpty(trimName)) { - log.error("Create aipp failed: name can not be empty."); - throw new AippParamException(context, AippErrCode.AIPP_NAME_IS_EMPTY); - } else { - if (name.length() > this.nameLengthMaximum) { - log.error("Create aipp failed: the length of task name is out of bounds. [name={}]", name); - throw new AippParamException(context, AippErrCode.AIPP_NAME_LENGTH_OUT_OF_BOUNDS); - } - } - } - - private Map createAppAttributes(AppBuilderAppCreateDto dto, boolean isUpgrade, String preVersion) { - Map attributes = new HashMap<>(); - attributes.put(APP_ATTR_DESCRIPTION, dto.getDescription()); - // 增加保护,前端传入null时部分场景导致出现字符串"null" - attributes.put(APP_ATTR_ICON, dto.getIcon() == null ? StringUtils.EMPTY : dto.getIcon()); - attributes.put(APP_ATTR_GREETING, dto.getGreeting() == null ? StringUtils.EMPTY : dto.getGreeting()); - attributes.put(APP_ATTR_APP_TYPE, dto.getAppType() == null ? StringUtils.EMPTY : dto.getAppType()); - if (StringUtils.isNotBlank(dto.getStoreId())) { - attributes.put(APP_ATTR_STORE_ID, dto.getStoreId()); - } - if (isUpgrade) { - attributes.put(APP_ATTR_LATEST_VERSION, preVersion); - } - return attributes; + return this.appVersionService.countByTenantId(cond, tenantId); } @Override @@ -771,45 +237,8 @@ public Rsp updateApp(String appId, AppBuilderAppDto appDto, Op if (appDto == null) { throw new AippException(AippErrCode.INVALID_OPERATION); } - - // 这一行都是有校验的作用,不能挪到下面 - AppBuilderApp update = this.appFactory.create(appId); - try { - this.validateUpdateApp(appId, appDto.getName(), context); - } catch (AippTaskNotFoundException e) { - throw new AippException(APP_UPDATE_FAILED); - } - this.appUpdateValidator.validate(appId); - update.setUpdateBy(context.getOperator()); - update.setUpdateAt(LocalDateTime.now()); - update.setName(appDto.getName()); - update.setType(appDto.getType()); - update.setAppType(appDto.getAppType()); - // 避免前端更新将app表的attributes覆盖了 - String oldIcon = ObjectUtils.cast(update.getAttributes().get("icon")); - this.updateAttributes(update, appDto.getAttributes()); - this.updateAppState(update, appDto.getState()); - update.setVersion(appDto.getVersion()); - this.appFactory.update(update); - String newIcon = ObjectUtils.cast(update.getAttributes().get("icon")); - if (StringUtils.isNotBlank(newIcon) && !StringUtils.equals(oldIcon, newIcon)) { - this.uploadedFileManageService.changeRemovable(AippFileUtils.getFileNameFromIcon(oldIcon), REMOVABLE); - this.uploadedFileManageService.changeRemovable(AippFileUtils.getFileNameFromIcon(newIcon), IRREMOVABLE); - } - return Rsp.ok(this.buildFullAppDto(update)); - } - - private void updateAttributes(AppBuilderApp update, Map attributes) { - Map attributesOld = update.getAttributes(); - attributesOld.putAll(attributes); - attributesOld.put(AippConst.ATTR_APP_IS_UPDATE, true); - } - - private void updateAppState(AppBuilderApp appToUpdated, String targetState) { - if (StringUtils.equals(appToUpdated.getState(), AppState.IMPORTING.getName()) && StringUtils.equals(targetState, - AppState.INACTIVE.getName())) { - appToUpdated.setState(AppState.INACTIVE.getName()); - } + AppVersion appVersion = this.appVersionService.update(appId, appDto, context); + return Rsp.ok(this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); } @Override @@ -817,29 +246,18 @@ private void updateAppState(AppBuilderApp appToUpdated, String targetState) { @Fitable(id = "default") public Rsp updateConfig(String appId, AppBuilderConfigDto configDto, List properties, OperationContext context) { - this.appUpdateValidator.validate(appId); - LocalDateTime operateTime = LocalDateTime.now(); - AppBuilderApp oldApp = this.appFactory.create(appId); - Span.current().setAttribute("name", oldApp.getName()); - - AppBuilderConfig oldConfig = oldApp.getConfig(); - AppBuilderFlowGraph oldFlowGraph = oldApp.getFlowGraph(); - List oldFormProperties = oldApp.getFormProperties(); - - // 先更新config - this.updateConfigPropertiesByAppBuilderConfigDto(appId, properties, oldConfig, oldFormProperties); - this.updateConfigAndForm(configDto, context, oldConfig, operateTime, oldApp); - // 然后同步更新flowGraph - oldFlowGraph.setUpdateBy(context.getOperator()); - oldFlowGraph.setUpdateAt(operateTime); - oldFlowGraph.setAppearance(this.updateFlowGraphAppearanceByConfigDto(oldFlowGraph.getAppearance(), properties)); - oldApp.getFlowGraphRepository().updateOne(oldFlowGraph); - // 最后更新app主表 - oldApp.setUpdateAt(operateTime); - oldApp.setUpdateBy(context.getOperator()); - this.updateAttributes(oldApp, new HashMap<>()); - this.appFactory.update(oldApp); - return Rsp.ok(this.buildFullAppDto(oldApp)); + AppVersion appVersion = this.appVersionService.retrieval(appId); + if (appVersion.isPublished()) { + throw new AippException(AippErrCode.APP_HAS_ALREADY); + } + Span.current().setAttribute("name", appVersion.getData().getName()); + appVersion.updateConfig(configDto, properties, context); + appVersion.updateGraph(properties, context); + appVersion.getData().setUpdateAt(LocalDateTime.now()); + appVersion.getData().setUpdateBy(context.getOperator()); + appVersion.putAttributes(new HashMap<>()); + this.appVersionService.update(appVersion); + return Rsp.ok(this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); } @Override @@ -847,39 +265,8 @@ public Rsp updateConfig(String appId, AppBuilderConfigDto conf @Fitable(id = "default") public Rsp saveConfig(String appId, AppBuilderSaveConfigDto appBuilderSaveConfigDto, OperationContext context) { - List formProperties = appBuilderSaveConfigDto.getInput() - .stream() - .map(formPropertyDto -> AppBuilderFormProperty.builder() - .id(formPropertyDto.getId()) - .formId(FORM_PROPERTY_GROUP_NULL) - .name(formPropertyDto.getName()) - .dataType(formPropertyDto.getDataType()) - .defaultValue(formPropertyDto.getDefaultValue()) - .from(formPropertyDto.getFrom()) - .group(formPropertyDto.getGroup()) - .description(formPropertyDto.getDescription()) - .build()) - .collect(Collectors.toList()); - AppBuilderApp appBuilderApp = this.appFactory.create(appId); - this.updateAttributes(appBuilderApp, new HashMap<>()); - this.appFactory.update(appBuilderApp); - appBuilderApp.getFormPropertyRepository().updateMany(formProperties); - - appBuilderApp.getFlowGraph().setAppearance(appBuilderSaveConfigDto.getGraph()); - appBuilderApp.getFlowGraphRepository().updateOne(appBuilderApp.getFlowGraph()); - return Rsp.ok(this.buildFullAppDto(appBuilderApp)); - } - - private void updateConfigAndForm(AppBuilderConfigDto configDto, OperationContext context, - AppBuilderConfig oldConfig, LocalDateTime operateTime, AppBuilderApp oldApp) { - oldConfig.setUpdateBy(context.getOperator()); - oldConfig.setUpdateAt(operateTime); - oldApp.getConfigRepository().updateOne(oldConfig); - oldConfig.getForm().setUpdateBy(context.getOperator()); - oldConfig.getForm().setUpdateAt(operateTime); - oldConfig.getForm().setName(configDto.getForm().getName()); - oldConfig.getForm().setAppearance(configDto.getForm().getAppearance()); - oldApp.getFormRepository().updateOne(oldConfig.getForm()); + AppVersion appVersion = this.appVersionService.update(appId, appBuilderSaveConfigDto, context); + return Rsp.ok(this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); } @Override @@ -887,422 +274,85 @@ private void updateConfigAndForm(AppBuilderConfigDto configDto, OperationContext @Fitable(id = "default") public Rsp updateFlowGraph(String appId, AppBuilderFlowGraphDto graphDto, OperationContext context) { - this.appUpdateValidator.validate(appId); - LocalDateTime operateTime = LocalDateTime.now(); - - AppBuilderApp oldApp = this.appFactory.create(appId); - Span.current().setAttribute("name", oldApp.getName()); - // 优先更新graph本身 - AppBuilderFlowGraph oldAppFlowGraph = oldApp.getFlowGraph(); - oldAppFlowGraph.setUpdateAt(operateTime); - oldAppFlowGraph.setUpdateBy(context.getOperator()); - oldAppFlowGraph.setName(graphDto.getName()); - oldAppFlowGraph.setAppearance(JSONObject.toJSONString(graphDto.getAppearance())); - oldApp.getFlowGraphRepository().updateOne(oldAppFlowGraph); - // 根据graph更新config - String appearance = oldAppFlowGraph.getAppearance(); - AppBuilderConfig oldConfig = oldApp.getConfig(); - List oldFormProperties = oldApp.getFormProperties(); - this.updateConfigByGlowGraphAppearance(appearance, oldFormProperties, oldConfig); // 这个方法是在更新properties - oldConfig.setUpdateAt(operateTime); - oldConfig.setUpdateBy(context.getOperator()); - oldApp.getConfigRepository().updateOne(oldConfig); - // 最后更新app主表 - oldApp.setUpdateAt(operateTime); - oldApp.setUpdateBy(context.getOperator()); - this.updateAttributes(oldApp, new HashMap<>()); - this.appFactory.update(oldApp); - return Rsp.ok(this.buildFullAppDto(oldApp)); + AppVersion appVersion = this.appVersionService.update(appId, graphDto, context); + return Rsp.ok(this.converterFactory.convert(appVersion, AppBuilderAppDto.class)); } @Override @Fitable(id = "default") public void delete(String appId, OperationContext context) { - AppBuilderApp app = this.validateApp(appId); - MetaFilter filter = new MetaFilter(); - try { - String metaId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - filter.setMetaIds(Collections.singletonList(metaId)); - List metas = MetaUtils.getListMetaHandle(this.metaService, filter, context); - if (CollectionUtils.isEmpty(metas)) { - return; - } - List metaVersionIds = - metas.stream().map(Meta::getVersionId).distinct().collect(Collectors.toList()); - List versionIdInstances = this.getVersionIdInstanceIds(metaVersionIds, context); - Set instanceIds = this.getInstanceIds(versionIdInstances); - List appIds = this.getFullAppIds(metas); - this.deleteApps(appIds); - this.deleteMetaInstances(versionIdInstances, context); - this.deleteMetas(metaVersionIds, context); - this.uploadedFileManageService.cleanAippFiles(Collections.singletonList(appId)); - this.deleteLogs(instanceIds); - this.deleteFlows(metas, context); - this.deleteStore(metas, AppCategory.findByType(app.getType()).orElse(null)); - this.deleteChats(metaId); - this.deleteUsrAppCollection(appId); - } catch (AippTaskNotFoundException exception) { - throw new AippException(APP_DELETE_FAILED); - } - } - - private void deleteChats(String metaId) { - this.aippChatMapper.deleteAppByAippId(metaId); - } - - private void deleteUsrAppCollection(String appId) { - this.usrAppCollectionService.deleteByAppId(appId); - } - - private void deleteStore(List metas, AppCategory type) { - if (type == null) { - return; - } - List uniqueNames = metas.stream() - .filter(meta -> meta != null && meta.getAttributes().containsKey(ATTR_UNIQUE_NAME)) - .map(meta -> ObjectUtils.cast(meta.getAttributes().get(ATTR_UNIQUE_NAME))) - .distinct() - .toList(); - if (type == AppCategory.WATER_FLOW) { - List pluginTools = this.pluginToolService.getPluginTools(uniqueNames); - pluginTools.forEach(pluginTool -> this.pluginService.deletePlugin(pluginTool.getPluginId())); - } else { - uniqueNames.forEach(this.appService::deleteApp); - } - } - - private void deleteFlows(List metas, OperationContext context) { - metas.forEach(meta -> this.flowsService.deleteFlowsWithoutElsa(meta.getId(), meta.getVersion(), context)); - } - - private void deleteLogs(Set instanceIds) { - if (instanceIds.isEmpty()) { - return; - } - this.aippLogMapper.deleteByInstanceIds(new ArrayList<>(instanceIds)); - } - - private Set getInstanceIds(List versionIdInstances) { - Set instanceIds = new HashSet<>(); - for (Tuple versionIdInstance : versionIdInstances) { - List instances = ObjectUtils.cast(versionIdInstance.get(1) - .orElseThrow(() -> new AippException(AippErrCode.DELETE_ERROR))); - if (CollectionUtils.isEmpty(instances)) { - continue; - } - instanceIds.addAll(instances.stream().map(Instance::getId).distinct().collect(Collectors.toList())); - } - return instanceIds; - } - - private List getVersionIdInstanceIds(List metaVersionIds, OperationContext context) { - return metaVersionIds.stream() - .map(versionId -> Tuple.duet(versionId, - MetaInstanceUtils.getOneInstance(versionId, this.metaInstanceService, context))) - .collect(Collectors.toList()); - } - - private void deleteApps(List appIds) { - this.appFactory.delete(appIds); - } - - private List getFullAppIds(List metas) { - return metas.stream() - .filter(meta -> meta != null && meta.getAttributes().containsKey(AippConst.ATTR_APP_ID_KEY)) - .map(meta -> ObjectUtils.cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY))) - .distinct() - .collect(Collectors.toList()); - } - - private void deleteMetaInstances(List versionIdInstances, OperationContext context) { - versionIdInstances.forEach(versionIdInstance -> this.deleteMetaInstance(context, versionIdInstance)); - } - - private void deleteMetaInstance(OperationContext context, Tuple versionIdInstance) { - String versionId = ObjectUtils.cast(versionIdInstance.get(0) - .orElseThrow(() -> new AippException(AippErrCode.DELETE_ERROR))); - List instances = ObjectUtils.cast(versionIdInstance.get(1) - .orElseThrow(() -> new AippException(AippErrCode.DELETE_ERROR))); - if (CollectionUtils.isEmpty(instances)) { - return; - } - instances.forEach(instance -> this.metaInstanceService.deleteMetaInstance(versionId, - instance.getId(), - context)); - } - - private void deleteMetas(List metaVersionIds, OperationContext context) { - metaVersionIds.forEach(versionId -> this.metaService.delete(versionId, context)); + this.appDomainService.deleteByAppId(appId, context); } @Override public PublishedAppResDto published(String uniqueName, OperationContext context) { - MetaFilter filter = new MetaFilter(); - Map> attributes = new HashMap<>(); - attributes.put(ATTR_UNIQUE_NAME, Collections.singletonList(uniqueName)); - filter.setAttributes(attributes); - RangedResultSet metas = this.metaService.list(filter, true, 0, 1, context); - if (metas.getResults().isEmpty()) { - log.error("Meta can not be null."); - throw new AippParamException(QUERY_PUBLICATION_HISTORY_FAILED); - } - Meta meta = metas.getResults().get(0); - String appId = String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - String publishedDescription = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_DESCRIPTION)); - String publishedUpdateLog = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_UPDATE_LOG)); - return PublishedAppResDto.builder() - .appId(appId) - .appVersion(meta.getVersion()) - .publishedAt(meta.getCreationTime()) - .publishedBy(meta.getCreator()) - .publishedDescription(publishedDescription) - .publishedUpdateLog(publishedUpdateLog) - .build(); - } - - @Override - public AppExportDto export(String appId, OperationContext context) { - AppBuilderApp app = this.appFactory.create(appId); - if (!StringUtils.equals(app.getCreateBy(), context.getName())) { - throw new AippException(AippErrCode.EXPORT_CONFIG_UNAUTHED); - } - - // 校验流程编排合法性 - try { - AppBuilderAppDto appDto = this.buildFullAppDto(app); - String flowDefinitionData = - this.aippFlowDefinitionService.getParsedGraphData(JsonUtils.toJsonString(appDto.getFlowGraph() - .getAppearance()), app.getVersion()); - this.flowDefinitionService.validateDefinitionData(flowDefinitionData); - } catch (Exception e) { - log.error("app config export failed", e); - throw new AippException(AippErrCode.EXPORT_INVALID_FLOW_EXCEPTION); - } - - try { - AppExportApp exportAppInfo = AppImExportUtil.convertToAppExportApp(app); - - String appType = this.appTypeService.query(app.getAppType(), context.getTenantId()).getName(); - exportAppInfo.setAppType(appType); - String icon = ObjectUtils.cast(app.getAttributes().get("icon")); - if (StringUtils.isNotBlank(icon)) { - exportAppInfo.getAttributes() - .put("icon", this.getIconAttributes(AippFileUtils.getFileNameFromIcon(icon))); - } - AppBuilderConfig appBuilderConfig = app.getConfig(); - appBuilderConfig.setApp(app); - AppExportConfig exportAppConfig = AppImExportUtil.convertToAppExportConfig(appBuilderConfig); - AppExportFlowGraph exportFlowGraph = AppImExportUtil.convertToAppExportFlowGraph(app.getFlowGraph()); - - return AppExportDto.builder() - .version(this.exportMeta.get("version")) - .app(exportAppInfo) - .config(exportAppConfig) - .flowGraph(exportFlowGraph) - .build(); - } catch (DataAccessException e) { - log.error("app config export failed", e); - throw new AippException(AippErrCode.EXPORT_CONFIG_DB_EXCEPTION); - } - } - - private Map getIconAttributes(String iconPath) { - try { - File iconFile = FileUtils.canonicalize(iconPath); - Map iconAttr = MapBuilder.get().build(); - byte[] iconBytes = AppImExportUtil.readAllBytes(Files.newInputStream(iconFile.toPath())); - iconAttr.put("content", Base64.getEncoder().encodeToString(iconBytes)); - iconAttr.put("type", AppImExportUtil.extractIconExtension(iconFile.getName())); - return iconAttr; - } catch (IllegalStateException | IOException e) { - return MapBuilder.get().put("content", StringUtils.EMPTY).build(); - } - } - - @Override - @Transactional - public AppBuilderAppDto importApp(String appConfig, OperationContext context) { - try { - AppExportDto appExportDto = new ObjectMapper().readValue(appConfig, AppExportDto.class); - if (!StringUtils.equals(appExportDto.getVersion(), this.exportMeta.get("version"))) { - throw new AippException(AippErrCode.IMPORT_CONFIG_UNMATCHED_VERSION, - this.exportMeta.get("version"), - appExportDto.getVersion()); - } - AppImExportUtil.checkAppExportDto(appExportDto); - String initAppName = appExportDto.getApp().getName(); - List similarNames = this.appRepository.selectWithSimilarName(initAppName); - String newName = AppImExportUtil.generateNewAppName(similarNames, initAppName); - this.validateAppName(newName, context); - appExportDto.getApp().setName(newName); - appExportDto.getApp().getAttributes().put("name", newName); - String appTypeId = this.createAppType(appExportDto.getApp().getAppType(), context.getTenantId()); - appExportDto.getApp().setAppType(appTypeId); - - Object iconAttr = appExportDto.getApp().getAttributes().get("icon"); - AppBuilderApp templateApp = AppImExportUtil.convertToAppBuilderApp(appExportDto, context); - this.appFactory.setRepositories(templateApp); - AppBuilderAppDto appDto = - this.createAppWithTemplate(null, templateApp, context, false, templateApp.getType(), true); - String iconContent = - iconAttr instanceof Map ? ObjectUtils.cast(ObjectUtils.>cast(iconAttr) - .get("content")) : StringUtils.EMPTY; - if (StringUtils.isBlank(iconContent)) { - return appDto; - } - String iconExtension = ObjectUtils.cast(ObjectUtils.>cast(iconAttr).get("type")); - String iconPath = AppImExportUtil.saveIconFile(iconContent, iconExtension, context.getTenantId(), - this.contextRoot); - if (StringUtils.isBlank(iconPath)) { - return appDto; - } - AppBuilderApp update = this.appFactory.create(appDto.getId()); - update.getAttributes().put("icon", iconPath); - this.appFactory.update(update); - this.uploadedFileManageService.addFileRecord(update.getId(), - context.getAccount(), - AippFileUtils.getFileNameFromIcon(iconPath), - Entities.generateId()); - return this.buildFullAppDto(update); - } catch (JsonProcessingException e) { - log.error("Imported config file is not json", e); - throw new AippException(AippErrCode.IMPORT_CONFIG_NOT_JSON, - e.getLocation().getLineNr(), - e.getLocation().getColumnNr()); - } - } - - private String createAppType(String appTypeName, String tenantId) { - Optional createdId = this.appTypeService.queryAll(tenantId) - .stream() - .filter(dto -> StringUtils.equals(dto.getName(), appTypeName)) - .map(AppTypeDto::getId) - .findAny(); - return createdId.orElseGet(() -> this.appTypeService.add(AppTypeDto.builder().name(appTypeName).build(), - tenantId).getId()); - } - - private String copyIconFiles(String icon, String aippId, String operator) throws IOException { - File originIcon = FileUtils.canonicalize(AippFileUtils.getFileNameFromIcon(icon)); - String originIconName = originIcon.getName(); - String copiedIconName = UUID.randomUUID() + FileUtils.extension(originIconName); - File copiedIcon = FileUtils.canonicalize(originIcon.getCanonicalPath().replace(originIconName, copiedIconName)); - IoUtils.copy(originIcon, copiedIcon); - this.uploadedFileManageService.addFileRecord(aippId, - operator, - copiedIcon.getCanonicalPath(), - Entities.generateId()); - return icon.replace(originIconName, copiedIconName); + return this.appTaskService.getLatest(uniqueName, context) + .map(appTask -> PublishedAppResDto.builder() + .appId(appTask.getEntity().getAppId()) + .appVersion(appTask.getEntity().getVersion()) + .publishedAt(appTask.getEntity().getCreationTime()) + .publishedBy(appTask.getEntity().getCreator()) + .publishedDescription(appTask.getEntity().getPublishDescription()) + .publishedUpdateLog(appTask.getEntity().getPublishLog()) + .build()) + .orElseThrow(() -> new AippParamException(QUERY_PUBLICATION_HISTORY_FAILED)); } @Override @Transactional public TemplateInfoDto publishTemplateFromApp(TemplateAppCreateDto createDto, OperationContext context) { - this.validateAppName(createDto.getName(), context); - AppBuilderApp app = this.appFactory.create(createDto.getId()); - AppTemplate newTemplate = TemplateUtils.convertToAppTemplate(app); - this.templateFactory.setRepositories(newTemplate); - return this.createTemplateFromApp(createDto, newTemplate, context); - } - - private TemplateInfoDto createTemplateFromApp(TemplateAppCreateDto dto, AppTemplate template, - OperationContext context) { - AppBuilderFlowGraph flowGraph = template.getFlowGraph(); - flowGraph.setId(Entities.generateId()); - List formProperties = template.getFormProperties(); - AppBuilderConfig config = resetConfig(formProperties, template.getConfig()); - template.setId(Entities.generateId()); - template.setAttributes(this.resetTemplateAttributes(template.getAttributes())); - config.setAppId(template.getId()); - template.setConfigId(config.getId()); - template.setFlowGraphId(flowGraph.getId()); - if (dto != null) { - template.setName(dto.getName()); - template.setAppType(dto.getAppType()); - template.getAttributes().put(TemplateUtils.DESCRIPTION_ATTR_KEY, dto.getDescription()); - String icon = ObjectUtils.cast(template.getAttributes().get(TemplateUtils.ICON_ATTR_KEY)); - if (StringUtils.isNotBlank(icon) && StringUtils.equals(icon, dto.getIcon())) { - try { - String copiedIcon = this.copyIconFiles(icon, template.getId(), context.getAccount()); - template.getAttributes().put(TemplateUtils.ICON_ATTR_KEY, copiedIcon); - } catch (IOException e) { - log.warn("Failed to create a copy of icon when publish.", e); - template.getAttributes().put(TemplateUtils.ICON_ATTR_KEY, StringUtils.EMPTY); - } - } - } - resetOperatorAndTime(template, LocalDateTime.now(), context.getOperator()); - this.templateFactory.save(template); - String icon = ObjectUtils.cast(template.getAttributes().get(TemplateUtils.ICON_ATTR_KEY)); - if (StringUtils.isNotBlank(icon)) { - this.uploadedFileManageService.updateRecord(template.getId(), - AippFileUtils.getFileNameFromIcon(icon), - IRREMOVABLE); - } - - return TemplateUtils.convertToTemplateDto(template); - } - - private Map resetTemplateAttributes(Map attributes) { - Map resetedAttr = MapBuilder.get().build(); - Arrays.stream(TEMPLATE_DEFAULT_ATTRIBUTE_KEYS).forEach(attr -> resetedAttr.put(attr, attributes.get(attr))); - return resetedAttr; + this.appVersionService.validateAppName(createDto.getName(), context); + AppVersion appVersion = this.appVersionService.retrieval(createDto.getId()); + return appVersion.publishTemplate(createDto, context); } @Override + @Transactional public AppBuilderAppDto createAppByTemplate(TemplateAppCreateDto createDto, OperationContext context) { - this.validateAppName(createDto.getName(), context); AppTemplate template = this.templateFactory.create(createDto.getId()); - AppBuilderApp appTemplate = TemplateUtils.convertToAppBuilderApp(template); - this.appFactory.setRepositories(appTemplate); - AppBuilderAppCreateDto dto = this.buildAppBuilderAppCreateDto(appTemplate); - dto.setName(createDto.getName()); - dto.setType(AppTypeEnum.APP.code()); - dto.setDescription(createDto.getDescription()); - dto.setAppType(createDto.getAppType()); - String icon = ObjectUtils.cast(appTemplate.getAttributes().get(TemplateUtils.ICON_ATTR_KEY)); + template.setName(createDto.getName()); + template.setDescription(createDto.getDescription()); + template.setAppType(createDto.getAppType()); + String icon = template.getIcon(); if (StringUtils.isNotBlank(icon) && StringUtils.equals(icon, createDto.getIcon())) { try { - String copiedIcon = this.copyIconFiles(icon, null, context.getAccount()); - dto.setIcon(copiedIcon); + String copiedIcon = this.uploadedFileManageService.copyIconFiles(icon, null, context.getAccount()); + template.setIcon(copiedIcon); } catch (IOException e) { log.warn("Failed to create a copy of icon when create app.", e); - dto.setIcon(StringUtils.EMPTY); + template.setIcon(StringUtils.EMPTY); } } else { - dto.setIcon(createDto.getIcon()); + template.setIcon(createDto.getIcon()); } - return this.createAppWithTemplate(dto, appTemplate, context, false, AppTypeEnum.APP.code(), false); + return this.converterFactory.convert(this.appVersionService.createByTemplate(template, context), + AppBuilderAppDto.class); } @Override + @Transactional public void deleteTemplate(String templateId, OperationContext context) { this.templateFactory.delete(templateId); this.uploadedFileManageService.cleanAippFiles(Collections.singletonList(templateId)); } @Override - public List recentPublished(AppQueryCondition cond, long offset, int limit, String appId, + public RangedResultSet recentPublished(AppQueryCondition cond, long offset, int limit, String appId, OperationContext context) { - this.validateApp(appId); - try { - String aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - List allPublishedMeta = MetaUtils.getAllPublishedMeta(this.metaService, aippId, context) - .stream() - .filter(meta -> !this.isAppBelong(appId, meta)) - .collect(Collectors.toList()); - List appIds = allPublishedMeta.stream() - .map(meta -> String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY))) - .collect(Collectors.toList()); - cond.setIds(appIds); - cond.setTenantId(context.getTenantId()); - List allPublishedApp = this.appRepository.selectWithCondition(cond); - Map appIdKeyAppValueMap = - allPublishedApp.stream().collect(Collectors.toMap(AppBuilderApp::getId, Function.identity())); - return this.buildPublishedAppResDtos(allPublishedMeta, appIdKeyAppValueMap); - } catch (AippTaskNotFoundException exception) { - throw new AippException(QUERY_PUBLICATION_HISTORY_FAILED); - } + AppVersion appVersion = this.appVersionService.retrieval(appId); + String aippId = appVersion.getData().getAppSuiteId(); + List publishedTasks = this.appTaskService.getPublishedByPage(aippId, offset, limit, context) + .stream() + .toList(); + Map appIdKeyAppVersionMap = publishedTasks.stream() + .map(appTask -> this.appVersionService.retrieval(appTask.getEntity().getAppId())) + .collect(Collectors.toMap(version -> version.getData().getAppId(), Function.identity())); + List appBuilderAppDtos = publishedTasks.stream() + .map(t -> AppTaskUtils.toPublishedAppBuilderAppDto(t, + appIdKeyAppVersionMap.get(t.getEntity().getAppId()), + this.converterFactory)) + .toList(); + return RangedResultSet.create(appBuilderAppDtos, offset, limit, appBuilderAppDtos.size()); } @Override @@ -1315,725 +365,13 @@ public List checkAvailable(List appCheckDtos, Operatio return results.stream().filter(result -> !result.isValid()).collect(Collectors.toList()); } - private boolean isAppBelong(String appId, Meta meta) { - return Objects.equals(String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)), appId); - } - - private List buildPublishedAppResDtos(List metas, - Map appIdKeyAppValueMap) { - return metas.stream() - .map(meta -> this.buildPublishedAppResDto(meta, appIdKeyAppValueMap)) - .collect(Collectors.toList()); - } - - private PublishedAppResDto buildPublishedAppResDto(Meta meta, Map appIdKeyAppValueMap) { - String appId = String.valueOf(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - String publishedDescription = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_DESCRIPTION)); - String publishedUpdateLog = String.valueOf(meta.getAttributes().get(AippConst.ATTR_PUBLISH_UPDATE_LOG)); - AppBuilderApp app = appIdKeyAppValueMap.get(appId); - return PublishedAppResDto.builder() - .appId(appId) - .appVersion(app.getVersion()) - .publishedAt(meta.getCreationTime()) - .publishedBy(meta.getCreator()) - .publishedDescription(publishedDescription) - .publishedUpdateLog(publishedUpdateLog) - .build(); - } - - private static AppBuilderConfig resetConfig(List formProperties, AppBuilderConfig config) { - AppBuilderForm form = config.getForm(); - // 这里先根据旧的formId查询得到formProperties - Map idToFormPropertyMap = - formProperties.stream().collect(Collectors.toMap(AppBuilderFormProperty::getId, Function.identity())); - // 先根据旧的configId查询得到configProperties - List configProperties = config.getConfigProperties(); - config.setId(Entities.generateId()); - configProperties.forEach(configProperty -> resetIdToConfigAndFormProperty(configProperty, - idToFormPropertyMap, - form.getId(), - config.getId())); - config.setFormId(form.getId()); - return config; - } - - private static void resetIdToConfigAndFormProperty(AppBuilderConfigProperty configProperty, - Map idToFormPropertyMap, String formId, String configId) { - configProperty.setId(Entities.generateId()); - configProperty.setConfigId(configId); - AppBuilderFormProperty formProperty = idToFormPropertyMap.get(configProperty.getFormPropertyId()); - if (formProperty == null) { - return; - } - formProperty.setId(Entities.generateId()); - formProperty.setFormId(formId); - configProperty.setFormPropertyId(formProperty.getId()); - } - - private static void resetOperatorAndTime(AppBuilderApp app, LocalDateTime time, String operator) { - app.setCreateBy(operator); - app.setCreateAt(time); - app.setUpdateBy(operator); - app.setUpdateAt(time); - resetOperatorAndTimeForConfig(app.getConfig(), time, operator); - resetOperatorAndTimeForFlowGraph(app.getFlowGraph(), time, operator); - } - - private static void resetOperatorAndTime(AppTemplate template, LocalDateTime time, String operator) { - template.setCreateBy(operator); - template.setCreateAt(time); - template.setUpdateBy(operator); - template.setUpdateAt(time); - resetOperatorAndTimeForConfig(template.getConfig(), time, operator); - resetOperatorAndTimeForFlowGraph(template.getFlowGraph(), time, operator); - } - - private static void resetOperatorAndTimeForConfig(AppBuilderConfig config, LocalDateTime time, String operator) { - config.setCreateBy(operator); - config.setCreateAt(time); - config.setUpdateBy(operator); - config.setUpdateAt(time); - AppBuilderForm form = config.getForm(); - form.setCreateBy(operator); - form.setCreateAt(time); - form.setUpdateBy(operator); - form.setUpdateAt(time); - } - - private static void resetOperatorAndTimeForFlowGraph(AppBuilderFlowGraph flowGraph, LocalDateTime time, - String operator) { - flowGraph.setCreateBy(operator); - flowGraph.setCreateAt(time); - flowGraph.setUpdateBy(operator); - flowGraph.setUpdateAt(time); - } - - private void saveNewAppBuilderApp(AppBuilderApp appBuilderApp) { - // 保存app - this.appFactory.save(appBuilderApp); - - String icon = ObjectUtils.cast(appBuilderApp.getAttributes().get("icon")); - if (StringUtils.isNotBlank(icon)) { - this.uploadedFileManageService.updateRecord(appBuilderApp.getId(), - AippFileUtils.getFileNameFromIcon(icon), - IRREMOVABLE); - } - - appBuilderApp.getConfigRepository().insertOne(appBuilderApp.getConfig()); - appBuilderApp.getFlowGraphRepository().insertOne(appBuilderApp.getFlowGraph()); - appBuilderApp.getConfigPropertyRepository().insertMore(appBuilderApp.getConfig().getConfigProperties()); - List formProperties = appBuilderApp.getFormProperties(); - formProperties.forEach(property -> { - property.setAppId(appBuilderApp.getId()); - }); - appBuilderApp.getFormPropertyRepository().insertMore(formProperties); - } - - private AppBuilderAppDto buildFullAppDto(AppBuilderApp app) { - AppBuilderAppDto.AppBuilderAppDtoBuilder appDtoBuilder = AppBuilderAppDto.builder() - .id(app.getId()) - .name(app.getName()) - .type(app.getType()) - .state(app.getState()) - .appType(app.getAppType()) - .attributes(app.getAttributes()) - .version(app.getVersion()) - .appCategory(app.getAppCategory()) - .createBy(app.getCreateBy()) - .updateBy(app.getUpdateBy()) - .createAt(app.getCreateAt()) - .updateAt(app.getUpdateAt()) - .config(this.buildAppBuilderConfig(app.getConfig())) - .flowGraph(this.buildFlowGraph(app.getFlowGraph())) - .appBuiltType(app.getAppBuiltType()) - .configFormProperties(this.buildAppBuilderConfigFormProperties(app.getFormProperties())); - Optional.ofNullable(app.getPath()) - .filter(path -> !path.isEmpty()) - .ifPresent(path -> appDtoBuilder.chatUrl(String.format("/chat/%s", path))); - return appDtoBuilder.build(); - } - - private AppBuilderFlowGraphDto buildFlowGraph(AppBuilderFlowGraph flowGraph) { - return AppBuilderFlowGraphDto.builder() - .id(flowGraph.getId()) - .name(flowGraph.getName()) - .appearance(JsonUtils.parseObject(flowGraph.getAppearance())) - .createBy(flowGraph.getCreateBy()) - .updateBy(flowGraph.getUpdateBy()) - .createAt(flowGraph.getCreateAt()) - .updateAt(flowGraph.getUpdateAt()) - .build(); - } - - private AppBuilderConfigDto buildAppBuilderConfig(AppBuilderConfig config) { - return AppBuilderConfigDto.builder() - .id(config.getId()) - .tenantId(config.getTenantId()) - .createBy(config.getCreateBy()) - .updateBy(config.getUpdateBy()) - .createAt(config.getCreateAt()) - .updateAt(config.getUpdateAt()) - .form(this.buildAppBuilderConfigFormDto(config)) - .build(); - } - - private AppBuilderConfigFormDto buildAppBuilderConfigFormDto(AppBuilderConfig config) { - Validation.notNull(config.getForm(), "Form can not be null."); - return AppBuilderConfigFormDto.builder() - .id(config.getFormId()) - .name(config.getForm().getName()) - .appearance(config.getForm().getAppearance()) - .build(); - } - - private List buildAppBuilderConfigFormProperties( - List formProperties) { - LinkedHashMap formPropertyMapping = formProperties.stream() - .map(AppBuilderFormProperty::toAppBuilderConfigFormPropertyDto) - .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getName, - Function.identity(), - (k1, k2) -> k1, - LinkedHashMap::new)); - String root = ""; - for (Map.Entry entry : formPropertyMapping.entrySet()) { - AppBuilderConfigFormPropertyDto dto = entry.getValue(); - String group = entry.getValue().getGroup(); - if (group.equals(FORM_PROPERTY_GROUP_NULL)) { - root = dto.getName(); - } else { - group = dto.getGroup(); - AppBuilderConfigFormPropertyDto parent = formPropertyMapping.get(group); - if (parent == null) { - throw new AippException(AippErrCode.FORM_PROPERTY_PARENT_NOT_EXIST); - } - parent.addChild(dto); - } - } - AppBuilderConfigFormPropertyDto rootProperty = formPropertyMapping.get(root); - return rootProperty == null ? Collections.emptyList() : Collections.singletonList(rootProperty); - } - - private void updateConfigPropertiesByAppBuilderConfigDto(String appId, - List newProperties, AppBuilderConfig oldConfig, - List oldFormProperties) { - Map newIdToPropertyDtoMap = newProperties.stream() - .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getId, Function.identity())); - List oldConfigProperties = oldConfig.getConfigProperties(); // 这个对象里全是id,所以是不会改动的 - Set oldFormPropertyIds = - oldFormProperties.stream().map(AppBuilderFormProperty::getId).collect(Collectors.toSet()); - - // 删除 - this.deleteProperties(oldConfig, oldConfigProperties, newIdToPropertyDtoMap, oldFormProperties); - - // 新增 - this.addProperties(appId, oldConfig, newProperties, oldFormPropertyIds); - - // 修改, 待修改的内容, 循环修改 - oldFormProperties.stream() - .filter(formProperty -> newIdToPropertyDtoMap.containsKey(formProperty.getId())) - .forEach(formProperty -> { - AppBuilderConfigFormPropertyDto propertyDto = newIdToPropertyDtoMap.get(formProperty.getId()); - formProperty.setName(propertyDto.getName()); - formProperty.setDataType(propertyDto.getDataType()); - formProperty.setDefaultValue(propertyDto.getDefaultValue()); - oldConfig.getForm().getFormPropertyRepository().updateOne(formProperty); - }); - } - - private void addProperties(String appId, AppBuilderConfig config, List properties, - Set formPropertyIds) { - List toAddConfigProperties = properties.stream() - .filter(pd -> StringUtils.isBlank(pd.getId()) || !formPropertyIds.contains(pd.getId())) - .map(propertyDto -> { - AppBuilderFormProperty formProperty = AppBuilderFormProperty.builder() - .formId(config.getFormId()) - .name(propertyDto.getName()) - .dataType(propertyDto.getDataType()) - .defaultValue(propertyDto.getDefaultValue()) - .id(Entities.generateId()) - .appId(appId) - .build(); - return AppBuilderConfigProperty.builder() - .id(Entities.generateId()) - .configId(config.getId()) - .nodeId(propertyDto.getNodeId()) - .formPropertyId(formProperty.getId()) - .formProperty(formProperty) - .build(); - }) - .collect(Collectors.toList()); - List toAddFormProperties = toAddConfigProperties.stream() - .map(AppBuilderConfigProperty::getFormProperty) - .collect(Collectors.toList()); - - config.getConfigPropertyRepository().insertMore(toAddConfigProperties); - config.getForm().getFormPropertyRepository().insertMore(toAddFormProperties); - } - - private void deleteProperties(AppBuilderConfig config, List configProperties, - Map idToPropertyDtoMap, - List formProperties) { - List toDeleteConfigPropertyIds = configProperties.stream() - .filter(cp -> !idToPropertyDtoMap.containsKey(cp.getFormPropertyId())) - .map(AppBuilderConfigProperty::getId) - .collect(Collectors.toList()); - List toDeleteFormPropertyIds = formProperties.stream() - .map(AppBuilderFormProperty::getId) - .filter(id -> !idToPropertyDtoMap.containsKey(id)) - .collect(Collectors.toList()); - config.getConfigPropertyRepository().deleteMore(toDeleteConfigPropertyIds); - config.getForm().getFormPropertyRepository().deleteMore(toDeleteFormPropertyIds); - } - - private String updateFlowGraphAppearanceByConfigDto(String oldAppearance, - List formProperties) { - // 将dto的properties转成 {nodeId : {name:value, name:value}, ... }形式 - Map> nodeIdToPropertyNameValueMap = formProperties.stream() - .filter(fp -> StringUtils.isNotBlank(fp.getNodeId())) - .collect(Collectors.groupingBy(AppBuilderConfigFormPropertyDto::getNodeId)) - .entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, - entry -> entry.getValue() - .stream() - .collect(Collectors.toMap(AppBuilderConfigFormPropertyDto::getName, - appBuilderConfigFormPropertyDto -> JsonUtils.toJsonString( - appBuilderConfigFormPropertyDto.getDefaultValue()))))); - JSONObject oldAppearanceObject = JSONObject.parseObject(oldAppearance); - JSONObject page = ObjectUtils.cast(oldAppearanceObject.getJSONArray("pages").get(0)); - JSONArray shapes = page.getJSONArray("shapes"); - - for (int j = 0; j < shapes.size(); j++) { - JSONObject node = shapes.getJSONObject(j); - String id = node.getString("id"); - String type = node.getString("type"); - if (!StringUtils.equals(type, "startNodeStart") && !type.endsWith("NodeState")) { - continue; - } - - Map nameValue = nodeIdToPropertyNameValueMap.get(id); - - String flowMetaString = node.get("flowMeta").toString(); - - ObjectMapper mapper = new ObjectMapper(); - JsonNode flowMeta = null; - try { - flowMeta = mapper.readTree(flowMetaString); - JsonNode params = flowMeta.findPath("inputParams"); - for (int i = 0; i < params.size(); i++) { - JsonNode child = params.get(i); - processParam(child, nameValue); - } - } catch (IOException e) { - e.printStackTrace(); - } - Object tt = JSON.parse(flowMeta.toString()); - node.put("flowMeta", tt); - } - - return JSONObject.toJSONString(oldAppearanceObject); - } - - private void processParam(JsonNode node, Map params) { - List singleLayerParams = new ArrayList<>(Arrays.asList("model", "temperature", "systemPrompt")); - List doubleLayerParams = new ArrayList<>(Arrays.asList("tools", "workflows")); - if (params == null) { - return; - } - for (Map.Entry param : params.entrySet()) { - handleParam(node, param, singleLayerParams, doubleLayerParams); - } - } - - private void handleParam(JsonNode node, Map.Entry param, List singleLayerParams, - List doubleLayerParams) { - if (StringUtils.equals(node.get("name").asText(), param.getKey())) { - if (singleLayerParams.contains(param.getKey())) { - this.handleParamTemperature(node, param); - return; - } - - if (doubleLayerParams.contains(param.getKey())) { - ArrayNode valueArrayNode = convertList(param.getValue()); - ObjectUtils.cast(node).set("value", valueArrayNode); - return; - } - - if (StringUtils.equals("knowledge", param.getKey())) { - this.handleParamKnowledge(node, param); - return; - } - - if (StringUtils.equals("memory", param.getKey())) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - ArrayNode valueArrayNode = nodeFactory.arrayNode(); - Map res = JsonUtils.parseObject(param.getValue(), Map.class); - if (Objects.equals(res.get("type"), "UserSelect")) { - this.parseUserSelect(res, valueArrayNode); - } else { - this.parseOtherMemoryType(res, valueArrayNode); - } - ObjectUtils.cast(node).set("value", valueArrayNode); - } - } - } - - private void handleParamTemperature(JsonNode node, Map.Entry param) { - if (StringUtils.equals(param.getKey(), "temperature")) { - ObjectUtils.cast(node).put("value", JsonUtils.parseObject(param.getValue(), Float.class)); - } else { - ObjectUtils.cast(node).put("value", JsonUtils.parseObject(param.getValue(), String.class)); - } - } - - private void handleParamKnowledge(JsonNode node, Map.Entry param) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - ArrayNode valueArrayNode = nodeFactory.arrayNode(); - List> res = - ObjectUtils.>>cast(JsonUtils.parseObject(param.getValue(), List.class)); - res.forEach(r -> { - ArrayNode valueArrayNode1 = nodeFactory.arrayNode(); - for (Map.Entry rr : r.entrySet()) { - if (StringUtils.equals(rr.getKey(), "id")) { - valueArrayNode1.add(convertId(rr.getKey(), ObjectUtils.cast(rr.getValue()).longValue())); - } else { - valueArrayNode1.add(convertObject(rr.getKey(), String.valueOf(rr.getValue()))); - } - } - Map a = new HashMap<>(); - a.put("id", UUID.randomUUID().toString()); - a.put("type", "Object"); - a.put("from", "Expand"); - a.put("value", valueArrayNode1); - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : a.entrySet()) { - if (StringUtils.equals(entry.getKey(), "value")) { - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } else { - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } - } - valueArrayNode.add(mapNode); - }); - ObjectUtils.cast(node).set("value", valueArrayNode); - } - - private void parseOtherMemoryType(Map res, ArrayNode valueArrayNode) { - for (Map.Entry resEntry : res.entrySet()) { - if (Objects.equals(resEntry.getKey(), AippConst.MEMORY_SWITCH_KEY)) { - this.checkEntryType(resEntry, Boolean.class); - valueArrayNode.add(this.convertMemorySwitch(resEntry.getKey(), ObjectUtils.cast(resEntry.getValue()))); - } else { - valueArrayNode.add(this.convertObject(resEntry.getKey(), String.valueOf(resEntry.getValue()))); - } - } - } - - private void parseUserSelect(Map res, ArrayNode valueArrayNode) { - for (Map.Entry resEntry : res.entrySet()) { - if (Objects.equals(resEntry.getKey(), AippConst.MEMORY_SWITCH_KEY)) { - this.checkEntryType(resEntry, Boolean.class); - valueArrayNode.add(this.convertMemorySwitch(resEntry.getKey(), ObjectUtils.cast(resEntry.getValue()))); - } else if (Objects.equals(resEntry.getKey(), "value")) { - valueArrayNode.add(this.convertValueForUserSelect(resEntry.getKey(), - String.valueOf(resEntry.getValue()))); - } else { - valueArrayNode.add(this.convertObject(resEntry.getKey(), String.valueOf(resEntry.getValue()))); - } - } - } - - private ArrayNode convertList(String value) { - String[] res = JsonUtils.parseObject(value, String[].class); - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - - List> re = Arrays.stream(res).map(this::convert).collect(Collectors.toList()); - - ArrayNode valueArrayNode = nodeFactory.arrayNode(); - for (Map rr : re) { - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : rr.entrySet()) { - mapNode.put(entry.getKey(), entry.getValue()); - } - valueArrayNode.add(mapNode); - } - return valueArrayNode; - } - - private Map convert(String value) { - Map map = new HashMap<>(); - map.put("id", UUID.randomUUID().toString()); - map.put("from", "input"); - map.put("type", "String"); - map.put("value", value); - return map; - } - - private ObjectNode convertObject(String key, String value) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - Map map = new HashMap<>(); - map.put("id", UUID.randomUUID().toString()); - map.put("name", key); - map.put("from", "input"); - map.put("type", "String"); - map.put("value", value); - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : map.entrySet()) { - mapNode.put(entry.getKey(), entry.getValue()); - } - return mapNode; - } - - private ObjectNode convertId(String key, Long value) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - Map map = new HashMap<>(); - map.put("id", UUID.randomUUID().toString()); - map.put("name", key); - map.put("from", "input"); - map.put("type", "String"); - map.put("value", value); - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : map.entrySet()) { - if (StringUtils.equals(entry.getKey(), "value")) { - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } else { - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } - } - return mapNode; - } - - private ObjectNode convertMemorySwitch(String key, Boolean isOpenSwitch) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - Map map = new HashMap<>(); - map.put("id", UUID.randomUUID().toString()); - map.put("name", key); - map.put("from", "Input"); - map.put("type", "Boolean"); - map.put("value", isOpenSwitch); - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : map.entrySet()) { - if (StringUtils.equals(entry.getKey(), "value")) { - this.checkEntryType(entry, Boolean.class); - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } else { - this.checkEntryType(entry, String.class); - mapNode.put(entry.getKey(), ObjectUtils.cast(entry.getValue())); - } - } - return mapNode; - } - - private ObjectNode convertValueForUserSelect(String key, String value) { - JsonNodeFactory nodeFactory = JsonNodeFactory.instance; - Map map = new HashMap<>(); - map.put("id", UUID.randomUUID().toString()); - map.put("name", key); - map.put("from", "input"); - map.put("type", StringUtils.EMPTY); - map.put("value", value); - ObjectNode mapNode = nodeFactory.objectNode(); - for (Map.Entry entry : map.entrySet()) { - mapNode.put(entry.getKey(), entry.getValue()); - } - return mapNode; - } - - private void checkEntryType(Map.Entry entry, Class clazz) { - if (!clazz.isInstance(entry.getValue())) { - throw new AippException(UPDATE_APP_CONFIGURATION_FAILED, entry.getValue().getClass().getName()); - } - } - - private void updateConfigByGlowGraphAppearance(String appearance, List formProperties, - AppBuilderConfig config) { - // 这个map {nodeId:{name:value}} - Map> nodeIdToJadeConfigMap = this.getJadeConfigsFromAppearance(appearance); - List configProperties = config.getConfigProperties(); - Map idToFormPropertyMap = - formProperties.stream().collect(Collectors.toMap(AppBuilderFormProperty::getId, Function.identity())); - // 这样写避免循环的时候去查询数据库获取configProperty对应的formProperty - for (AppBuilderConfigProperty cp : configProperties) { - if (!idToFormPropertyMap.containsKey(cp.getFormPropertyId())) { - // 2024/4/29 0029 这里可能拿到null,这里暂时不知道什么问题,先把拿不到的跳过 - continue; - } - cp.setFormProperty(idToFormPropertyMap.get(cp.getFormPropertyId())); - String nodeId = cp.getNodeId(); - if (StringUtils.isBlank(nodeId)) { - // 这里排除掉空nodeId的config - continue; - } - Map nameValue = nodeIdToJadeConfigMap.get(nodeId); - AppBuilderFormProperty formProperty = cp.getFormProperty(); - if (MapUtils.isEmpty(nameValue)) { - // 2024/4/29 0029 暂时先不删除了,仅修改现存的内容 - continue; - } else { - if (nameValue.get(formProperty.getName()) == null) { - continue; - } - } - if ("model".equals(formProperty.getName())) { - if (nameValue.get("accessInfo") == null) { - formProperty.setDefaultValue(nameValue.get(formProperty.getName())); - } else { - formProperty.setDefaultValue(ObjectUtils.>cast(nameValue.get("accessInfo")) - .get("serviceName")); - } - } else { - formProperty.setDefaultValue(nameValue.get(formProperty.getName())); - } - // 更新 - config.getFormPropertyRepository().updateOne(formProperty); - } - } - - private Map> getJadeConfigsFromAppearance(String appearance) { - JSONArray pages = JSONObject.parseObject(appearance).getJSONArray("pages"); - // 这个map {nodeId:{name:value}} - Map> nodeIdToJadeConfigMap = new HashMap<>(); - for (int i = 0; i < pages.size(); i++) { - JSONObject page = pages.getJSONObject(i); - JSONArray shapes = page.getJSONArray("shapes"); - for (int j = 0; j < shapes.size(); j++) { - JSONObject node = shapes.getJSONObject(j); - String nodeId = node.getString("id"); - JSONArray inputParams = this.extractingInputParams(node); - if (Objects.isNull(inputParams)) { - continue; - } - nodeIdToJadeConfigMap.put(nodeId, this.extractingExpandObject(inputParams)); - } - } - return nodeIdToJadeConfigMap; - } - - private JSONArray extractingInputParams(JSONObject node) { - String nodeType = node.getString("type"); - if (StringUtils.equalsIgnoreCase("startNodeStart", nodeType)) { - return node.getJSONObject("flowMeta").getJSONArray("inputParams"); - } else if (StringUtils.equalsIgnoreCase("evaluationStartNodeStart", nodeType)) { - return new JSONArray(); - } else if (StringUtils.equalsIgnoreCase("endNodeEnd", nodeType) || StringUtils.equalsIgnoreCase( - "evaluationEndNodeEnd", - nodeType)) { - return null; - } else if (StringUtils.equalsIgnoreCase("jadeEvent", nodeType)) { - return null; - } else if (StringUtils.equalsIgnoreCase("conditionNodeCondition", nodeType)) { - return null; - } else if (StringUtils.equalsIgnoreCase("manualCheckNodeState", nodeType) || StringUtils.equalsIgnoreCase( - "intelligentFormNodeState", - nodeType)) { - return node.getJSONObject("flowMeta") - .getJSONObject("task") - .getJSONObject("converter") - .getJSONObject("entity") - .getJSONArray("inputParams"); - } else { - return node.getJSONObject("flowMeta") - .getJSONObject("jober") - .getJSONObject("converter") - .getJSONObject("entity") - .getJSONArray("inputParams"); - } - } - - // 如果type是Array,那么调用这个方法获取一个List - private List extractingExpandArray(JSONArray value) { - List result = new ArrayList<>(); - for (int index = 0; index < value.size(); index++) { - JSONObject jsonObject = value.getJSONObject(index); - if (StringUtils.equalsIgnoreCase("Input", jsonObject.getString("from"))) { - result.add(jsonObject.get("value")); - continue; - } - if (StringUtils.equalsIgnoreCase("Expand", jsonObject.getString("from"))) { - this.handleExpandType(jsonObject, result); - } - } - return result; - } - - private void handleExpandType(JSONObject jsonObject, List result) { - if (StringUtils.equalsIgnoreCase("Array", jsonObject.getString("type"))) { - List array = this.extractingExpandArray(jsonObject.getJSONArray("value")); - result.add(array); - return; - } - if (StringUtils.equalsIgnoreCase("Object", jsonObject.getString("type"))) { - Map map = this.extractingExpandObject(jsonObject.getJSONArray("value")); - if (MapUtils.isNotEmpty(map)) { - result.add(map); - } - } - } - - // 如果type是Object,那么调用这个方法获取一个Map - private Map extractingExpandObject(JSONArray value) { - Map result = new HashMap<>(); - for (int index = 0; index < value.size(); index++) { - JSONObject jsonObject = value.getJSONObject(index); - if (StringUtils.equalsIgnoreCase("Input", jsonObject.getString("from"))) { - result.put(jsonObject.getString("name"), jsonObject.get("value")); - continue; - } - if (StringUtils.equalsIgnoreCase("Expand", jsonObject.getString("from"))) { - this.handleExpandType(jsonObject, result); - } - } - return result; - } - - private void handleExpandType(JSONObject jsonObject, Map result) { - if (StringUtils.equalsIgnoreCase("Array", jsonObject.getString("type"))) { - List array = this.extractingExpandArray(jsonObject.getJSONArray("value")); - result.put(jsonObject.getString("name"), array); - return; - } - if (StringUtils.equalsIgnoreCase("Object", jsonObject.getString("type"))) { - Map map = this.extractingExpandObject(jsonObject.getJSONArray("value")); - if (MapUtils.isNotEmpty(map)) { - result.put(jsonObject.getString("name"), map); - } - } - } - - private String[] getFirstModelInfo(OperationContext context) { - // TODO: 缩小异常捕获的范围。 - try { - ModelListDto modelList = this.aippModelCenter.fetchModelList(AippConst.CHAT_MODEL_TYPE, null, context); - if (modelList != null && modelList.getModels() != null && !modelList.getModels().isEmpty()) { - ModelAccessInfo firstModel = modelList.getModels().get(0); - return new String[]{firstModel.getServiceName(), firstModel.getTag()}; - } else { - return new String[]{StringUtils.EMPTY, StringUtils.EMPTY}; - } - } catch (Exception e) { - log.error("Failed to get first model information.", e); - return new String[]{StringUtils.EMPTY, StringUtils.EMPTY}; - } - } - - private String generateUniquePath() { - String path; - int retryTimes = RETRY_PATH_GENERATION_TIMES; - do { - path = RandomPathUtils.generateRandomString(PATH_LENGTH); - if (!this.appRepository.checkPathExists(path)) { - return path; - } - log.warn("Path already exists, retrying... {} times left", retryTimes - 1); - } while (retryTimes-- > 0); - - log.error("Failed to generate a unique path for app after {} retries.", RETRY_PATH_GENERATION_TIMES); - throw new AippException(UPDATE_APP_CONFIGURATION_FAILED); - } - - private String getAttribute(Map attributes, String name) { - // 增加保护,之前创建的应用部分前端传入了null, 如果再新建版本则导致新版本出现字符串"null" - Object value = attributes.get(name); - return value == null ? StringUtils.EMPTY : String.valueOf(value); + @Override + @Transactional + public AppBuilderAppDto recoverApp(String appId, String resetId, OperationContext context) { + AppVersion resetApp = this.appVersionService.retrieval(resetId); + AppVersion currentApp = this.appVersionService.retrieval(appId); + currentApp.cloneVersion(resetApp); + this.appVersionService.update(currentApp); + return this.converterFactory.convert(currentApp, AppBuilderAppDto.class); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderFormServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderFormServiceImpl.java index 726ad504bc..e990c05cb6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderFormServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderFormServiceImpl.java @@ -8,11 +8,9 @@ import static modelengine.fitframework.util.ObjectUtils.cast; -import modelengine.fit.jane.task.util.Entities; - -import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; +import modelengine.fit.jane.task.util.Entities; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.condition.FormQueryCondition; @@ -23,6 +21,8 @@ import modelengine.fit.jober.aipp.service.AppBuilderFormService; import modelengine.fit.jober.aipp.service.UploadedFileManageService; import modelengine.fit.jober.common.RangedResultSet; + +import modelengine.fit.http.server.HttpClassicServerRequest; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Value; import modelengine.fitframework.log.Logger; @@ -43,39 +43,23 @@ @Component public class AppBuilderFormServiceImpl implements AppBuilderFormService { private static final String RUNTIME = "runtime"; - private static final String VERSION = "1.0.0"; - private static final double NAME_MAX_LENGTH = 64; - private static final double DESCRIPTION_MAX_LENGTH = 300; - private static final String IMG_URL = "imgUrl"; - private static final String IFRAME_URL = "iframeUrl"; - private static final String FILE_UUID = "fileUuid"; - private static final String FILE_NAME = "fileName"; - private static final String SCHEMA = "schema"; - private static final String DESCRIPTION = "description"; - private static final int REMOVABLE = 1; - private static final int IRREMOVABLE = 0; - private static final String FORM_NAME_FORMAT = "^[\\u4E00-\\u9FA5A-Za-z0-9][\\u4E00-\\u9FA5A-Za-z0-9-_]*$"; - private static final Logger log = Logger.get(AppBuilderFormServiceImpl.class); private final AppBuilderFormRepository formRepository; - private final AippFormCreateConfig aippFormCreateConfig; - private final UploadedFileManageService uploadedFileManageService; - private final List excludeNames; public AppBuilderFormServiceImpl(AppBuilderFormRepository formRepository, AippFormCreateConfig aippFormCreateConfig, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderPromptServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderPromptServiceImpl.java index 621e737717..aae6bf37cc 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderPromptServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppBuilderPromptServiceImpl.java @@ -6,14 +6,9 @@ package modelengine.fit.jober.aipp.service.impl; -import modelengine.jade.common.globalization.LocaleService; - -import com.alibaba.fastjson.JSONObject; - import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.condition.InspirationQueryCondition; @@ -27,12 +22,14 @@ import modelengine.fit.jober.aipp.repository.AppBuilderInspirationRepository; import modelengine.fit.jober.aipp.service.AppBuilderPromptService; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; import modelengine.fit.jober.aipp.util.UUIDUtil; +import modelengine.jade.common.globalization.LocaleService; + +import com.alibaba.fastjson.JSONObject; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; -import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.StringUtils; import java.util.ArrayList; @@ -76,8 +73,8 @@ public Rsp queryInspirations(String appId, String categoryI boolean isDebug) { PromptProperty promptProperty = this.findInspirationProperty(appId); AppBuilderPromptCategoryDto category = promptProperty.getCategoryById(categoryId); - List customInspirationList = this.getCustomInspirationListWithCategoryId(categoryId, category, - appId, context); + List customInspirationList = + this.getCustomInspirationListWithCategoryId(categoryId, category, appId, context); // 判断是否直接查看的是“我的”类目下的灵感大全 boolean isCustomCategory = promptProperty.isCustomCategory(categoryId); @@ -87,11 +84,15 @@ public Rsp queryInspirations(String appId, String categoryI category = this.buildCustomCategory(categoryId, customInspirationList); inspirations = this.getInspirationsWhenIsCustomCategory(customInspirationList); } else { - inspirations = this.getInspirationsWhenIsNotCustomCategory(isDebug, category, customInspirationList, + inspirations = this.getInspirationsWhenIsNotCustomCategory(isDebug, + category, + customInspirationList, promptProperty.getInspirationsByCategoryId(categoryId)); } - return Rsp.ok( - AppBuilderPromptDto.builder().inspirations(inspirations).categories(category.getChildren()).build()); + return Rsp.ok(AppBuilderPromptDto.builder() + .inspirations(inspirations) + .categories(category.getChildren()) + .build()); } private void checkCustomQueryValid(String categoryId, boolean isDebug, List customInspirationList) { @@ -142,9 +143,12 @@ private List getInspirationsWhenIs private List getCustomInspirationList(String appId, OperationContext context, String parentId, String categoryId) { - String aippId = this.getAippIdByAppId(appId, context); - return this.inspirationRepo.selectWithCondition( - new InspirationQueryCondition(aippId, parentId, categoryId, context.getOperator())); + AppBuilderApp appBuilderApp = this.appFactory.create(appId); + String aippId = appBuilderApp.getAppSuiteId(); + return this.inspirationRepo.selectWithCondition(new InspirationQueryCondition(aippId, + parentId, + categoryId, + context.getOperator())); } private void mergeCustomInspirations(List customInspirationList, @@ -163,7 +167,8 @@ private void mergeCustomInspirations(List customInspirationList, private void mergeCustomCategories(List customInspirationList, List categories) { Map categoryMap = customInspirationList.stream() - .collect(Collectors.toMap(InspirationPo::getParentId, InspirationPo::getCategoryId, + .collect(Collectors.toMap(InspirationPo::getParentId, + InspirationPo::getCategoryId, (oldValue, newValue) -> oldValue)); this.mergeCustomCategoriesRecurse(categories, categoryMap); } @@ -203,8 +208,11 @@ private void mergeCustomCategoriesRecurse(List cate String categoryId = category.getId(); if (categoryMap.containsKey(categoryId)) { String customCategoryId = categoryMap.get(categoryId); - AppBuilderPromptCategoryDto customCategory = new AppBuilderPromptCategoryDto(msg, customCategoryId, - categoryId + ":" + customCategoryId, true, new ArrayList<>()); + AppBuilderPromptCategoryDto customCategory = new AppBuilderPromptCategoryDto(msg, + customCategoryId, + categoryId + ":" + customCategoryId, + true, + new ArrayList<>()); category.getChildren().add(customCategory); categoryMap.remove(categoryId); } @@ -215,23 +223,15 @@ private void mergeCustomCategoriesRecurse(List cate }); } - private String getAippIdByAppId(String appId, OperationContext context) { - List metas = MetaUtils.getAllMetasByAppId(this.metaService, appId, context); - if (CollectionUtils.isEmpty(metas)) { - log.error("Meta can not be null."); - throw new AippParamException(AippErrCode.QUERY_INSPIRATION_FAILED); - } - return metas.get(0).getId(); - } - @Override public void addCustomInspiration(String appId, String parentId, AppBuilderPromptDto.AppBuilderInspirationDto inspirationDto, OperationContext context) { - String aippId = getAippIdByAppId(appId, context); + AppBuilderApp appBuilderApp = this.appFactory.create(appId); + String aippId = appBuilderApp.getAppSuiteId(); String customId; // 查询是否已存在"我的"类目 - Optional categoryId = this.inspirationRepo.findCustomCategoryId(aippId, parentId, - context.getOperator()); + Optional categoryId = + this.inspirationRepo.findCustomCategoryId(aippId, parentId, context.getOperator()); customId = categoryId.orElseGet(() -> UUIDUtil.uuid().substring(0, 6)); InspirationPo inspirationPo = buildInspirationPo(parentId, inspirationDto, context, customId, aippId); this.inspirationRepo.addCustomInspiration(inspirationPo); @@ -255,8 +255,10 @@ private static InspirationPo buildInspirationPo(String parentId, @Override public void updateCustomInspiration(String appId, String categoryId, String inspirationId, AppBuilderPromptDto.AppBuilderInspirationDto inspirationDto, OperationContext context) { - String aippId = getAippIdByAppId(appId, context); - Validation.equals(inspirationId, inspirationDto.getId(), + AppBuilderApp appBuilderApp = this.appFactory.create(appId); + String aippId = appBuilderApp.getAppSuiteId(); + Validation.equals(inspirationId, + inspirationDto.getId(), () -> new AippParamException(AippErrCode.INPUT_PARAM_IS_INVALID, "inspiration id")); this.inspirationRepo.updateCustomInspiration(inspirationId, buildInspirationPo(null, inspirationDto, context, categoryId, aippId)); @@ -265,7 +267,8 @@ public void updateCustomInspiration(String appId, String categoryId, String insp @Override public void deleteCustomInspiration(String appId, String categoryId, String inspirationId, OperationContext context) { - String aippId = getAippIdByAppId(appId, context); + AppBuilderApp appBuilderApp = this.appFactory.create(appId); + String aippId = appBuilderApp.getAppSuiteId(); this.inspirationRepo.deleteCustomInspiration(aippId, categoryId, inspirationId, context.getOperator()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatServiceImpl.java index 3b1a9d6d71..3689c90811 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatServiceImpl.java @@ -6,66 +6,16 @@ package modelengine.fit.jober.aipp.service.impl; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.INPUT_PARAM_IS_INVALID; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.TASK_NOT_FOUND; -import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_FLOW_DEF_ID_KEY; -import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INFOS_KEY; -import static modelengine.fit.jober.aipp.constants.AippConst.BUSINESS_INPUT_KEY; -import static modelengine.fit.jober.aipp.enums.AppTypeEnum.APP; - -import modelengine.fit.jade.waterflow.FlowsService; +import lombok.RequiredArgsConstructor; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; -import modelengine.fit.jober.aipp.common.exception.AippException; -import modelengine.fit.jober.aipp.common.exception.AippParamException; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; -import modelengine.fit.jober.aipp.dto.AppInputParam; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; -import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; -import modelengine.fit.jober.aipp.entity.AippInstLog; -import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; -import modelengine.fit.jober.aipp.entity.ChatInfo; -import modelengine.fit.jober.aipp.enums.AippInstLogType; -import modelengine.fit.jober.aipp.enums.AppState; -import modelengine.fit.jober.aipp.enums.InputParamType; -import modelengine.fit.jober.aipp.enums.RestartModeEnum; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; -import modelengine.fit.jober.aipp.genericable.AppBuilderAppService; -import modelengine.fit.jober.aipp.mapper.AippChatMapper; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; -import modelengine.fit.jober.aipp.service.AippLogService; -import modelengine.fit.jober.aipp.service.AippRunTimeService; import modelengine.fit.jober.aipp.service.AppChatService; -import modelengine.fit.jober.aipp.util.AippLogUtils; -import modelengine.fit.jober.aipp.util.AppUtils; -import modelengine.fit.jober.aipp.util.CacheUtils; -import modelengine.fit.jober.aipp.util.FlowUtils; -import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.UUIDUtil; -import modelengine.fit.jober.common.ServerInternalException; import modelengine.fitframework.annotation.Component; -import modelengine.fitframework.annotation.Value; import modelengine.fitframework.flowable.Choir; -import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; -import modelengine.fitframework.merge.ConflictResolutionPolicy; -import modelengine.fitframework.model.Tuple; -import modelengine.fitframework.util.CollectionUtils; -import modelengine.fitframework.util.MapUtils; -import modelengine.fitframework.util.ObjectUtils; -import modelengine.fitframework.util.StringUtils; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * 历史会话服务实现类 @@ -74,361 +24,30 @@ * @since 2024-07-23 */ @Component +@RequiredArgsConstructor public class AppChatServiceImpl implements AppChatService { private static final Logger LOGGER = Logger.get(AppChatServiceImpl.class); - private static final int FROM_OTHER_CHAT = 2; - private static final String DEFAULT_CHAT_NAME_PREFIX = "@appBuilderDebug-"; - - private final AppBuilderAppFactory appFactory; - - private final AippChatMapper aippChatMapper; - - private final AippRunTimeService aippRunTimeService; - - private final AppBuilderAppService appService; - - private final AippLogService aippLogService; - - private final AppBuilderAppRepository appRepository; - - private final MetaService metaService; - - private final FlowsService flowsService; - - private final int maxQuestionLen; - private final int maxUserContextLen; - - public AppChatServiceImpl(AppBuilderAppFactory appFactory, AippChatMapper aippChatMapper, - AippRunTimeService aippRunTimeService, AppBuilderAppService appService, AippLogService aippLogService, - AppBuilderAppRepository appRepository, MetaService metaService, FlowsService flowsService, - @Value("${app.question.max-length}") Integer maxQuestionLen, - @Value("${app.user-context.max-length}") Integer maxUserContextLen) { - this.appFactory = appFactory; - this.aippChatMapper = aippChatMapper; - this.aippRunTimeService = aippRunTimeService; - this.appService = appService; - this.aippLogService = aippLogService; - this.appRepository = appRepository; - this.metaService = metaService; - this.flowsService = flowsService; - this.maxQuestionLen = maxQuestionLen != null ? maxQuestionLen : 20000; - this.maxUserContextLen = maxUserContextLen != null ? maxUserContextLen : 500; - } + private final AppVersionService appVersionService; @Override public Choir chat(CreateAppChatRequest body, OperationContext context, boolean isDebug) { - LOGGER.info("[perf] [{}] chat start, appId={}, isDebug={}", - System.currentTimeMillis(), - body.getAppId(), - isDebug); - this.validateApp(body.getAppId()); - AppBuilderApp app = this.appFactory.create(body.getAppId()); - if (isInvalidQuestion(app.getType(), body)) { - throw new AippParamException(INPUT_PARAM_IS_INVALID, AippConst.BS_AIPP_QUESTION_KEY); - } - Map businessData = this.convertContextToBusinessData(body, isDebug); - // 这里几行代码的顺序不可以调整,必须先把对话的appId查询出来,再去创建chatId - String chatAppId = this.getAppId(body); - boolean hasAtOtherApp = body.hasAtOtherApp(); - this.createChatId(body, hasAtOtherApp, businessData); - // create instance —— 根据实际的那个app创建 - AppUtils.setAppChatInfo(body.getAppId(), isDebug); - if (isDebug) { - this.appService.updateFlow(chatAppId, context); - } - LOGGER.info("[perf] [{}] chat updateFlow end, appId={}", System.currentTimeMillis(), body.getAppId()); - this.addUserContext(body, businessData, isDebug, context, app.getType()); - Tuple tuple = this.aippRunTimeService.createInstanceByApp(chatAppId, - body.getQuestion(), - businessData, - context, + LOGGER.info("[perf] [{}] chat start, appId={}, isDebug={}", System.currentTimeMillis(), body.getAppId(), isDebug); - LOGGER.info("[perf] [{}] chat createInstanceByApp end, appId={}", System.currentTimeMillis(), body.getAppId()); - // 这tuple的两个值都不可能为null - try { - this.saveChatInfos(body, - context, - ObjectUtils.cast(tuple.get(0).orElseThrow(this::generalServerException)), - chatAppId, - isDebug); - } catch (AippTaskNotFoundException e) { - throw new AippException(TASK_NOT_FOUND); - } + + Choir choir = isDebug + ? this.appVersionService.debug(body, context) + : this.appVersionService.run(body, context); + LOGGER.info("[perf] [{}] chat saveChatInfos end, appId={}", System.currentTimeMillis(), body.getAppId()); - Choir result = ObjectUtils.cast(tuple.get(1).orElseThrow(this::generalServerException)); LOGGER.info("[perf] [{}] chat end, appId={}, isDebug={}", System.currentTimeMillis(), body.getAppId(), isDebug); - return result; - } - - private boolean isInvalidQuestion(String appType, CreateAppChatRequest request) { - return StringUtils.equals(APP.code(), appType) && (request.getQuestion() == null || !StringUtils.lengthBetween( - request.getQuestion(), - 0, - this.maxQuestionLen, - true, - true)); + return choir; } @Override public Choir restartChat(String instanceId, Map additionalContext, OperationContext operationContext) { - String path = this.aippLogService.getParentPath(instanceId); - String parentInstanceId = path.split(AippLogUtils.PATH_DELIMITER)[1]; - if (StringUtils.isEmpty(parentInstanceId)) { - LOGGER.error("parentInstanceId is empty."); - throw new AippException(AippErrCode.RE_CHAT_FAILED, instanceId); - } - // 这个方法查询的chatList,0号位一定是原对话,1号位一定是at对话(如果有的话),详见本类saveChatInfo方法 - List chatIds = this.aippChatMapper.selectChatIdByInstanceId(parentInstanceId); - if (chatIds.isEmpty()) { - throw new IllegalArgumentException(StringUtils.format("The instance id {0} does not match any chat id.", - parentInstanceId)); - } - List chatList = this.aippChatMapper.selectChatListByChatIds(chatIds); - if (CollectionUtils.isEmpty(chatList)) { - LOGGER.error("chatList is empty."); - throw new AippParamException(AippErrCode.RE_CHAT_FAILED, parentInstanceId); - } - String restartMode = ObjectUtils.cast(additionalContext.getOrDefault(AippConst.RESTART_MODE, - RestartModeEnum.OVERWRITE.getMode())); - additionalContext.put(AippConst.RESTART_MODE, restartMode); - CreateAppChatRequest body = this.buildChatBody(parentInstanceId, additionalContext, chatList); - if (StringUtils.equals(RestartModeEnum.OVERWRITE.getMode(), restartMode)) { - this.aippChatMapper.deleteWideRelationshipByInstanceId(parentInstanceId); - this.aippLogService.deleteInstanceLog(parentInstanceId); - } - boolean isDebug = AppState.INACTIVE.getName() - .equals(JsonUtils.parseObject(chatList.get(0).getAttributes()).get(AippConst.ATTR_CHAT_STATE_KEY)); - return this.chat(body, operationContext, isDebug); - } - - private CreateAppChatRequest buildChatBody(String parentInstanceId, Map additionalContextParam, - List chatList) { - Map additionalContext = additionalContextParam; - CreateAppChatRequest.CreateAppChatRequestBuilder bodyBuilder = CreateAppChatRequest.builder(); - List instLogs = this.aippLogService.queryLogsByInstanceIdAndLogTypes(parentInstanceId, - Arrays.asList(AippInstLogType.QUESTION.name(), AippInstLogType.HIDDEN_QUESTION.name())); - AippInstLog questionLog = instLogs.get(0); - Map logData = JsonUtils.parseObject(questionLog.getLogData()); - String question = ObjectUtils.cast(logData.get("msg")); - if (logData.containsKey(BUSINESS_INFOS_KEY)) { - Map infos = ObjectUtils.cast(logData.get(BUSINESS_INFOS_KEY)); - if (infos != null && infos.containsKey(BUSINESS_INPUT_KEY)) { - Map input = ObjectUtils.cast(infos.get(BUSINESS_INPUT_KEY)); - Map mergedContext = - MapUtils.merge(additionalContext, input, ConflictResolutionPolicy.OVERRIDE); - additionalContext = mergedContext; - } - } - bodyBuilder.question(question); - bodyBuilder.chatId(chatList.get(0).getChatId()); - bodyBuilder.appId(chatList.get(0).getAppId()); - CreateAppChatRequest.Context.ContextBuilder contextBuilder = CreateAppChatRequest.Context.builder(); - contextBuilder.userContext(additionalContext); - if (additionalContext.containsKey(AippConst.BS_DIMENSION_ID_KEY)) { - contextBuilder.dimensionId(ObjectUtils.cast(additionalContext.get(AippConst.BS_DIMENSION_ID_KEY))); - } - if (chatList.size() == FROM_OTHER_CHAT) { - contextBuilder.atChatId(chatList.get(1).getChatId()); - } - return bodyBuilder.context(contextBuilder.build()).build(); - } - - private void validateApp(String appId) { - AppBuilderApp appBuilderApp = this.appRepository.selectWithId(appId); - if (appBuilderApp == null || StringUtils.isEmpty(appBuilderApp.getId())) { - throw new AippException(AippErrCode.APP_NOT_FOUND_WHEN_CHAT); - } - } - - private void saveChatInfos(CreateAppChatRequest body, OperationContext context, String instId, - String chatAppId, boolean isDebug) throws AippTaskNotFoundException { - AppBuilderApp app = this.appFactory.create(body.getAppId()); - Map attributes = new HashMap<>(); - Meta meta = CacheUtils.getMetaByAppId(this.metaService, chatAppId, isDebug, context); - if (meta == null) { - LOGGER.error("Cannot find meta for chat app. [appId={}, instId={}]", chatAppId, instId); - throw new AippTaskNotFoundException(TASK_NOT_FOUND); - } - String aippId = meta.getId(); - attributes.put(AippConst.ATTR_CHAT_INST_ID_KEY, instId); - attributes.put(AippConst.ATTR_CHAT_STATE_KEY, app.getState()); - attributes.put(AippConst.BS_AIPP_ID_KEY, aippId); - if (body.getContext() != null && StringUtils.isNotBlank(body.getContext().getDimensionId())) { - attributes.put(AippConst.BS_DIMENSION_ID_KEY, body.getContext().getDimensionId()); - } - String chatId = body.getChatId(); - this.buildAndInsertChatInfo(app, attributes, body.getQuestion(), chatId, context.getOperator()); - this.buildAndInsertWideRelationInfo(instId, chatId); - if (body.hasAtOtherApp()) { - AppBuilderApp chatApp = this.appFactory.create(chatAppId); - // 被@的应用的对话 - Map originAttributes = new HashMap<>(); - originAttributes.put(AippConst.ATTR_CHAT_INST_ID_KEY, instId); - originAttributes.put(AippConst.ATTR_CHAT_STATE_KEY, chatApp.getState()); - originAttributes.put(AippConst.ATTR_CHAT_ORIGIN_APP_KEY, app.getId()); - originAttributes.put(AippConst.ATTR_CHAT_ORIGIN_APP_VERSION_KEY, app.getVersion()); - String atChatId = body.getContext().getAtChatId(); - this.buildAndInsertChatInfo(chatApp, originAttributes, body.getQuestion(), atChatId, context.getOperator()); - this.buildAndInsertWideRelationInfo(instId, atChatId); - } - } - - private Map convertContextToBusinessData(CreateAppChatRequest body, boolean isDebug) { - Map businessData = new HashMap<>(); - if (body.getContext().getUseMemory() != null) { - businessData.put(AippConst.BS_AIPP_USE_MEMORY_KEY, body.getContext().getUseMemory()); - } - businessData.put("dimension", body.getContext().getDimension()); - businessData.put("isDebug", isDebug); - return businessData; - } - - private void addUserContext(CreateAppChatRequest body, Map businessData, boolean isDebug, - OperationContext context, String appType) { - Meta meta = CacheUtils.getMetaByAppId(this.metaService, body.getAppId(), isDebug, context); - String flowDefinitionId = ObjectUtils.cast(meta.getAttributes().get(ATTR_FLOW_DEF_ID_KEY)); - List inputParams = FlowUtils.getAppInputParams(this.flowsService, flowDefinitionId, context); - if (StringUtils.equals(APP.code(), appType)) { - inputParams = inputParams.stream() - .filter(param -> !StringUtils.equals("Question", param.getName())) - .collect(Collectors.toList()); - } - if (MapUtils.isEmpty(body.getContext().getUserContext())) { - if (inputParams.stream().noneMatch((AppInputParam::isRequired))) { - return; - } - LOGGER.error("No user context when starting a chat."); - throw new AippParamException(INPUT_PARAM_IS_INVALID, "user context"); - } - Map userContext = body.getContext().getUserContext(); - this.validateUserContext(userContext, inputParams); - businessData.putAll(userContext); - } - - private void validateUserContext(Map userContext, List inputParams) { - inputParams.forEach(param -> { - String paramName = param.getName(); - if (param.isRequired()) { - Validation.notNull(ObjectUtils.cast(userContext.get(paramName)), - () -> new AippParamException(INPUT_PARAM_IS_INVALID, paramName)); - } - if (userContext.get(param.getName()) == null) { - return; - } - boolean isValid; - switch (InputParamType.getParamType(param.getType())) { - case STRING_TYPE: - isValid = userContext.get(paramName) instanceof String - && StringUtils.lengthBetween((String) userContext.get(paramName), - 1, - this.maxUserContextLen, - true, - true); - break; - case BOOLEAN_TYPE: - isValid = userContext.get(paramName) instanceof Boolean; - break; - case INTEGER_TYPE: - isValid = - userContext.get(paramName) instanceof Integer && ObjectUtils.between((int) userContext.get( - paramName), -999999999, 999999999); - break; - case NUMBER_TYPE: - isValid = isValidNumber(userContext.get(paramName)); - break; - default: - throw new AippParamException(INPUT_PARAM_IS_INVALID, paramName); - } - if (!isValid) { - throw new AippParamException(INPUT_PARAM_IS_INVALID, paramName); - } - }); - } - - private boolean isValidNumber(Object value) { - if (!(value instanceof Number)) { - return false; - } - BigDecimal numberValue = new BigDecimal(value.toString()); - if (numberValue.compareTo(new BigDecimal("-999999999.99")) < 0 - || numberValue.compareTo(new BigDecimal("999999999.99")) > 0) { - return false; - } - int scale = numberValue.scale(); - return scale <= 2; - } - - private String getAppId(CreateAppChatRequest body) { - String atChatId = body.getContext().getAtChatId(); - if (StringUtils.isNotBlank(atChatId)) { - List chats = this.aippChatMapper.selectChatList(null, atChatId, null); - if (CollectionUtils.isEmpty(chats)) { - throw new AippException(AippErrCode.APP_CHAT_ERROR); - } - return chats.get(0).getAppId(); - } - String atAppId = body.getContext().getAtAppId(); - if (StringUtils.isNotBlank(atAppId)) { - return atAppId; - } - return body.getAppId(); - } - - private void createChatId(CreateAppChatRequest body, boolean hasAtOtherApp, Map businessData) { - // body里没有chatId:第一次对话 - if (StringUtils.isBlank(body.getChatId())) { - body.setChatId(UUIDUtil.uuid()); - } - // 没有被@的chatId, @其它应用的第一次对话 - if (hasAtOtherApp && StringUtils.isBlank((body.getContext().getAtChatId()))) { - body.getContext().setAtChatId(UUIDUtil.uuid()); - businessData.put(AippConst.BS_AT_CHAT_ID, body.getContext().getAtChatId()); - } - businessData.put(AippConst.BS_CHAT_ID, body.getChatId()); - } - - private void buildAndInsertChatInfo(AppBuilderApp app, Map attributes, String chatName, - String chatId, String operator) { - String cutChatName = this.generateChatName(chatName); - LocalDateTime operateTime = LocalDateTime.now(); - ChatInfo chatInfo = ChatInfo.builder() - .appId(app.getId()) - .version(app.getVersion()) - .attributes(JsonUtils.toJsonString(attributes)) - .chatId(chatId) - .chatName(cutChatName) - .status(AippConst.CHAT_STATUS) - .updater(operator) - .createTime(operateTime) - .updateTime(operateTime) - .creator(operator) - .build(); - this.aippChatMapper.insertChat(chatInfo); - } - - private String generateChatName(String chatName) { - if (chatName == null) { - return DEFAULT_CHAT_NAME_PREFIX + UUIDUtil.uuid().substring(0, 6); - } - return chatName.length() > 64 ? chatName.substring(0, 32) : chatName; - } - - private void buildAndInsertWideRelationInfo(String instId, String chatId) { - LocalDateTime operateTime = LocalDateTime.now(); - ChatAndInstanceMap wideRelationInfo = ChatAndInstanceMap.builder() - .msgId(UUIDUtil.uuid()) - .instanceId(instId) - .chatId(chatId) - .createTime(operateTime) - .updateTime(operateTime) - .build(); - this.aippChatMapper.insertWideRelationship(wideRelationInfo); - } - - private ServerInternalException generalServerException() { - return new ServerInternalException("Except no null value but null!"); + return this.appVersionService.restart(instanceId, additionalContext, operationContext); } } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSessionServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSessionServiceImpl.java index 9b1906dc8b..55341fe7f8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSessionServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSessionServiceImpl.java @@ -7,13 +7,13 @@ package modelengine.fit.jober.aipp.service.impl; import modelengine.fit.jane.task.util.Entities; - -import lombok.RequiredArgsConstructor; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.entity.ChatSession; import modelengine.fit.jober.aipp.mapper.AppChatNumMapper; import modelengine.fit.jober.aipp.service.AppChatSessionService; + +import lombok.RequiredArgsConstructor; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.schedule.annotation.Scheduled; @@ -38,14 +38,14 @@ public class AppChatSessionServiceImpl implements AppChatSessionService { private static final Logger log = Logger.get(AppChatSessionServiceImpl.class); private final Map> emitterMap = new ConcurrentHashMap<>(); - private final AppChatNumMapper appChatNumMapper; @Override public void addSession(String instanceId, ChatSession chatSession) { this.emitterMap.put(instanceId, chatSession); try { - this.appChatNumMapper.insertOrAddOne(Entities.generateId(), chatSession.getAppId(), + this.appChatNumMapper.insertOrAddOne(Entities.generateId(), + chatSession.getAppId(), String.valueOf(chatSession.isDebug())); chatSession.setOccupied(true); } catch (DataAccessException e) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSseServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSseServiceImpl.java index a4c3198c86..b035089d6d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSseServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppChatSseServiceImpl.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.service.impl; -import lombok.RequiredArgsConstructor; import modelengine.fit.jober.aipp.entity.ChatSession; -import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; import modelengine.fit.jober.aipp.service.AppChatSessionService; import modelengine.fit.jober.aipp.service.AppChatSseService; import modelengine.fit.jober.aipp.util.AippLogUtils; + +import lombok.RequiredArgsConstructor; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.StringUtils; @@ -26,8 +27,7 @@ @Component @RequiredArgsConstructor public class AppChatSseServiceImpl implements AppChatSseService { - private final AippLogService logService; - + private final AippLogMapper aippLogMapper; private final AppChatSessionService appChatSessionService; @Override @@ -62,7 +62,7 @@ public void sendToAncestorLastData(String instanceId, Object data) { } private String getProcessedInstanceId(String instanceId) { - String path = this.logService.getParentPath(instanceId); + String path = this.aippLogMapper.getParentPath(instanceId); if (StringUtils.isNotEmpty(path)) { return path.split(AippLogUtils.PATH_DELIMITER)[1]; } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImpl.java index a407e5f7c8..eb5bfcdbe0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImpl.java @@ -16,8 +16,9 @@ import modelengine.fit.jober.aipp.service.AppTemplateService; import modelengine.fit.jober.aipp.util.TemplateUtils; import modelengine.fit.jober.common.RangedResultSet; + +import lombok.AllArgsConstructor; import modelengine.fitframework.annotation.Component; -import modelengine.fitframework.log.Logger; import modelengine.fitframework.transaction.Transactional; import java.util.List; @@ -30,18 +31,11 @@ * @since 2025-01-02 */ @Component +@AllArgsConstructor public class AppTemplateServiceImpl implements AppTemplateService { - private static final Logger log = Logger.get(AppTemplateServiceImpl.class); - private final AppBuilderAppService appService; - private final AppTemplateRepository templateRepository; - AppTemplateServiceImpl(AppBuilderAppService appService, AppTemplateRepository templateRepository) { - this.appService = appService; - this.templateRepository = templateRepository; - } - @Override public RangedResultSet query(TemplateQueryCondition cond, OperationContext context) { List rawResult = this.templateRepository.selectWithCondition(cond) diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImpl.java index 58a25a31d3..aeea19aa81 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImpl.java @@ -7,13 +7,13 @@ package modelengine.fit.jober.aipp.service.impl; import modelengine.fit.jane.task.util.Entities; - import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.dto.AppTypeDto; import modelengine.fit.jober.aipp.mapper.AppBuilderAppTypeMapper; import modelengine.fit.jober.aipp.po.AppBuilderAppTypePo; import modelengine.fit.jober.aipp.service.AppTypeService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.util.StringUtils; @@ -39,9 +39,9 @@ public AppTypeServiceImpl(AppBuilderAppTypeMapper appBuilderAppTypeMapper) { @Override public List queryAll(String tenantId) { return this.appBuilderAppTypeMapper.queryAll(tenantId) - .stream() - .map(this::deserialize) - .collect(Collectors.toList()); + .stream() + .map(this::deserialize) + .collect(Collectors.toList()); } @Override @@ -73,12 +73,12 @@ public void update(AppTypeDto dto, String tenantId) { private AppBuilderAppTypePo serialize(AppTypeDto dto, String tenantId) { LocalDateTime now = LocalDateTime.now(); return AppBuilderAppTypePo.builder() - .id(dto.getId()) - .name(dto.getName()) - .tenantId(tenantId) - .createAt(now) - .updateAt(now) - .build(); + .id(dto.getId()) + .name(dto.getName()) + .tenantId(tenantId) + .createAt(now) + .updateAt(now) + .build(); } private AppTypeDto deserialize(AppBuilderAppTypePo po) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/DatabaseFieldLocaleServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/DatabaseFieldLocaleServiceImpl.java index 5ed14eda4d..8539329e58 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/DatabaseFieldLocaleServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/DatabaseFieldLocaleServiceImpl.java @@ -8,6 +8,7 @@ import modelengine.fit.jober.aipp.repository.I18nRepository; import modelengine.fit.jober.aipp.service.DatabaseFieldLocaleService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Initialize; import modelengine.fitframework.log.Logger; @@ -25,7 +26,6 @@ @Component public class DatabaseFieldLocaleServiceImpl implements DatabaseFieldLocaleService { private static final Logger log = Logger.get(DatabaseFieldLocaleServiceImpl.class); - private static Map> resourceMap = null; private final I18nRepository i18nRepository; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/EndNodeChecker.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/EndNodeChecker.java index 4669eee873..ada58cc5be 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/EndNodeChecker.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/EndNodeChecker.java @@ -11,6 +11,7 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; + import modelengine.fitframework.annotation.Component; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FfmpegServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FfmpegServiceImpl.java index a1fd4976f8..34a41d4c00 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FfmpegServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FfmpegServiceImpl.java @@ -10,6 +10,7 @@ import modelengine.fit.jober.aipp.entity.ffmpeg.FfmpegTask; import modelengine.fit.jober.aipp.entity.ffmpeg.FfmpegUtil; import modelengine.fit.jober.aipp.service.FfmpegService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; @@ -29,7 +30,6 @@ @Component public class FfmpegServiceImpl implements FfmpegService { private static final Pattern STAT_PATTERN = Pattern.compile("Duration: (.*?),(.*?)Audio: (.*?) "); - private static final Logger log = Logger.get(FfmpegServiceImpl.class); @Override @@ -63,8 +63,9 @@ public void splitAudio(String inputFilePath, String outputPatten, int segmentSiz put(inputFilePath, null); }}; HashMap> outputs = new HashMap>() {{ - put(outputPatten, Arrays.asList("-f", "segment", "-segment_time", String.valueOf(segmentSize), "-c", "copy", - "-hide_banner", "-loglevel", "quiet")); + put(outputPatten, + Arrays.asList("-f", "segment", "-segment_time", String.valueOf(segmentSize), + "-c", "copy", "-hide_banner", "-loglevel", "quiet")); }}; new FfmpegTask(inputs, outputs).exec(); log.info("split {} to {} success.", inputFilePath, outputPatten); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FileServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FileServiceImpl.java index 62243aec60..3c3783dbbf 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FileServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/FileServiceImpl.java @@ -10,20 +10,6 @@ import static modelengine.fit.jober.aipp.entity.FileExtensionEnum.getFileExtension; import static modelengine.fitframework.util.ObjectUtils.cast; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; - -import modelengine.fit.http.client.HttpClassicClientFactory; -import modelengine.fit.http.client.HttpClassicClientRequest; -import modelengine.fit.http.client.HttpClassicClientResponse; -import modelengine.fit.http.entity.FileEntity; -import modelengine.fit.http.entity.NamedEntity; -import modelengine.fit.http.entity.PartitionedEntity; -import modelengine.fit.http.protocol.HttpRequestMethod; -import modelengine.fit.http.protocol.HttpResponseStatus; -import modelengine.fit.http.server.HttpClassicServerRequest; -import modelengine.fit.http.server.HttpClassicServerResponse; -import modelengine.fit.http.server.handler.CustomResourceHandler; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; import modelengine.fit.jane.task.util.Entities; @@ -39,6 +25,21 @@ import modelengine.fit.jober.aipp.util.HttpUtils; import modelengine.fit.jober.aipp.util.JsonUtils; import modelengine.fit.jober.aipp.validation.FormFileValidator; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import modelengine.fit.http.client.HttpClassicClientFactory; +import modelengine.fit.http.client.HttpClassicClientRequest; +import modelengine.fit.http.client.HttpClassicClientResponse; +import modelengine.fit.http.entity.FileEntity; +import modelengine.fit.http.entity.NamedEntity; +import modelengine.fit.http.entity.PartitionedEntity; +import modelengine.fit.http.protocol.HttpRequestMethod; +import modelengine.fit.http.protocol.HttpResponseStatus; +import modelengine.fit.http.server.HttpClassicServerRequest; +import modelengine.fit.http.server.HttpClassicServerResponse; +import modelengine.fit.http.server.handler.CustomResourceHandler; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Value; import modelengine.fitframework.log.Logger; @@ -77,40 +78,24 @@ @Component public class FileServiceImpl implements FileService, CustomResourceHandler { private static final Logger log = Logger.get(FileServiceImpl.class); - private static final String CONFIG_JSON = "config.json"; - private static final String BUILD = "build"; - private static final String SCHEMA = "schema"; - private static final List IMAGE_TYPE = new ArrayList<>(Arrays.asList("form.jpg", "form.png", "form.jpeg")); - private static final String FORM_IMAGE = "form image"; - private static final String INDEX_HTML = "index.html"; - private static final String TEMPLATE_ZIP = "template.zip"; - private static final long UNZIP_MAX_SIZE = 0x5FFFFFL; - private static final long FILE_MAX_COUNT = 1024L; private final FormFileValidator formFileValidator; - private final UploadedFileManageService uploadedFileManageService; - private final FormFileUploadConfig formFileUploadConfig; - private final String formFullTemporaryPath; - private final String formFullPath; - private final String pathPrefix; - private final String groupName; - - private final String resourcePathPrefix; + private final String resourceUrlPrefix; private final HttpClassicClientFactory httpClassicClientFactory; @@ -124,8 +109,9 @@ public FileServiceImpl(HttpClassicClientFactory httpClassicClientFactory, UploadedFileManageService uploadedFileManageService, FormFileUploadConfig formFileUploadConfig, @Value("${app-engine.form.path-prefix}") String pathPrefix, @Value("${app-engine.form.temporary-path}") String temporaryPath, - @Value("${app-engine.form.group-name}") String groupName, @Value("${app-engine.form.path}") String formPath, - @Value("${app-engine.resource.path-prefix}") String resourcePathPrefix) { + @Value("${app-engine.form.group-name}") String groupName, + @Value("${app-engine.form.path}") String formPath, + @Value("${app-engine.resource.url-prefix}") String resourceUrlPrefix) { this.httpClassicClientFactory = httpClassicClientFactory; this.imageGenModelUrl = imageGenModelUrl; this.imageGenModel = imageGenModel; @@ -136,14 +122,14 @@ public FileServiceImpl(HttpClassicClientFactory httpClassicClientFactory, this.formFullPath = pathPrefix + formPath; this.formFullTemporaryPath = pathPrefix + temporaryPath; this.groupName = groupName; - this.resourcePathPrefix = resourcePathPrefix; + this.resourceUrlPrefix = resourceUrlPrefix; } @Override public Rsp generateImage(GenerateImageDto imageDto) { log.info("Start generate image."); - HttpClassicClientRequest request = httpClassicClientFactory.create() - .createRequest(HttpRequestMethod.POST, imageGenModelUrl); + HttpClassicClientRequest request = + httpClassicClientFactory.create().createRequest(HttpRequestMethod.POST, imageGenModelUrl); Map requestData = new HashMap<>(); requestData.put("model", imageGenModel); requestData.put("size", imageDto.getSize()); @@ -151,7 +137,8 @@ public Rsp generateImage(GenerateImageDto imageDto) { request.jsonEntity(requestData); try (HttpClassicClientResponse response = HttpUtils.execute(request)) { if (HttpResponseStatus.OK.statusCode() != response.statusCode()) { - log.error("Generate image error, response code: {}, message: {}.", response.statusCode(), + log.error("Generate image error, response code: {}, message: {}.", + response.statusCode(), response.reasonPhrase()); throw new AippException(AippErrCode.GENERATE_IMAGE_FAILED); } @@ -159,8 +146,8 @@ public Rsp generateImage(GenerateImageDto imageDto) { log.error("Generate image error, result is empty."); throw new AippException(AippErrCode.GENERATE_IMAGE_FAILED); } - Map responseData = JsonUtils.parseObject( - JsonUtils.toJsonString(response.objectEntity().get().object())); + Map responseData = + JsonUtils.parseObject(JsonUtils.toJsonString(response.objectEntity().get().object())); List data = ObjectUtils.cast(responseData.get("data")); Map dataMap = ObjectUtils.cast(data.get(0)); return Rsp.ok(dataMap.get("b64_json").toString()); @@ -174,7 +161,8 @@ private String generateImagePrompt(String imageName, String description) { return String.format("根据以下信息生成一张图片:\n\n" + "- **图片名称**:%s\n" + "- **描述**:%s\n\n" + "图片应当直观地反映名称和描述中的内容,捕捉描述中的主题、氛围以及关键元素。" + "图像的构图、色彩和风格应与描述中的主要概念一致。请发挥创意,确保生成的图片能够生动呈现名称和描述中的画面感。", - imageName, description); + imageName, + description); } @Override @@ -192,7 +180,9 @@ public FileEntity getFile(OperationContext context, String fileCanonicalPath, St throw new AippException(context, AippErrCode.FILE_EXPIRED_OR_BROKEN); } ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Files.readAllBytes(filePath)); - return FileEntity.createAttachment(httpClassicServerResponse, fileName, byteArrayInputStream, + return FileEntity.createAttachment(httpClassicServerResponse, + fileName, + byteArrayInputStream, byteArrayInputStream.available()); } else { throw new IllegalArgumentException("FileCanonicalPath is empty"); @@ -245,10 +235,8 @@ public FormFileDto uploadSmartForm(PartitionedEntity receivedFile, String fileNa String decodedFileName = URLDecoder.decode(fileName, "UTF-8"); String uniqueFileName = generateUniqueFileName(decodedFileName); log.info("upload file fileName={} uniqueFileName={}", decodedFileName, uniqueFileName); - List entities = receivedFile.entities() - .stream() - .filter(NamedEntity::isFile) - .collect(Collectors.toList()); + List entities = + receivedFile.entities().stream().filter(NamedEntity::isFile).collect(Collectors.toList()); if (entities.isEmpty()) { throw new AippException(AippErrCode.NO_FILE_UPLOAD_ERROR); } @@ -270,8 +258,11 @@ public FormFileDto uploadSmartForm(PartitionedEntity receivedFile, String fileNa } Map schema = this.getSchema(this.getFile(files, CONFIG_JSON)); String tempDirPath = tempDir.toURI().getPath(); - return this.saveMetaData(this.removePrefix(tempDirPath), this.getFile(files, FORM_IMAGE).getName(), - cast(schema.get(SCHEMA)), decodedFileName, context); + return this.saveMetaData(this.removePrefix(tempDirPath), + this.getFile(files, FORM_IMAGE).getName(), + cast(schema.get(SCHEMA)), + decodedFileName, + context); } /** @@ -325,7 +316,9 @@ public FileEntity getSmartFormTemplate(HttpClassicServerRequest httpRequest, Ope throw new AippException(AippErrCode.FILE_EXPIRED_OR_BROKEN); } ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Files.readAllBytes(path)); - return FileEntity.createAttachment(httpRequest, TEMPLATE_ZIP, byteArrayInputStream, + return FileEntity.createAttachment(httpRequest, + TEMPLATE_ZIP, + byteArrayInputStream, byteArrayInputStream.available()); } @@ -333,12 +326,12 @@ public FileEntity getSmartFormTemplate(HttpClassicServerRequest httpRequest, Ope public FileEntity handle(String positionName, HttpClassicServerRequest request, HttpClassicServerResponse response) { String requestPath = request.path(); - int urlPathPrefixIndex = requestPath.indexOf(this.resourcePathPrefix); + int urlPathPrefixIndex = requestPath.indexOf(this.resourceUrlPrefix); if (urlPathPrefixIndex == -1 || requestPath.contains("..")) { log.error("Url is invalid. Url={}", requestPath); throw new IllegalArgumentException(requestPath); } - String formPath = requestPath.substring(urlPathPrefixIndex + this.resourcePathPrefix.length()); + String formPath = requestPath.substring(urlPathPrefixIndex + this.resourceUrlPrefix.length()); String handledFormFullPath = this.getFormFullPath(formPath); Path path = Paths.get(handledFormFullPath); if (!path.toFile().exists()) { @@ -355,13 +348,15 @@ public FileEntity handle(String positionName, HttpClassicServerRequest request, log.error("Url is incorrect. Url={}", request.path()); throw new IllegalArgumentException(request.path()); } - return FileEntity.createInline(request, formPath.substring(index + 1), byteArrayInputStream, + return FileEntity.createInline(request, + formPath.substring(index + 1), + byteArrayInputStream, byteArrayInputStream.available()); } @Override public boolean canHandle(String positionName, HttpClassicServerRequest request) { - int urlPathPrefixIndex = request.path().indexOf(this.resourcePathPrefix); + int urlPathPrefixIndex = request.path().indexOf(this.resourceUrlPrefix); return urlPathPrefixIndex != -1; } @@ -414,7 +409,9 @@ private void validateFormConstraintInfo() { } private void storeFormFile(String fileName, FileEntity file, File targetFile) { - log.info("fileName:{}, targetFile path:{}, targetFile name:{}", fileName, targetFile.getPath(), + log.info("fileName:{}, targetFile path:{}, targetFile name:{}", + fileName, + targetFile.getPath(), targetFile.getName()); File targetDirectory = targetFile.getParentFile(); try { @@ -425,7 +422,7 @@ private void storeFormFile(String fileName, FileEntity file, File targetFile) { throw new AippException(AippErrCode.ENSURE_FORM_DIRECTORY_FAILED, fileName); } try (InputStream inStream = file.getInputStream(); - OutputStream outStream = Files.newOutputStream(targetFile.toPath())) { + OutputStream outStream = Files.newOutputStream(targetFile.toPath())) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = inStream.read(buffer)) != -1) { @@ -461,14 +458,10 @@ private File[] getFiles(File tempDir) { } private List checkFiles(File[] files) { - List fileNames = Arrays.stream(files) - .filter(File::isFile) - .map(File::getName) - .collect(Collectors.toList()); - List directoryNames = Arrays.stream(files) - .filter(File::isDirectory) - .map(File::getName) - .collect(Collectors.toList()); + List fileNames = + Arrays.stream(files).filter(File::isFile).map(File::getName).collect(Collectors.toList()); + List directoryNames = + Arrays.stream(files).filter(File::isDirectory).map(File::getName).collect(Collectors.toList()); List missingFileNames = new ArrayList<>(); if (fileNames.stream().noneMatch(CONFIG_JSON::equals)) { missingFileNames.add(CONFIG_JSON); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/GenericableManageServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/GenericableManageServiceImpl.java index d0ac2ffdbd..cc9ed71280 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/GenericableManageServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/GenericableManageServiceImpl.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.dto.FitableInfoDto; import modelengine.fit.jober.aipp.service.GenericableManageService; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.broker.client.BrokerClient; import modelengine.fitframework.broker.client.filter.route.FitableIdFilter; @@ -62,13 +63,13 @@ public List getFitablesByGenerableId(String genericableId, int p @Override public List> executeInspirationFitable(String fitableId, String appId, String appType, - OperationContext operationContext) { + OperationContext operationContext) { final String genericableId = "d01041a73e00ac46bedde08d02c6818e"; List> res; try { res = this.client.getRouter(genericableId) - .route(new FitableIdFilter(fitableId)) - .invoke(new HashMap<>(), appId, appType, operationContext); + .route(new FitableIdFilter(fitableId)) + .invoke(new HashMap<>(), appId, appType, operationContext); } catch (FitException e) { log.error("Error occurred when running inspiration fitable, error: {}", e.getMessage()); throw new AippException(AippErrCode.EXECUTE_INSPIRATION_FITABLE_FAILED); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LLMServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LLMServiceImpl.java index 5147fee901..2a609d84c8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LLMServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LLMServiceImpl.java @@ -6,27 +6,20 @@ package modelengine.fit.jober.aipp.service.impl; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; import modelengine.fel.core.chat.ChatMessage; import modelengine.fel.core.chat.ChatModel; import modelengine.fel.core.chat.ChatOption; import modelengine.fel.core.chat.Prompt; import modelengine.fel.core.chat.support.ChatMessages; import modelengine.fel.core.chat.support.HumanMessage; -import modelengine.fit.http.client.HttpClassicClientFactory; import modelengine.fit.jober.aipp.enums.LlmModelNameEnum; import modelengine.fit.jober.aipp.service.LLMService; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; -import modelengine.fitframework.annotation.Value; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; -import modelengine.jade.voice.service.VoiceService; import java.util.List; @@ -42,22 +35,8 @@ public class LLMServiceImpl implements LLMService { private final ChatModel chatModel; - private final String appengineEndPoint; - - private final String pathPrefix; - - private final VoiceService voiceService; - - private final HttpClassicClientFactory httpClientFactory; - - public LLMServiceImpl(@Value("${app-engine.endpoint}") String endpoint, - @Value("${app-engine.pathPrefix}") String pathPrefix, - @Fit ChatModel chatModel, @Fit VoiceService voiceService, @Fit HttpClassicClientFactory httpClientFactory) { + public LLMServiceImpl(@Fit ChatModel chatModel) { this.chatModel = chatModel; - this.appengineEndPoint = endpoint; - this.pathPrefix = pathPrefix; - this.voiceService = voiceService; - this.httpClientFactory = httpClientFactory; } @Override diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LlmNodeChecker.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LlmNodeChecker.java index ca8708997e..f626208d82 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LlmNodeChecker.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/LlmNodeChecker.java @@ -47,7 +47,7 @@ public LlmNodeChecker(AippModelCenter fetchModelService, PluginToolService plugi public List validate(AppCheckDto appCheckDto, OperationContext context) { List results = this.initialResults(appCheckDto, LLM_NODE.type()); Map resultMap = results.stream() - .collect(Collectors.toMap(CheckResult::getNodeId, result -> result)); + .collect(Collectors.toMap(CheckResult::getNodeId, result -> result)); List modelInfos = fetchModelService.fetchModelList(AippConst.CHAT_MODEL_TYPE, null, context).getModels(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ManualCheckNodeChecker.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ManualCheckNodeChecker.java index 40d200ee6a..90a36518ff 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ManualCheckNodeChecker.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ManualCheckNodeChecker.java @@ -11,6 +11,7 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; + import modelengine.fitframework.annotation.Component; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/OperatorServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/OperatorServiceImpl.java index 1cf068697f..93f14c6659 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/OperatorServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/OperatorServiceImpl.java @@ -96,23 +96,17 @@ public class OperatorServiceImpl implements OperatorService { }; private final LLMService llmService; - private final BrokerClient client; - private final Function pdfExtractor = this::extractPdfFile; - private final Function excelExtractor = this::extractExcelFile; - private final Function wordExtractor = this::extractWordFile; - private final Function textExtractor = this::extractTextFile; - - private final EnumMap> outlineOperatorMap - = new EnumMap>(FileType.class) { - { - put(FileType.WORD, docOutlineExtractor); - } - }; + private final EnumMap> outlineOperatorMap = + new EnumMap>(FileType.class) { + { + put(FileType.WORD, docOutlineExtractor); + } + }; private final EnumMap> fileOperatorMap = new EnumMap>(FileType.class) { @@ -155,7 +149,7 @@ private static String getCellValueAsString(Cell cell) { private static String extractDocHandle(InputStream fis, String fileName) throws IOException { try (XWPFDocument doc = new XWPFDocument(fis); - XWPFWordExtractor xwpfWordExtractor = new XWPFWordExtractor(doc)) { + XWPFWordExtractor xwpfWordExtractor = new XWPFWordExtractor(doc)) { // 去页眉页脚 doc.getHeaderList().forEach(h -> h.setHeaderFooter(newCTHdrFtrInstance())); doc.getFooterList().forEach(h -> h.setHeaderFooter(newCTHdrFtrInstance())); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RetrievalNodeChecker.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RetrievalNodeChecker.java index d200233b27..c8d31bd753 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RetrievalNodeChecker.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RetrievalNodeChecker.java @@ -9,6 +9,7 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; + import modelengine.fitframework.annotation.Component; import java.util.List; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RuntimeInfoServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RuntimeInfoServiceImpl.java index e8fb35b891..fa700ca5d5 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RuntimeInfoServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/RuntimeInfoServiceImpl.java @@ -11,24 +11,29 @@ import static modelengine.fitframework.util.ObjectUtils.cast; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; import modelengine.fit.jober.aipp.service.RuntimeInfoService; import modelengine.fit.jober.aipp.util.ConvertUtils; import modelengine.fit.jober.aipp.util.DataUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.util.MetaInstanceUtils; -import modelengine.fit.jober.aipp.util.MetaUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; import modelengine.fit.jober.entity.consts.NodeTypes; +import modelengine.fit.jober.util.FlowDataUtils; import modelengine.fit.runtime.entity.Parameter; + +import lombok.AllArgsConstructor; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; @@ -38,7 +43,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -49,57 +53,30 @@ * @since 2024-12-17 */ @Component +@AllArgsConstructor public class RuntimeInfoServiceImpl implements RuntimeInfoService { - private final MetaService metaService; - - private final AppBuilderAppFactory appFactory; - - private final MetaInstanceService metaInstanceService; - private final AppBuilderRuntimeInfoRepository runtimeInfoRepository; + private final AppTaskService appTaskService; + private final AppTaskInstanceService appTaskInstanceService; + private final AppVersionService appVersionService; - /** - * 构造函数. - * - * @param metaService {@link MetaService} 对象. - * @param appFactory {@link AppBuilderAppFactory} app工厂对象. - * @param metaInstanceService {@link MetaInstanceService} 对象。 - * @param runtimeInfoRepository {@link AppBuilderRuntimeInfoRepository} 对象。 - */ - public RuntimeInfoServiceImpl(MetaService metaService, AppBuilderAppFactory appFactory, - MetaInstanceService metaInstanceService, AppBuilderRuntimeInfoRepository runtimeInfoRepository) { - this.metaService = metaService; - this.appFactory = appFactory; - this.metaInstanceService = metaInstanceService; - this.runtimeInfoRepository = runtimeInfoRepository; - } - - /** - * 根据业务数据判断应用是否已发布。 - * - * @param businessData 业务数据。 - * @return 是否已发布。 - */ + @Override public boolean isPublished(Map businessData) { - String aippId = cast(businessData.get(AippConst.BS_AIPP_ID_KEY)); - String version = cast(businessData.get(AippConst.BS_AIPP_VERSION_KEY)); - Meta meta = MetaUtils.getAnyMeta(this.metaService, aippId, version, DataUtils.getOpContext(businessData)); - String appId = cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - AppBuilderApp app = this.appFactory.create(appId); - return app.isPublished(); + String aippId = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_ID_KEY)); + String version = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_VERSION_KEY)); + AppTask task = this.appTaskService.getLatest(aippId, version, DataUtils.getOpContext(businessData)) + .orElseThrow(() -> new AippException(AippErrCode.APP_NOT_FOUND, + StringUtils.format("App task not found, appSuiteId:{0}, version: {1}.", aippId, version))); + String appId = task.getEntity().getAppId(); + AppVersion appVersion = this.appVersionService.retrieval(appId); + return appVersion.isPublished(); } - /** - * 构建参数集合 - * - * @param map 业务数据。 - * @param nodeId 节点id。 - * @return 构建的参数集合 - */ + @Override public List buildParameters(Map map, String nodeId) { // 如果根据nodeId找不到,则说明节点没有出入参. List> executeInfos = cast( - this.getValueByKeys(map, Arrays.asList("_internal", "executeInfo", nodeId), List.class) + FlowDataUtils.getValueByKeyPath(map, Arrays.asList("_internal", "executeInfo", nodeId), List.class) .orElseGet(Collections::emptyList)); if (executeInfos.isEmpty()) { return Collections.emptyList(); @@ -115,24 +92,24 @@ public List buildParameters(Map map, String nodeId) { @Override public void insertRuntimeInfo(String instanceId, Map map, MetaInstStatusEnum status, String errorMsg, OperationContext context) { - String versionId = this.metaInstanceService.getMetaVersionId(instanceId); - Instance instDetail = MetaInstanceUtils.getInstanceDetail(versionId, instanceId, context, - this.metaInstanceService); - String flowTraceId = instDetail.getInfo().get(AippConst.INST_FLOW_INST_ID_KEY); - Meta meta = this.metaService.retrieve(versionId, context); - String appId = cast(meta.getAttributes().get(AippConst.ATTR_APP_ID_KEY)); - AppBuilderApp app = this.appFactory.create(appId); + AppTaskInstance instance = this.appTaskInstanceService.getInstanceById(instanceId, context) + .orElseThrow(() -> new JobberException(ErrorCodes.UN_EXCEPTED_ERROR, + StringUtils.format("App task instance[{0}] not found.", instanceId))); + String flowTraceId = instance.getEntity().getFlowTranceId(); + AppTask appTask = this.appTaskService.getTaskById(instance.getTaskId(), context) + .orElseThrow(() -> new AippException(AippErrCode.TASK_NOT_FOUND)); + String appId = appTask.getEntity().getAppId(); + AppVersion appVersion = this.appVersionService.retrieval(appId); String nodeId = cast(map.getOrDefault(BS_NODE_ID_KEY, StringUtils.EMPTY)); AppBuilderRuntimeInfo runtimeInfo = AppBuilderRuntimeInfo.builder() .traceId(flowTraceId) - .flowDefinitionId( - cast(meta.getAttributes().getOrDefault(AippConst.ATTR_FLOW_DEF_ID_KEY, StringUtils.EMPTY))) + .flowDefinitionId(appTask.getEntity().getFlowDefinitionId()) .instanceId(instanceId) .nodeId(nodeId) .nodeType(NodeTypes.STATE.getType()) .startTime(cast(map.getOrDefault(NODE_START_TIME_KEY, ConvertUtils.toLong(LocalDateTime.now())))) .endTime(ConvertUtils.toLong(LocalDateTime.now())) - .published(app.isPublished()) + .published(appVersion.isPublished()) .parameters(this.buildParameters(map, nodeId)) .errorMsg(errorMsg) .status(status.name()) @@ -143,19 +120,4 @@ public void insertRuntimeInfo(String instanceId, Map map, MetaIn .build(); this.runtimeInfoRepository.insertOne(runtimeInfo); } - - private Optional getValueByKeys(Map map, List keys, Class clz) { - Map tmp = map; - for (int i = 0; i < keys.size() - 1; i++) { - if (tmp.get(keys.get(i)) instanceof Map) { - tmp = cast(tmp.get(keys.get(i))); - } else { - tmp = null; - } - if (Objects.isNull(tmp)) { - return Optional.empty(); - } - } - return Optional.ofNullable(ObjectUtils.as(tmp.get(keys.get(keys.size() - 1)), clz)); - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/StatisticsServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/StatisticsServiceImpl.java index ef7f9c93b5..cb181888ab 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/StatisticsServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/StatisticsServiceImpl.java @@ -6,9 +6,6 @@ package modelengine.fit.jober.aipp.service.impl; -import modelengine.jade.store.service.PluginService; -import modelengine.jade.store.service.support.DeployStatus; - import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.condition.AppQueryCondition; import modelengine.fit.jober.aipp.dto.StatisticsDTO; @@ -17,6 +14,9 @@ import modelengine.fit.jober.aipp.service.AppBuilderAppService; import modelengine.fit.jober.aipp.service.AppBuilderFormService; import modelengine.fit.jober.aipp.service.StatisticsService; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.support.DeployStatus; + import modelengine.fitframework.annotation.Component; /** @@ -30,9 +30,7 @@ public class StatisticsServiceImpl implements StatisticsService { private static final String RUNTIME = "runtime"; private final PluginService pluginService; - private final AppBuilderFormService appBuilderFormService; - private final AppBuilderAppService appBuilderAppService; public StatisticsServiceImpl(PluginService pluginService, AppBuilderFormService appBuilderFormService, diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ToolInvokeNodeChecker.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ToolInvokeNodeChecker.java index 14fd25fd77..66f2635212 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ToolInvokeNodeChecker.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/ToolInvokeNodeChecker.java @@ -38,9 +38,9 @@ public ToolInvokeNodeChecker(PluginToolService pluginToolService) { public List validate(AppCheckDto appCheckDto, OperationContext context) { List results = initialResults(appCheckDto, TOOL_INVOKE_NODE.type()); Map resultMap = results.stream() - .collect(Collectors.toMap(CheckResult::getNodeId, result -> result)); + .collect(Collectors.toMap(CheckResult::getNodeId, result -> result)); Map toolResults = this.getToolResult(pluginToolService, - this.getAllUniqueNames(appCheckDto, TOOL_NAME)); + this.getAllUniqueNames(appCheckDto, TOOL_NAME)); appCheckDto.getNodeInfos().forEach(nodeInfo -> { this.checkTool(nodeInfo, TOOL_NAME, resultMap, toolResults); }); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/UploadedFileMangeServiceImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/UploadedFileMangeServiceImpl.java index 01eef26739..11aa78917f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/UploadedFileMangeServiceImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/impl/UploadedFileMangeServiceImpl.java @@ -6,20 +6,26 @@ package modelengine.fit.jober.aipp.service.impl; +import modelengine.fit.jane.task.util.Entities; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.dto.aipplog.AippUploadedFileInfoDto; import modelengine.fit.jober.aipp.mapper.AippUploadedFileMapper; import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.util.AippFileUtils; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.schedule.annotation.Scheduled; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.FileUtils; +import modelengine.fitframework.util.IoUtils; import java.io.File; +import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.stream.Stream; /** @@ -120,8 +126,10 @@ private List batchDeleteFiles(List fileNames) { @Override public void addFileRecord(String aipp, String createUserAccount, String filename, String fileUuid) { if (Stream.of(createUserAccount, filename).allMatch(s -> s != null && !s.isEmpty())) { - aippUploadedFileMapper.insertFileRecord( - new AippUploadedFileInfoDto(aipp, createUserAccount, filename, fileUuid)); + aippUploadedFileMapper.insertFileRecord(new AippUploadedFileInfoDto(aipp, + createUserAccount, + filename, + fileUuid)); } } @@ -145,4 +153,20 @@ public void changeRemovableWithFileUuid(String fileUuid, Integer status) { public void updateRecord(String appId, String fileName, Integer status) { aippUploadedFileMapper.updateRecord(appId, fileName, status); } + + @Override + public String copyIconFiles(String icon, String aippId, String operator) throws IOException { + File originIcon = FileUtils.canonicalize(AippFileUtils.getFileNameFromIcon(icon)); + String originIconName = originIcon.getName(); + String copiedIconName = UUID.randomUUID() + FileUtils.extension(originIconName); + File copiedIcon = FileUtils.canonicalize(originIcon.getCanonicalPath().replace(originIconName, copiedIconName)); + IoUtils.copy(originIcon, copiedIcon); + this.addFileRecord( + aippId, + operator, + copiedIcon.getCanonicalPath(), + Entities.generateId() + ); + return icon.replace(originIconName, copiedIconName); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java new file mode 100644 index 0000000000..f514edb09e --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleaner.java @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import com.opencsv.CSVWriter; + +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.CollectionUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import static modelengine.fit.jober.aipp.service.scheduletask.AppBuilderDbCleanScheduler.FILE_MAX_NUM; + +/** + * 应用实例日志清理器。 + * + * @author 杨祥宇 + * @since 2025-04-15 + */ +@Component +public class AippInstanceLogCleaner { + private static final Logger log = Logger.get(AippInstanceLogCleaner.class); + private static final String FILE_NAME = "aipp-instance-log"; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final String CONNECTOR = "-"; + + private final AippInstanceLogRepository instanceLogRepo; + private final CsvWriterHelper csvWriterHelper; + private final String aippInstanceLogFilePath; + + /** + * 表示用应用日志仓库实例构造 {@link AippInstanceLogCleaner} 的实例。 + * + * @param instanceLogRepo 表示应用日志仓库实例的 {@link AippInstanceLogRepository}。 + * @param csvWriterHelper 表示文件写入助手实例的 {@link CsvWriterHelper}。 + * @param aippInstanceLogFilePath 表示日志文件备份路径的 {@link String}。 + */ + public AippInstanceLogCleaner(AippInstanceLogRepository instanceLogRepo, CsvWriterHelper csvWriterHelper, + @Value("${aipp.instance.log.file.path}") String aippInstanceLogFilePath) { + this.instanceLogRepo = instanceLogRepo; + this.csvWriterHelper = csvWriterHelper; + this.aippInstanceLogFilePath = aippInstanceLogFilePath; + } + + /** + * 清理已发布的应用对话历史记录表数据,并备份。 + * + * @param expiredDays 表示数据最大保留时长的 {@code int}。 + * @param limit 表示批量处理数量的 {@code int}。 + */ + public void cleanAippInstanceNormalLog(int expiredDays, int limit) { + try { + while (true) { + List instanceLogIds = + this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), expiredDays, limit); + if (instanceLogIds.isEmpty()) { + break; + } + backupData(instanceLogIds); + this.instanceLogRepo.forceDeleteInstanceLogs(instanceLogIds); + } + cleanupOldBackups(FILE_MAX_NUM); + } catch (Exception e) { + log.error("Error occurred while business data cleaner, exception:.", e); + } + } + + private void backupData(List logIds) { + List aippInstLogs = this.instanceLogRepo.selectByLogIds(logIds); + if (CollectionUtils.isEmpty(aippInstLogs)) { + return; + } + String currentDate = LocalDate.now().format(DATE_FORMATTER); + Path backupPath = Paths.get(this.aippInstanceLogFilePath, FILE_NAME + CONNECTOR + currentDate + ".csv"); + try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) { + List backupData = aippInstLogs.stream().map(aippInstLog -> new String[] { + String.valueOf(aippInstLog.getLogId()), aippInstLog.getAippId(), aippInstLog.getVersion(), + aippInstLog.getInstanceId(), aippInstLog.getLogData(), aippInstLog.getLogType(), + String.valueOf(aippInstLog.getCreateAt()), aippInstLog.getCreateUserAccount(), aippInstLog.getPath() + }).toList(); + csvWriter.writeAll(backupData); + } catch (IOException e) { + log.error("Error occurred while writing aipp-instance-log.", e); + throw new IllegalStateException(e); + } + } + + private void cleanupOldBackups(int fileMaxNum) { + File backupFolder = this.csvWriterHelper.getFile(this.aippInstanceLogFilePath); + File[] backupFiles = backupFolder.listFiles((dir, name) -> name.startsWith(FILE_NAME) && name.endsWith(".csv")); + if (backupFiles == null) { + return; + } + List sortedFiles = + Arrays.stream(backupFiles).sorted(Comparator.comparing(File::getName).reversed()).toList(); + for (int i = fileMaxNum; i < sortedFiles.size(); i++) { + sortedFiles.get(i).delete(); + } + } + + /** + * 清理调试应用对话历史记录表数据。 + * + * @param expiredDays 表示数据最大保留时长的 {@code int}。 + * @param limit 表示批量处理数量的 {@code int}。 + */ + public void cleanAippInstancePreviewLog(int expiredDays, int limit) { + log.info("Start cleaning aipp preview instance logs"); + try { + while (true) { + List instanceLogIds = + this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.PREVIEW.type(), expiredDays, limit); + if (instanceLogIds.isEmpty()) { + break; + } + this.instanceLogRepo.forceDeleteInstanceLogs(instanceLogIds); + } + } catch (Exception e) { + log.error("clean instance logs failed, exception:", e); + } + log.info("Finish cleaning aipp instance logs"); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java new file mode 100644 index 0000000000..08729ddf36 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderDbCleanScheduler.java @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.schedule.annotation.Scheduled; + +/** + * 数据库清理定时任务执行器。 + * + * @author 杨祥宇 + * @since 2025-04-09 + */ +@Component +public class AppBuilderDbCleanScheduler { + private static final Logger log = Logger.get(AppBuilderDbCleanScheduler.class); + + /** + * 表示待清理的数据行数上限。 + */ + private static final int LIMIT = 1000; + + /** + * 表示备份文件的最大数量。 + */ + public static final int FILE_MAX_NUM = 15; + + private final int nonBusinessDataTtl; + private final int businessDataTtl; + private final AippInstanceLogCleaner aippInstanceLogCleaner; + private final ChatSessionCleaner chatSessionCleaner; + private final AppBuilderRuntimeInfoCleaner appBuilderRuntimeInfoCleaner; + + /** + * 表示用对话清理器和运行时日志清理器构造 {@link AppBuilderDbCleanScheduler} 的实例。 + * + * @param nonBusinessDataTtl 表示非业务数据的过期时间的 {@link String}。 + * @param businessDataTtl 表示业务数据的过期时间的 {@link String}。 + * @param aippInstanceLogCleaner 表示日志清理器的 {@link AippInstanceLogCleaner}。 + * @param chatSessionCleaner 表示对话清理器的 {@link ChatSessionCleaner}。 + * @param appBuilderRuntimeInfoCleaner 表示运行时信息清理器的 {@link AppBuilderRuntimeInfoCleaner}。 + */ + public AppBuilderDbCleanScheduler(@Value("${app-engine.ttl.nonBusinessData}") int nonBusinessDataTtl, + @Value("${app-engine.ttl.businessData}") int businessDataTtl, AippInstanceLogCleaner aippInstanceLogCleaner, + ChatSessionCleaner chatSessionCleaner, AppBuilderRuntimeInfoCleaner appBuilderRuntimeInfoCleaner) { + this.nonBusinessDataTtl = nonBusinessDataTtl; + this.businessDataTtl = businessDataTtl; + this.aippInstanceLogCleaner = aippInstanceLogCleaner; + this.chatSessionCleaner = chatSessionCleaner; + this.appBuilderRuntimeInfoCleaner = appBuilderRuntimeInfoCleaner; + } + + /** + * 每天凌晨 3 点定时清理超期指定天数的应用相关数据。 + */ + @Scheduled(strategy = Scheduled.Strategy.CRON, value = "0 0 3 * * ?") + public void appBuilderDbCleanSchedule() { + try { + // 清理非业务数据 + this.aippInstanceLogCleaner.cleanAippInstancePreviewLog(this.nonBusinessDataTtl, LIMIT); + this.appBuilderRuntimeInfoCleaner.clean(this.nonBusinessDataTtl, LIMIT); + + // 清理业务数据 + this.aippInstanceLogCleaner.cleanAippInstanceNormalLog(this.businessDataTtl, LIMIT); + this.chatSessionCleaner.clean(this.businessDataTtl, LIMIT); + } catch (Exception e) { + log.error("App builder Db Clean Error, exception:", e); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java new file mode 100644 index 0000000000..1e3edbba45 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/AppBuilderRuntimeInfoCleaner.java @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.log.Logger; + +import java.util.List; + +/** + * 应用编排运行时信息清理器。 + * + * @author 杨祥宇 + * @since 2025-04-15 + */ +@Component +public class AppBuilderRuntimeInfoCleaner { + private static final Logger log = Logger.get(AppBuilderRuntimeInfoCleaner.class); + + private final AppBuilderRuntimeInfoRepository runtimeInfoRepo; + + public AppBuilderRuntimeInfoCleaner(AppBuilderRuntimeInfoRepository runtimeInfoRepo) { + this.runtimeInfoRepo = runtimeInfoRepo; + } + + /** + * 清理对话运行时表数据,并备份。 + * + * @param expiredDays 表示数据最大保留时长的 {@code int}。 + * @param limit 表示批量处理数量的 {@code int}。 + */ + public void clean(int expiredDays, int limit) { + log.info("Start cleaning app builder runtime infos"); + try { + while (true) { + List expiredRuntimeInfoIds = this.runtimeInfoRepo.getExpiredRuntimeInfos(expiredDays, limit); + if (expiredRuntimeInfoIds.isEmpty()) { + break; + } + this.runtimeInfoRepo.deleteRuntimeInfos(expiredRuntimeInfoIds); + } + } catch (Exception e) { + log.error("cleaning app builder runtime infos failed, exception:", e); + } + log.info("Finish cleaning app builder runtime infos"); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java new file mode 100644 index 0000000000..036d2f8301 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleaner.java @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import static modelengine.fit.jober.aipp.service.scheduletask.AppBuilderDbCleanScheduler.FILE_MAX_NUM; + +import com.opencsv.CSVWriter; + +import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; +import modelengine.fit.jober.aipp.entity.ChatInfo; +import modelengine.fit.jober.aipp.repository.AippChatRepository; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.util.CollectionUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * 聊天会话数据库表清理器。 + * + * @author 杨祥宇 + * @since 2025-04-15 + */ +@Component +public class ChatSessionCleaner { + private static final Logger log = Logger.get(AippInstanceLogCleaner.class); + private static final String CHAT_SESSION_FILE_NAME = "chat-session"; + private static final String INSTANCE_RELATIONS_FILE_NAME = "instance-relations"; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final String CONNECTOR = "-"; + + private final AippChatRepository chatRepo; + private final CsvWriterHelper csvWriterHelper; + private final String chatSessionFilePath; + + /** + * 表示用对话仓库和文件写入助手来构造 {@link ChatSessionCleaner} 的实例。 + * + * @param chatRepo 表示对话仓库实例的 {@link AippChatRepository}。 + * @param csvWriterHelper 表示文件写入助手实例的 {@link CsvWriterHelper}。 + * @param chatSessionFilePath 表示对话文件路径的 {@link String}。 + */ + public ChatSessionCleaner(AippChatRepository chatRepo, CsvWriterHelper csvWriterHelper, + @Value("${chat.session.file.path}") String chatSessionFilePath) { + this.chatRepo = chatRepo; + this.csvWriterHelper = csvWriterHelper; + this.chatSessionFilePath = chatSessionFilePath; + } + + /** + * 清理对话会话相关数据,并备份。 + * + * @param expiredDays 表示数据最大保留天数的 {@code int}。 + * @param limit 表示批量处理数量的 {@code int}。 + */ + public void clean(int expiredDays, int limit) { + try { + while (true) { + List expiredChatIds = this.chatRepo.getExpiredChatIds(expiredDays, limit); + if (expiredChatIds.isEmpty()) { + break; + } + backupChatSessionData(expiredChatIds); + backupInstanceRelationData(expiredChatIds); + this.chatRepo.forceDeleteChat(expiredChatIds); + } + cleanupOldBackups(CHAT_SESSION_FILE_NAME, FILE_MAX_NUM); + cleanupOldBackups(INSTANCE_RELATIONS_FILE_NAME, FILE_MAX_NUM); + } catch (Exception e) { + log.error("Error occurred while business data cleaner, exception:.", e); + } + } + + private void backupChatSessionData(List chatIds) { + List chatSessionPos = this.chatRepo.selectByChatIds(chatIds); + if (CollectionUtils.isEmpty(chatSessionPos)) { + return; + } + String currentDate = LocalDate.now().format(DATE_FORMATTER); + Path backupPath = Paths.get(this.chatSessionFilePath, CHAT_SESSION_FILE_NAME + CONNECTOR + currentDate + ".csv"); + try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) { + List backupData = chatSessionPos.stream().map(session -> new String[]{ + session.getChatId(), session.getAppId(), session.getVersion(), session.getChatName(), + session.getAttributes(), String.valueOf(session.getCreateTime()), session.getCreator(), + String.valueOf(session.getUpdateTime()), session.getUpdater() + }).toList(); + csvWriter.writeAll(backupData); + } catch (IOException e) { + log.error("Error occurred while backup chat session data, exception:", e); + throw new RuntimeException(e); + } + } + + private void backupInstanceRelationData(List chatIds) { + List relationPos = this.chatRepo.selectTaskInstanceRelationsByChatIds(chatIds); + if (CollectionUtils.isEmpty(relationPos)) { + return; + } + String currentDate = LocalDate.now().format(DATE_FORMATTER); + Path backupPath = Paths.get(this.chatSessionFilePath, INSTANCE_RELATIONS_FILE_NAME + CONNECTOR + currentDate + ".csv"); + try (CSVWriter csvWriter = this.csvWriterHelper.createCsvWriter(backupPath, true)) { + List backupData = relationPos.stream().map(relationPo -> new String[]{ + relationPo.getMsgId(), relationPo.getChatId(), relationPo.getInstanceId(), + String.valueOf(relationPo.getCreateTime()), relationPo.getCreator(), + String.valueOf(relationPo.getUpdateTime()), relationPo.getUpdater() + }).toList(); + csvWriter.writeAll(backupData); + } catch (IOException e) { + log.error("Error occurred while backup instance relation data, exception:", e); + throw new RuntimeException(e); + } + } + + private void cleanupOldBackups(String fileName, int fileMaxNum) { + try { + File backupFolder = this.csvWriterHelper.getFile(this.chatSessionFilePath); + File[] backupFiles = + backupFolder.listFiles((dir, name) -> name.startsWith(fileName) && name.endsWith(".csv")); + if (backupFiles == null) { + return; + } + List sortedFiles = + Arrays.stream(backupFiles).sorted(Comparator.comparing(File::getName).reversed()).toList(); + for (int i = fileMaxNum; i < sortedFiles.size(); i++) { + sortedFiles.get(i).delete(); + } + } catch (Exception e) { + log.error("Cleanup old backups failed, filename:{}, exception:", fileName, e); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java new file mode 100644 index 0000000000..6d33098801 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/service/scheduletask/CsvWriterHelper.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import com.opencsv.CSVWriter; + +import modelengine.fitframework.annotation.Component; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Path; + +/** + * csv 文件写入助手。 + * + * @author 杨祥宇 + * @since 2025-04-18 + */ +@Component +public class CsvWriterHelper { + /** + * 生成 CsvWriter 对象。 + * + * @param path 表示文件路径的 {@link Path}。 + * @param isAppend 表示是否追加的 {@link Boolean}。 + * @return 表示生成 CSVWriter类的 {@link CSVWriter}。 + * @throws IOException 表示可能抛出异常的 {@link IOException}。 + */ + public CSVWriter createCsvWriter(Path path, boolean isAppend) throws IOException { + return new CSVWriter(new FileWriter(path.toFile(), isAppend)); + } + + /** + * 生成 File 对象。 + * + * @param path 表示文件路径的 {@link Path}。 + * @return 表示生成 File 对象的 {@link File}。 + */ + public File getFile(String path) { + return new File(path); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AppBuilderAppToolImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AppBuilderAppToolImpl.java index a3145890f9..bf6d2940f9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AppBuilderAppToolImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AppBuilderAppToolImpl.java @@ -6,11 +6,11 @@ package modelengine.fit.jober.aipp.tool.impl; -import modelengine.fel.core.formatters.OutputParser; -import modelengine.fel.core.formatters.json.JsonOutputParser; -import modelengine.fel.core.formatters.support.MarkdownParser; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.dto.AppBuilderConfigDto; @@ -19,6 +19,10 @@ import modelengine.fit.jober.aipp.enums.AppTypeEnum; import modelengine.fit.jober.aipp.service.AppBuilderAppService; import modelengine.fit.jober.aipp.tool.AppBuilderAppTool; + +import modelengine.fel.core.formatters.OutputParser; +import modelengine.fel.core.formatters.json.JsonOutputParser; +import modelengine.fel.core.formatters.support.MarkdownParser; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; @@ -42,39 +46,29 @@ @Component public class AppBuilderAppToolImpl implements AppBuilderAppTool { private static final String DEFAULT_TENANT_ID = "31f20efc7e0848deab6a6bc10fc3021e"; - private static final String SYSTEM_PROMPT_KEY = "systemPrompt"; - private static final String DEFAULT_TEMPLATE_ID = "df87073b9bc85a48a9b01eccc9afccc4"; - - private static final String INDEX_URL_FORMAT - = "应用创建成功! \n访问地址:{0}//#//app-develop//31f20efc7e0848deab6a6bc10fc3021e//app-detail//{1}"; - + private static final String INDEX_URL_FORMAT = + "应用创建成功! \n访问地址:{0}//#//app-develop//31f20efc7e0848deab6a6bc10fc3021e//app-detail//{1}"; private static final Logger log = Logger.get(AppBuilderAppToolImpl.class); - private static final String APP_BUILT_TYPE = "app"; - private static final String APP_CATEGORY = "chatbot"; private final AppBuilderAppService appService; - private final String appEngineUrl; - private final ObjectSerializer objectSerializer; + private final AppVersionService appVersionService; + private final ConverterFactory converterFactory; - /** - * 构造函数 - * - * @param appService app服务 - * @param objectSerializer 序列化器 - * @param appEngineUrl app引擎url - */ public AppBuilderAppToolImpl(AppBuilderAppService appService, @Fit(alias = "json") ObjectSerializer objectSerializer, - @Value("${app-engine.endpoint}") String appEngineUrl) { + @Value("${app-engine.endpoint}") String appEngineUrl, AppVersionService appVersionService, + ConverterFactory converterFactory) { this.appService = appService; this.appEngineUrl = appEngineUrl; this.objectSerializer = objectSerializer; + this.appVersionService = appVersionService; + this.converterFactory = converterFactory; } @Override @@ -82,8 +76,9 @@ public AppBuilderAppToolImpl(AppBuilderAppService appService, public String createApp(String appInfo, String userId) { AppCreateToolDto dto; try { - OutputParser parser = new MarkdownParser<>( - JsonOutputParser.create(this.objectSerializer, AppCreateToolDto.class), "json"); + OutputParser parser = + new MarkdownParser<>(JsonOutputParser.create(this.objectSerializer, AppCreateToolDto.class), + "json"); dto = parser.parse(appInfo); } catch (SerializationException exception) { log.error("Failed to create app, parse json str error: {}", appInfo, exception); @@ -106,7 +101,8 @@ public String createApp(String appInfo, String userId) { OperationContext context = this.buildOperationContext(userId); AppBuilderAppDto appDto; try { - appDto = this.appService.create(DEFAULT_TEMPLATE_ID, this.convert(dto), context, false); + AppVersion appVersion = this.appVersionService.create(DEFAULT_TEMPLATE_ID, this.convert(dto), context); + appDto = this.converterFactory.convert(appVersion, AppBuilderAppDto.class); } catch (AippException exception) { log.error("Failed to create app: {}", exception.getMessage(), exception); return "创建应用失败:" + exception.getMessage(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AudioExtractor.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AudioExtractor.java index e9e568823a..c25c41846f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AudioExtractor.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/AudioExtractor.java @@ -8,9 +8,6 @@ import static modelengine.fit.jober.aipp.constant.AippConstant.NAS_SHARE_DIR; -import modelengine.jade.voice.service.VoiceService; - -import modelengine.fel.core.chat.ChatModel; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.dto.audio.AudioSplitInfo; @@ -24,6 +21,9 @@ import modelengine.fit.jober.aipp.util.JsonUtils; import modelengine.fit.jober.aipp.util.LLMUtils; import modelengine.fit.jober.aipp.util.UUIDUtil; +import modelengine.jade.voice.service.VoiceService; + +import modelengine.fel.core.chat.ChatModel; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; @@ -68,21 +68,18 @@ public class AudioExtractor implements FileExtractor { private static final String TMP_DIR_PREFIX = "audioTmp-"; - private static final ExecutorService SUMMARY_EXECUTOR = new ThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>()); + private static final ExecutorService SUMMARY_EXECUTOR = + new ThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); private final ChatModel openAiClient; - private final VoiceService voiceService; - private final String endpoint; - private final String pathPrefix; - private final FfmpegService ffmpegService; - public AudioExtractor(FfmpegService ffmpegService, @Fit ChatModel openAiClient, @Fit VoiceService voiceService, - @Value("${app-engine.endpoint}") String endpoint, @Value("${app-engine.pathPrefix}") String pathPrefix) { + public AudioExtractor(FfmpegService ffmpegService, @Fit ChatModel openAiClient, + @Fit VoiceService voiceService, @Value("${app-engine.endpoint}") String endpoint, + @Value("${app-engine.pathPrefix}") String pathPrefix) { this.ffmpegService = ffmpegService; this.openAiClient = openAiClient; this.voiceService = voiceService; @@ -100,8 +97,8 @@ private SummaryDto batchSummary(List audioList, int segmentSize) throws In SUMMARY_EXECUTOR.execute(() -> { try { File audio = audioList.get(id); - String audioPath = AippFileUtils.getFileDownloadFilePath(endpoint, this.pathPrefix, - audio.getPath()); + String audioPath = AippFileUtils.getFileDownloadFilePath( + endpoint, this.pathPrefix, audio.getPath()); log.info("audio filePath: {}, audio fileName: {}", audioPath, audio.getName()); String text = voiceService.getText(audioPath + "&fileName=" + audio.getName()); String summary = LLMUtils.askModelForSummary(openAiClient, String.format(PROMPT, text), @@ -117,8 +114,10 @@ private SummaryDto batchSummary(List audioList, int segmentSize) throws In countDownLatch.await(); SummaryDto summaryDto = generateSummary(output, segmentSize); long endTime = System.currentTimeMillis(); - log.info("Summarize {} task use time {} seconds, segment size: {} seconds.", taskCnt, - (endTime - startTime) / 1000, segmentSize); + log.info("Summarize {} task use time {} seconds, segment size: {} seconds.", + taskCnt, + (endTime - startTime) / 1000, + segmentSize); return summaryDto; } @@ -129,8 +128,8 @@ private SummaryDto generateSummary(List output, int segmentSize) { try { String llmOutput = LLMUtils.askModelForSummary(openAiClient, String.format(PROMPT, sb), LlmModelNameEnum.QWEN_72B, 16000); - SummarySection section = JsonUtils.parseObject(LLMUtils.tryFixLlmJsonString(llmOutput), - SummarySection.class); + SummarySection section = + JsonUtils.parseObject(LLMUtils.tryFixLlmJsonString(llmOutput), SummarySection.class); summaryDto.setSummary(section.getText()); } catch (IOException e) { log.error("Llm generate unexpect rsp, msg: {}.", e); @@ -148,7 +147,8 @@ private AudioSplitInfo covertAudio(String dirName, File audio) throws IOExceptio int segmentCount = Math.max(1, Math.min(meta.getDuration() / 300, 8)); int segmentSize = (meta.getDuration() + segmentCount - 1) / segmentCount; ffmpegService.splitAudio(audio.getCanonicalPath(), - targetDir.getCanonicalPath() + "/split_%03d." + meta.getVideoExt(), segmentSize); + targetDir.getCanonicalPath() + "/split_%03d." + meta.getVideoExt(), + segmentSize); FileUtils.delete(copyAudio); return new AudioSplitInfo(targetDir.getCanonicalPath(), segmentSize); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/StableDiffusionParserToolImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/StableDiffusionParserToolImpl.java index b50f5b6637..6de732fc95 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/StableDiffusionParserToolImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/tool/impl/StableDiffusionParserToolImpl.java @@ -6,10 +6,11 @@ package modelengine.fit.jober.aipp.tool.impl; -import modelengine.fel.core.formatters.json.JsonOutputParser; -import modelengine.fel.core.pattern.Parser; import modelengine.fit.jober.aipp.dto.image.StableDiffusionInput; import modelengine.fit.jober.aipp.tool.StableDiffusionParserTool; + +import modelengine.fel.core.formatters.json.JsonOutputParser; +import modelengine.fel.core.pattern.Parser; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.annotation.Fitable; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippFileUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippFileUtils.java index 9a0f9b89f2..22c12f9ca4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippFileUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippFileUtils.java @@ -34,9 +34,7 @@ public class AippFileUtils { * 上传文件会上传到该共享目录。 */ private static final String DOWNLOAD_FILE_ORIGIN = "/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?"; - private static final Pattern PATTERN = Pattern.compile("filePath=([^&]+)"); - private static final Logger log = Logger.get(AippFileUtils.class); /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippLogUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippLogUtils.java index 468ce50ff3..d875ff0b28 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippLogUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippLogUtils.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.entity.AippInstLog; import modelengine.fit.jober.aipp.entity.AippLogData; import modelengine.fit.jober.aipp.enums.AippInstLogType; + import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AopAippLogService; import modelengine.fitframework.util.ObjectUtils; @@ -54,8 +55,8 @@ private static boolean isFormIdValid(String formId) { } private static boolean isFormVersionValid(String formVersion) { - return !(StringUtils.isBlank(formVersion) || StringUtils.equals(AippConst.INVALID_FORM_VERSION_ID, - formVersion)); + return !(StringUtils.isBlank(formVersion) + || StringUtils.equals(AippConst.INVALID_FORM_VERSION_ID, formVersion)); } /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippStringUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippStringUtils.java index 6e5eb7b2f8..755a81b3f0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippStringUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AippStringUtils.java @@ -24,7 +24,6 @@ public class AippStringUtils { * 标题数量 */ public static final int MAX_OUTLINE_LINE = 50; - private static final Logger log = Logger.get(AippStringUtils.class); /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppImExportUtil.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppImExportUtil.java index f2b4a4f524..a0a64cdab8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppImExportUtil.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppImExportUtil.java @@ -9,9 +9,8 @@ import static modelengine.fit.jober.aipp.common.exception.AippErrCode.IMPORT_CONFIG_FIELD_ERROR; import static modelengine.fit.jober.aipp.constant.AippConstant.NAS_SHARE_DIR; -import modelengine.fit.jane.task.util.Entities; - import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.task.util.Entities; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; import modelengine.fit.jober.aipp.domain.AppBuilderApp; @@ -20,6 +19,7 @@ import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; import modelengine.fit.jober.aipp.dto.export.AppExportApp; import modelengine.fit.jober.aipp.dto.export.AppExportConfig; import modelengine.fit.jober.aipp.dto.export.AppExportConfigProperty; @@ -29,6 +29,7 @@ import modelengine.fit.jober.aipp.dto.export.AppExportFormProperty; import modelengine.fit.jober.aipp.enums.AppState; import modelengine.fit.jober.aipp.enums.FormPropertyTypeEnum; + import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.StringUtils; @@ -62,33 +63,19 @@ */ public class AppImExportUtil { private static final char FILE_EXTENSION_DELIM = '.'; - private static final String RENAME_PATTERN = "-副本([1-9]\\d*)?"; - private static final String RENAME_FORMATTER = "{0}-副本{1}"; - private static final Pattern TENANT_PATTERN = Pattern.compile("^[0-9a-fA-F]{32}$"); - private static final Pattern VERSION_PATTERN = Pattern.compile("\\d+\\.\\d+\\.\\d+"); - private static final String ICON_URL_PATTERN = "/v1/api/{0}/file?filePath={1}&fileName={2}"; - - private static final String[] FORM_PROPERTY_GROUP_SET = new String[] { - "null", "ability", "basic", "workflow", "chat" - }; - - private static final String[] FORM_PROPERTY_FROM_SET = new String[] {"graph", "input", "none"}; - + private static final String[] FORM_PROPERTY_GROUP_SET = + new String[]{"null", "ability", "basic", "workflow", "chat"}; + private static final String[] FORM_PROPERTY_FROM_SET = new String[]{"graph", "input", "none"}; private static final int MAX_NAME_LENGTH = 64; - private static final String[] LEGAL_ICON_TYPE = {"jpg", "jpeg", "png", "gif"}; - private static final String[] APP_BUILT_TYPE_SET = {"basic", "workflow"}; - private static final String[] APP_CATEGORY_SET = {"chatbot", "workflow", "agent"}; - private static final String[] APP_TYPE_SET = {"app", "waterflow", "template"}; - private static final String[] APP_ATTR_DEFAULT_SET = {"icon", "greeting", "description", "name"}; /** @@ -120,13 +107,8 @@ public static AppExportApp convertToAppExportApp(AppBuilderApp appBuilderApp) { public static AppExportConfig convertToAppExportConfig(AppBuilderConfig appBuilderConfig) { List configProperties = appBuilderConfig.getConfigProperties(); AppBuilderForm appBuilderForm = appBuilderConfig.getForm(); - Map idToFormProperty = appBuilderConfig.getApp() - .getFormProperties() - .stream() - .collect(Collectors.toMap(AppBuilderFormProperty::getId, Function.identity())); - List exportProperties = configProperties.stream() - .map(configProperty -> convertToAppExportConfigProperty(configProperty, idToFormProperty)) + .map(configProperty -> convertToAppExportConfigProperty(configProperty, appBuilderConfig.getAppVersion())) .filter(appExportConfigProperty -> appExportConfigProperty.getFormProperty() != null) .collect(Collectors.toList()); AppExportForm exportForm = AppExportForm.builder() @@ -144,14 +126,15 @@ public static AppExportConfig convertToAppExportConfig(AppBuilderConfig appBuild * 将应用 configUI 配置属性导出为应用导出的 config 配置属性。 * * @param configProperty 表示应用 configUI 配置的 {@link AppBuilderConfigProperty}。 - * @param idToFormProperty 表达从 formPropertyId 到 appBuilderFormProperty 的映射的 {@link Map}。 + * @param appVersion 应用版本。 * @return 表示应用导出 config 配置的 {@link AppExportConfig}。 */ public static AppExportConfigProperty convertToAppExportConfigProperty(AppBuilderConfigProperty configProperty, - Map idToFormProperty) { + AppVersion appVersion) { return AppExportConfigProperty.builder() .nodeId(configProperty.getNodeId()) - .formProperty(convertToAppExportFormProperty(idToFormProperty.get(configProperty.getFormPropertyId()))) + .formProperty( + convertToAppExportFormProperty(appVersion.getFormProperty(configProperty.getFormPropertyId()))) .build(); } @@ -432,8 +415,18 @@ public static AppBuilderApp convertToAppBuilderApp(AppExportDto appExportDto, Op .build(); } - private static List getFormProperties(List configProperties) { - return configProperties.stream().map(AppBuilderConfigProperty::getFormProperty).collect(Collectors.toList()); + /** + * 将应用的配置 {@link List}{@code <}{@link AppBuilderConfigProperty}{@code >} 转换 + * 为应用的表单配置 {@link List}{@code <}{@link AppBuilderFormProperty}{@code >}。 + * + * @param configProperties 表示应用配置列表。 + * @return {@link List}{@code <}{@link AppBuilderFormProperty}{@code >} 表示表单配置。 + */ + public static List getFormProperties(List configProperties) { + return configProperties + .stream() + .map(AppBuilderConfigProperty::getFormProperty) + .collect(Collectors.toList()); } private static void resetAppAttributes(AppExportApp importedApp) { @@ -471,8 +464,7 @@ public static AppBuilderConfig convertToAppBuilderConfig(AppExportConfig appExpo * @return 表示应用的 ConfigUI 表单配置的 {@link AppBuilderForm}。 */ public static AppBuilderForm convertToAppBuilderForm(AppExportForm appExportForm, OperationContext context) { - return AppBuilderForm.builder() - .id(appExportForm.getId()) + return AppBuilderForm.builder().id(appExportForm.getId()) .name(appExportForm.getName()) .type(appExportForm.getType()) .appearance(appExportForm.getAppearance()) @@ -539,16 +531,18 @@ public static AppBuilderFormProperty convertToAppBuilderFormProperty(AppExportFo * @param iconExtension 表示图像类型后缀的 {@link String}。 * @param tenantId 表示租户 id 的 {@link String}。 * @param contextRoot 表示请求上下文根的 {@link String}。 + * @param resourcePath 表示资源目录的 {@link String}。 * @return 表示构造好的图像的路径,可以存放在 attribute 中的 {@link String}。 */ - public static String saveIconFile(String iconContent, String iconExtension, String tenantId, String contextRoot) { + public static String saveIconFile(String iconContent, String iconExtension, String tenantId, String contextRoot, + String resourcePath) { boolean isValidExtension = Stream.of(LEGAL_ICON_TYPE) .anyMatch(type -> StringUtils.equalsIgnoreCase(type, iconExtension)); if (!isValidExtension) { return StringUtils.EMPTY; } String newFileName = UUIDUtil.uuid() + "." + iconExtension; - File iconFile = Paths.get(NAS_SHARE_DIR, newFileName).toFile(); + File iconFile = Paths.get(resourcePath, newFileName).toFile(); byte[] iconBytes = iconContent.getBytes(StandardCharsets.UTF_8); try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(iconBytes))) { FileUtils.copyInputStreamToFile(inputStream, iconFile); diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppUtils.java index 899eceee1d..4df81d0b47 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/AppUtils.java @@ -26,7 +26,10 @@ public class AppUtils { * @return 表示替换处理后的询接口排除的应用名称的 {@link List}{@code <}{@link String}{@code >}。 */ public static List replaceAsterisks(List excludeNames) { - return excludeNames.stream().map(s -> s.replaceAll("\\*(\\w+)\\*", "{$1}")).collect(Collectors.toList()); + return excludeNames + .stream() + .map(s -> s.replaceAll("\\*(\\w+)\\*", "{$1}")) + .collect(Collectors.toList()); } /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/CacheUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/CacheUtils.java index d6485f6bc1..b6ed2579b1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/CacheUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/CacheUtils.java @@ -16,13 +16,15 @@ import modelengine.fit.jane.meta.multiversion.definition.Meta; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; import modelengine.fit.jober.aipp.po.AppBuilderAppPo; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.CollectionUtils; import java.util.List; -import java.util.Optional; import java.util.concurrent.TimeUnit; /** @@ -49,15 +51,9 @@ public class CacheUtils { Caffeine.newBuilder().expireAfterAccess(48, TimeUnit.HOURS).maximumSize(30).build(); /** - * 用于缓存app_id和aipp_id的关系 + * 用于缓存应用ID和最新发布的应用任务数据关系 */ - private static final Cache APP_ID_TO_AIPP_ID_CACHE = - Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.DAYS).maximumSize(1000).build(); - - /** - * 用于缓存应用ID和最新发布的元数据关系 - */ - private static final Cache AIPP_ID_TO_LAST_META_CACHE = + private static final Cache APP_ID_TO_LAST_APP_TASK_CACHE = Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).maximumSize(1000).build(); /** @@ -66,13 +62,11 @@ public class CacheUtils { public static void clear() { APP_CACHE.invalidateAll(); FLOW_CACHE.invalidateAll(); - APP_ID_TO_AIPP_ID_CACHE.invalidateAll(); - AIPP_ID_TO_LAST_META_CACHE.invalidateAll(); + APP_ID_TO_LAST_APP_TASK_CACHE.invalidateAll(); APP_CACHE.cleanUp(); FLOW_CACHE.cleanUp(); - APP_ID_TO_AIPP_ID_CACHE.cleanUp(); - AIPP_ID_TO_LAST_META_CACHE.cleanUp(); + APP_ID_TO_LAST_APP_TASK_CACHE.cleanUp(); } /** @@ -91,33 +85,21 @@ public static FlowInfo getPublishedFlowWithCache(FlowsService flowsService, Stri /** * 根据应用唯一标识查询对应元数据。 * - * @param metaService 表示用于查询元数据的服务实例的 {@link MetaService}。 + * @param appVersionService 表示用于查询元数据的服务实例的 {@link AppVersionService}。 * @param appId 表示应用唯一标识的 {@link String}。 * @param isDebug 表示是否为调试会话的 {@code boolean}。 * @param context 表示操作上下文的 {@link OperationContext}。 - * @return 表示应用对应的元数据信息的 {@link Meta}。 + * @return 表示应用对应的元数据信息的 {@link AppTask}。 */ - public static Meta getMetaByAppId(MetaService metaService, String appId, boolean isDebug, + public static AppTask getAppTaskByAppId(AppVersionService appVersionService, String appId, boolean isDebug, OperationContext context) { if (isDebug) { - return getLatestMetaByAppId(metaService, appId, context); + AppVersion appVersion = appVersionService.retrieval(appId); + return appVersion.getLatestTask(context); } - // 获取一个aipp_id的缓存,然后根据aipp_id查询最新发布版的meta。 - String aippId = - APP_ID_TO_AIPP_ID_CACHE.get(appId, id -> getLatestMetaByAppId(metaService, id, context).getId()); - return AIPP_ID_TO_LAST_META_CACHE.get(aippId, (ignore) -> { - Meta lastPublishedMeta = MetaUtils.getLastPublishedMeta(metaService, aippId, context); - return Optional.ofNullable(lastPublishedMeta) - .orElseThrow(() -> new AippException(AippErrCode.APP_CHAT_PUBLISHED_META_NOT_FOUND)); + return APP_ID_TO_LAST_APP_TASK_CACHE.get(appId, id -> { + AppVersion appVersion = appVersionService.retrieval(appId); + return appVersion.getLatestPublishedTask(context); }); } - - private static Meta getLatestMetaByAppId(MetaService metaService, String appId, OperationContext context) { - List metas = MetaUtils.getAllMetasByAppId(metaService, appId, context); - if (CollectionUtils.isEmpty(metas)) { - log.error("No metas found for appId: {}" + appId); - throw new AippException(AippErrCode.APP_CHAT_DEBUG_META_NOT_FOUND); - } - return metas.get(0); - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/ConvertUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/ConvertUtils.java index 8204dff15d..eaa205e270 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/ConvertUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/ConvertUtils.java @@ -6,12 +6,13 @@ package modelengine.fit.jober.aipp.util; -import modelengine.fit.appbuilder.security.util.XssUtils; import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.genericable.entity.AippCreate; + +import modelengine.fit.appbuilder.security.util.XssUtils; import modelengine.fitframework.util.StringUtils; import java.time.LocalDateTime; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/DataUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/DataUtils.java index c50144db35..5a36f3793a 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/DataUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/DataUtils.java @@ -14,6 +14,7 @@ import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.common.ErrorCodes; import modelengine.fit.jober.common.exceptions.JobberException; + import modelengine.fitframework.inspection.Validation; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.ObjectUtils; @@ -40,7 +41,6 @@ public class DataUtils { * prompt的正则pattern中表示key的group */ private static final int KEY_GROUP = 1; - private static final Logger log = Logger.get(DataUtils.class); /** @@ -202,7 +202,9 @@ public static void putFromMapIfPresent(Map from, String name, Ma */ public static String getAppId(Map businessData) { Object appId = notNull(businessData.get(AippConst.ATTR_APP_ID_KEY), "App id cannot be null."); - return isInstanceOf(appId, String.class, "App id cast error, it must be string. [type={0}]", + return isInstanceOf(appId, + String.class, + "App id cast error, it must be string. [type={0}]", appId.getClass().getName()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FlowInfoUtil.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FlowInfoUtil.java new file mode 100644 index 0000000000..d7d6951407 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FlowInfoUtil.java @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.util; + +import modelengine.fit.dynamicform.entity.FormMetaQueryParameter; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jade.waterflow.entity.FlowNodeFormInfo; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.dto.AippNodeForms; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * flowInfo工具类. + * + * @author 张越 + * @since 2025/02/08 + */ +public class FlowInfoUtil { + /** + * 构建 {@link AippNodeForms} 列表. + * + * @param flowInfo 流程信息. + * @param formProperties 表单属性列表. + * @return {@link AippNodeForms} 列表. + */ + public static List buildAippNodeForms(FlowInfo flowInfo, + List formProperties) { + if (flowInfo.getFlowNodes() == null) { + return Collections.emptyList(); + } + return flowInfo.getFlowNodes().stream().filter(item -> item.getFlowNodeForm() != null).map(item -> { + FlowNodeFormInfo form = item.getFlowNodeForm(); + List parameter = Collections.singletonList( + new FormMetaQueryParameter(form.getFormId(), form.getVersion())); + return AippNodeForms.builder() + .type(item.getType()) + .metaInfo(FormUtils.buildFormMetaInfos(parameter, formProperties)) + .build(); + }).collect(Collectors.toList()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FormUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FormUtils.java index e9105f0b3d..b1c1dd6dfe 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FormUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/FormUtils.java @@ -16,6 +16,7 @@ import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; import modelengine.fit.jober.aipp.entity.AippLogData; + import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; @@ -36,11 +37,8 @@ */ public class FormUtils { private static final Logger log = Logger.get(FormUtils.class); - private static final String SCHEMA_KEY = "schema"; - private static final String PARAMETERS_KEY = "parameters"; - private static final String PROPERTIES_KEY = "properties"; /** @@ -61,7 +59,8 @@ public static Map buildFormData(Map businessData return form; } - private static Map buildFormData(Map businessData, Map appearance) { + private static Map buildFormData(Map businessData, + Map appearance) { Map formDataMap = new HashMap<>(); if (appearance == null) { return formDataMap; @@ -100,8 +99,8 @@ private static Set getPropertiesKeys(Map appearance) { */ public static AippLogData buildLogDataWithFormData(List formProperties, String formId, String formVersion, Map businessData) { - List parameter = Collections.singletonList( - new FormMetaQueryParameter(formId, formVersion)); + List parameter = + Collections.singletonList(new FormMetaQueryParameter(formId, formVersion)); Map formArgs = buildFormMetaInfos(parameter, formProperties).stream() .flatMap(item -> item.getFormMetaItems().stream().map(FormMetaItem::getKey)) diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/HttpUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/HttpUtils.java index ec3dc61867..9875102c2c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/HttpUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/HttpUtils.java @@ -44,13 +44,15 @@ public static HttpClassicClientResponse execute(HttpClassicClientRequest public static String sendHttpRequest(HttpClassicClientRequest httpRequest) throws IOException { try (HttpClassicClientResponse response = HttpUtils.execute(httpRequest)) { if (response.statusCode() != HttpResponseStatus.OK.statusCode()) { - throw new IOException( - String.format(Locale.ROOT, "send http fail. url=%s result=%d", httpRequest.requestUri(), - response.statusCode())); + throw new IOException(String.format(Locale.ROOT, + "send http fail. url=%s result=%d", + httpRequest.requestUri(), + response.statusCode())); } if (!response.textEntity().isPresent()) { - throw new IOException( - String.format(Locale.ROOT, "get empty response entity, url=%s", httpRequest.requestUri())); + throw new IOException(String.format(Locale.ROOT, + "get empty response entity, url=%s", + httpRequest.requestUri())); } return response.textEntity().get().content(); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/JsonUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/JsonUtils.java index 82d84d2bf2..4820771f21 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/JsonUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/JsonUtils.java @@ -6,6 +6,11 @@ package modelengine.fit.jober.aipp.util; +import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; +import modelengine.fit.jober.aipp.common.exception.AippJsonEncodeException; +import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeDeserializer; +import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeSerializer; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.core.type.TypeReference; @@ -13,10 +18,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; -import modelengine.fit.jober.aipp.common.exception.AippJsonEncodeException; -import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeDeserializer; -import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeSerializer; import modelengine.fitframework.util.StringUtils; import java.time.LocalDateTime; @@ -106,8 +107,8 @@ static String toJsonString(Object object) { static boolean isValidJson(String jsonLikeString) { boolean isValid; try { - TreeNode node = new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS) - .readTree(jsonLikeString); + TreeNode node = + new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS).readTree(jsonLikeString); isValid = Objects.nonNull(node); } catch (JsonProcessingException e) { isValid = false; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/LLMUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/LLMUtils.java index 885778f83a..5da1d42dd0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/LLMUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/LLMUtils.java @@ -6,14 +6,15 @@ package modelengine.fit.jober.aipp.util; +import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; +import modelengine.fit.jober.aipp.enums.LlmModelNameEnum; +import modelengine.fit.jober.aipp.service.LLMService; + import modelengine.fel.core.chat.ChatMessage; import modelengine.fel.core.chat.ChatModel; import modelengine.fel.core.chat.ChatOption; import modelengine.fel.core.chat.support.ChatMessages; import modelengine.fel.core.chat.support.HumanMessage; -import modelengine.fit.jober.aipp.common.exception.AippJsonDecodeException; -import modelengine.fit.jober.aipp.enums.LlmModelNameEnum; -import modelengine.fit.jober.aipp.service.LLMService; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.StringUtils; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/MetaUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/MetaUtils.java index c127063efa..10653af656 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/MetaUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/MetaUtils.java @@ -6,40 +6,11 @@ package modelengine.fit.jober.aipp.util; -import static modelengine.fit.jober.aipp.common.exception.AippErrCode.TASK_NOT_FOUND; -import static modelengine.fitframework.util.ObjectUtils.nullIf; - -import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.common.enums.DirectionEnum; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; -import modelengine.fit.jober.aipp.common.exception.AippException; -import modelengine.fit.jober.aipp.common.exception.AippNotFoundException; -import modelengine.fit.jober.aipp.common.exception.AippParamException; -import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; -import modelengine.fit.jober.aipp.enums.AippSortKeyEnum; -import modelengine.fit.jober.aipp.enums.AippTypeEnum; -import modelengine.fit.jober.aipp.enums.JaneCategory; import modelengine.fit.jober.common.RangedResultSet; -import modelengine.fitframework.inspection.Validation; -import modelengine.fitframework.log.Logger; -import modelengine.fitframework.util.CollectionUtils; -import modelengine.fitframework.util.StringUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.LongStream; import java.util.stream.Stream; @@ -50,269 +21,6 @@ * @since 2024/02/21 */ public class MetaUtils { - private static final Logger log = Logger.get(MetaUtils.class); - - /** - * 查询最近更新的任意{@link Meta} - * - * @param metaService 使用的{@link MetaService} - * @param metaId 指定aipp的id - * @param version 指定aipp的版本 - * @param context 操作人上下文 - * @return 最近更新的{@link Meta} - * @throws AippNotFoundException 没有找到 - */ - public static Meta getAnyMeta(MetaService metaService, String metaId, String version, OperationContext context) - throws AippNotFoundException { - MetaFilter metaFilter = getAnyMetaFilter(metaId, version); - return getSingleMetaHandle(metaService, metaId, metaFilter, context); - } - - /** - * 查询指定aippId最新的非预览{@link Meta}(包括发布和未发布的) - * - * @param metaService 使用的{@link MetaService} - * @param metaId 指定aipp的id - * @param context 操作人上下文 - * @return 最近更新的非预览{@link Meta} - * @throws AippException 没有找到 - */ - public static Meta getLastNormalMeta(MetaService metaService, String metaId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.DEFAULT); - return getSingleMetaHandle(metaService, metaId, metaFilter, context); - } - - /** - * 查询指定aippId最新的已发布{@link Meta} - * - * @param metaService 使用的{@link MetaService} - * @param metaId 指定aipp的id - * @param context 操作人上下文 - * @return 最近更新的已发布{@link Meta} - * @throws AippException 没有找到 - */ - public static Meta getLastPublishedMeta(MetaService metaService, String metaId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.PUBLISHED); - return getSingleMetaHandle(metaService, metaId, metaFilter, context); - } - - /** - * 查询指定aippId最新的未发布草稿{@link Meta} - * - * @param metaService 使用的{@link MetaService} - * @param metaId 指定aipp的id - * @param context 操作人上下文 - * @return 最近更新的未发布草稿{@link Meta} - * @throws AippException 没有找到 - */ - public static Meta getLastDraftMeta(MetaService metaService, String metaId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.DRAFT); - return getSingleMetaHandle(metaService, metaId, metaFilter, context); - } - - /** - * 查询指定aippId所有发布的 {@link Meta}, 按更新时间倒序 - * - * @param metaService 使用的{@link MetaService} - * @param metaId 指定aipp的id - * @param context 操作人上下文 - * @return 所有非预览{@link Meta} - * @throws AippException 没有找到 - */ - public static List getAllPublishedMeta(MetaService metaService, String metaId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.PUBLISHED); - return getListMetaHandle(metaService, metaFilter, context); - } - - /** - * 查询指定aippId所有预览{@link Meta}, 按更新时间倒序 - * - * @param metaService 使用的{@link MetaService} - * @param baselineAippId 指定aipp的id - * @param context 操作人上下文 - * @return 所有预览{@link Meta} - * @throws AippException 没有找到 - */ - public static List getAllPreviewMeta(MetaService metaService, String baselineAippId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getPreviewMetaFilter(baselineAippId); - return getListMetaHandle(metaService, metaFilter, context); - } - - private static Meta getSingleMetaHandle(MetaService metaService, String metaId, MetaFilter metaFilter, - OperationContext context) throws AippException { - Validation.notBlank(metaId, () -> new AippParamException(context, AippErrCode.INPUT_PARAM_IS_INVALID, metaId)); - RangedResultSet metaRes = metaService.list(metaFilter, true, 0, 1, context); - if (metaRes.getResults().isEmpty()) { - log.warn("meta {} version {} meta status {} not found", - metaId, - metaFilter.getVersions(), - metaFilter.getAttributes().get(AippConst.ATTR_META_STATUS_KEY)); - return null; - } - return metaRes.getResults().get(0); - } - - /** - * 获取MetaList - * - * @param metaService 处理Meta请求的service - * @param metaFilter 过滤器 - * @param context 上下文 - * @return 结果 - * @throws AippException 抛出aipp的异常 - */ - public static List getListMetaHandle(MetaService metaService, MetaFilter metaFilter, OperationContext context) - throws AippException { - final int limitPerQuery = 10; - return getAllFromRangedResult(limitPerQuery, - (offset) -> metaService.list(metaFilter, - false, - offset, - limitPerQuery, - context)).collect(Collectors.toList()); - } - - private static Map> deepCopy(Map> originalMap) { - Map> copiedMap = new HashMap<>(); - for (Map.Entry> entry : originalMap.entrySet()) { - String key = entry.getKey(); - List originalList = entry.getValue(); - List copiedList = new ArrayList<>(originalList); - copiedMap.put(key, copiedList); - } - return copiedMap; - } - - private static MetaFilter getAnyMetaFilter(String metaId, String version) { - MetaFilter metaFilter = new MetaFilter(); - metaFilter.setCategories(Collections.singletonList(JaneCategory.AIPP.name())); - metaFilter.setMetaIds(Collections.singletonList(metaId)); - if (version != null) { - metaFilter.setVersions(Collections.singletonList(version)); - } - metaFilter.setOrderBys(Collections.singletonList(formatSorter(null, null))); - return metaFilter; - } - - private static MetaFilter getNormalMetaFilter(String metaId, NormalFilterEnum normalFilter) { - Map> attributes = new HashMap>() {{ - // 仅查找普通aipp,不包含预览aipp - put(AippConst.ATTR_AIPP_TYPE_KEY, Collections.singletonList(AippTypeEnum.NORMAL.name())); - }}; - if (normalFilter == NormalFilterEnum.PUBLISHED) { - attributes.put(AippConst.ATTR_META_STATUS_KEY, - Collections.singletonList(AippMetaStatusEnum.ACTIVE.getCode())); - } else { - if (normalFilter == NormalFilterEnum.DRAFT) { - attributes.put(AippConst.ATTR_META_STATUS_KEY, - Collections.singletonList(AippMetaStatusEnum.INACTIVE.getCode())); - } - } - String sortEncode = MetaUtils.formatSorter(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()); - MetaFilter metaFilter = getAnyMetaFilter(metaId, null); - metaFilter.setOrderBys(Collections.singletonList(sortEncode)); - metaFilter.setAttributes(attributes); - return metaFilter; - } - - /** - * 判断一个Meta是否被发布 - * - * @param meta 待验证的Meta - * @return 该meta是否发布 - */ - public static boolean isPublished(Meta meta) { - Map attributes = meta.getAttributes(); - if (!attributes.containsKey(AippConst.ATTR_AIPP_TYPE_KEY) - || !attributes.containsKey(AippConst.ATTR_META_STATUS_KEY)) { - return false; - } - String aippType = String.valueOf(attributes.get(AippConst.ATTR_AIPP_TYPE_KEY)); - String metaStatus = String.valueOf(attributes.get(AippConst.ATTR_META_STATUS_KEY)); - return StringUtils.equals(aippType, AippTypeEnum.NORMAL.name()) && Objects.equals(metaStatus, - AippMetaStatusEnum.ACTIVE.getCode()); - } - - /** - * 通过appID获取所有的Meta - * - * @param metaService 处理Meta请求的service - * @param appId app的Id - * @param aippType aipp的类型 - * @param context 上下文 - * @return 结果 - * @throws AippException 抛出aipp的异常 - */ - public static List getAllMetasByAppId(MetaService metaService, String appId, String aippType, - OperationContext context) throws AippException { - MetaFilter metaFilter = getMetaFilter(appId, aippType); - return getListMetaHandle(metaService, metaFilter, context); - } - - /** - * 通过appId获取所有的元数据 - * - * @param metaService 元数据服务 - * @param appId appId - * @param context 上下文 - * @return 元数据列表 - * @throws AippException aipp通用异常 - */ - public static List getAllMetasByAppId(MetaService metaService, String appId, OperationContext context) - throws AippException { - MetaFilter metaFilter = getMetaFilter(appId); - return getListMetaHandle(metaService, metaFilter, context); - } - - private static MetaFilter getMetaFilter(String appId) { - MetaFilter metaFilter = new MetaFilter(); - Map> attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, Collections.singletonList(appId)); - metaFilter.setAttributes(attributes); - String sortEncode = MetaUtils.formatSorter(AippSortKeyEnum.CREATE_AT.name(), DirectionEnum.DESCEND.name()); - metaFilter.setOrderBys(Collections.singletonList(sortEncode)); - return metaFilter; - } - - private static MetaFilter getMetaFilter(String appId, String aippType) { - MetaFilter metaFilter = new MetaFilter(); - Map> attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, Collections.singletonList(appId)); - attributes.put(AippConst.ATTR_AIPP_TYPE_KEY, Collections.singletonList(AippTypeEnum.getType(aippType).type())); - metaFilter.setAttributes(attributes); - return metaFilter; - } - - private static MetaFilter getPreviewMetaFilter(String metaId) { - MetaFilter metaFilter = getAnyMetaFilter(metaId, null); - metaFilter.setAttributes(new HashMap>() { - { - // 仅查找预览aipp - put(AippConst.ATTR_AIPP_TYPE_KEY, Collections.singletonList(AippTypeEnum.PREVIEW.name())); - } - }); - return metaFilter; - } - - /** - * 查询指定aippId所有预览{@link Meta}, 按更新时间倒序 - * - * @param sortKey 见{@link AippSortKeyEnum}, 默认update_at - * @param direction 排序方向, 见{@link DirectionEnum}, 默认desc - * @return {@link MetaFilter}中setOrderBys中的字符串 - */ - public static String formatSorter(String sortKey, String direction) { - return String.format(Locale.ROOT, - "%s(%s)", - DirectionEnum.getDirection(nullIf(direction, DirectionEnum.DESCEND.name())).getValue(), - AippSortKeyEnum.getSortKey(nullIf(sortKey, AippSortKeyEnum.UPDATE_AT.name())).getKey()); - } - /** * 从rangeResult实例中获取查询结果 * @@ -333,51 +41,8 @@ public static Stream getAllFromRangedResult(int limitPerQuery, } return Stream.concat(firstResult.stream(), LongStream.rangeClosed(1, (int) (metaRes.getRange().getTotal() / limitPerQuery)) - .mapToObj(offsetCount -> CompletableFuture.supplyAsync(() -> resultGetter.apply( - offsetCount * limitPerQuery).getResults().stream())) + .mapToObj(offsetCount -> CompletableFuture.supplyAsync( + () -> resultGetter.apply(offsetCount * limitPerQuery).getResults().stream())) .flatMap(CompletableFuture::join)); } - - /** - * 状态枚举 - */ - public enum NormalFilterEnum { - // 已发布 - PUBLISHED, - // 草稿 - DRAFT, - // 默认 - DEFAULT - } - - /** - * 根据versionId批量删除meta - * - * @param metaService 使用的{@link MetaService} - * @param versionIds 需要删除的versionId列表 - * @param context 操作人上下文 - */ - public static void deleteMetasByVersionIds(MetaService metaService, List versionIds, - OperationContext context) { - versionIds.forEach(versionId -> metaService.delete(versionId, context)); - } - - /** - * 根据 appId 获取 aippId - * - * @param metaService meta服务 - * @param appId 表示 app 的唯一标识 - * @param context 上下文 - * @return aipp标识 - * @throws AippTaskNotFoundException meta找不到时抛出 - */ - public static String getAippIdByAppId(MetaService metaService, String appId, OperationContext context) - throws AippTaskNotFoundException { - List metas = MetaUtils.getAllMetasByAppId(metaService, appId, context); - if (CollectionUtils.isEmpty(metas)) { - log.error("Meta can not be null."); - throw new AippTaskNotFoundException(TASK_NOT_FOUND); - } - return metas.get(0).getId(); - } } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/Retryable.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/Retryable.java new file mode 100644 index 0000000000..b951dba80f --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/Retryable.java @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.util; + +import lombok.Setter; +import modelengine.fitframework.util.ObjectUtils; + +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * 重试类. + * + * @author 张越 + * @since 2025-02-14 + */ +@Setter +public class Retryable { + private Supplier supplier; + private int retryTimes; + private Class observeException; + private Predicate breakCondition; + private BiConsumer exceptionConsumer; + + public Retryable(Supplier supplier, int retryTimes) { + this.supplier = supplier; + this.retryTimes = retryTimes; + } + + /** + * 重试方法,返回重试结果. + * {@code T} 表示实际的返回结果,{@code E} 代表用户关注的异常对象. + * + * @return {@link RetryResult} 重试结果 + */ + public RetryResult retry() { + int left = this.retryTimes; + E lastException; + do { + try { + return new RetryResult<>(this.supplier.get(), null); + } catch (Exception e) { + if (this.observeException == null || !this.observeException.isInstance(e)) { + throw e; + } + lastException = ObjectUtils.cast(e); + if (this.breakCondition != null && this.breakCondition.test(ObjectUtils.cast(e))) { + break; + } + if (this.exceptionConsumer != null) { + this.exceptionConsumer.accept(ObjectUtils.cast(e), this.retryTimes - left); + } + } + } while (left-- > 0); + return new RetryResult<>(null, lastException); + } + + /** + * 重试结果类. + * + * @param 表示实际的返回结果 + * @param 代表用户关注的异常对象 + */ + public static class RetryResult { + private final T t; + + private final E exception; + + public RetryResult(T t, E e) { + this.t = t; + this.exception = e; + } + + /** + * 当 T 为 null 时抛出异常. + * + * @param exceptionSupplier 异常提供器. + * @return 实际结果. + * @throws X 异常对象. + */ + public T orElseThrow(Function exceptionSupplier) throws X { + if (this.t != null) { + return this.t; + } + throw exceptionSupplier.apply(this.exception); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/SensitiveFilterTools.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/SensitiveFilterTools.java index 5a785ad0e9..b5e8345af6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/SensitiveFilterTools.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/SensitiveFilterTools.java @@ -6,11 +6,12 @@ package modelengine.fit.jober.aipp.util; +import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fitframework.annotation.Component; -import modelengine.fitframework.annotation.Value; import java.util.List; import java.util.regex.Pattern; @@ -57,9 +58,7 @@ public String filterString(String sensitive) { @AllArgsConstructor public static class SensitiveReplaceEntity { private String pattern; - private String to; - private Pattern compiledPattern; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/TemplateUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/TemplateUtils.java index b2295b466d..42ed15dc7b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/TemplateUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/TemplateUtils.java @@ -8,10 +8,15 @@ import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; import modelengine.fit.jober.aipp.enums.AppState; import modelengine.fit.jober.aipp.enums.AppTypeEnum; + import modelengine.fitframework.util.ObjectUtils; +import modelengine.fitframework.util.StringUtils; + +import java.util.Map; /** * 应用模板相关的工具类。 @@ -30,8 +35,10 @@ public class TemplateUtils { */ public static final String ICON_ATTR_KEY = "icon"; + private static final String APP_ATTR_ICON = "icon"; + private static final String APP_ATTR_GREETING = "greeting"; + private static final String APP_ATTR_STORE_ID = "store_id"; private static final String INIT_VERSION = "1.0.0"; - private static final int ZERO_COUNT = 0; /** @@ -97,4 +104,36 @@ public static AppBuilderApp convertToAppBuilderApp(AppTemplate template) { .appCategory(template.getCategory()) .build(); } + + /** + * 将 {@link AppTemplate} 转换 {@link AppBuilderAppCreateDto}。 + * + * @param template {@link AppTemplate} 对象. + * @return {@link AppBuilderAppCreateDto} 对象. + */ + public static AppBuilderAppCreateDto toAppCreateDTO(AppTemplate template) { + AppBuilderApp appTemplate = convertToAppBuilderApp(template); + Map attributes = appTemplate.getAttributes(); + String description = getAttribute(attributes, DESCRIPTION_ATTR_KEY); + String icon = getAttribute(attributes, APP_ATTR_ICON); + String greeting = getAttribute(attributes, APP_ATTR_GREETING); + String storeId = getAttribute(attributes, APP_ATTR_STORE_ID); + return AppBuilderAppCreateDto.builder() + .name(appTemplate.getName()) + .description(description) + .icon(icon) + .greeting(greeting) + .appType(appTemplate.getAppType()) + .type(appTemplate.getType()) + .storeId(storeId) + .appBuiltType(appTemplate.getAppBuiltType()) + .appCategory(appTemplate.getAppCategory()) + .build(); + } + + private static String getAttribute(Map attributes, String name) { + // 增加保护,之前创建的应用部分前端传入了null, 如果再新建版本则导致新版本出现字符串"null" + Object value = attributes.get(name); + return value == null ? StringUtils.EMPTY : String.valueOf(value); + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/UsefulUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/UsefulUtils.java new file mode 100644 index 0000000000..ce8cbe8ace --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/UsefulUtils.java @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.util; + +import modelengine.fitframework.util.StringUtils; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * 有用的工具. + * + * @author 张越 + * @since 2025.01.06 + */ +public class UsefulUtils { + /** + * 当不为null的时候处理. + * + * @param v 待判断数据. + * @param consumer 消费者. + * @param 数据类型. + */ + public static void doIfNotNull(T v, Consumer consumer) { + if (v != null) { + consumer.accept(v); + } + } + + /** + * 当字符串不为空的时候处理. + * + * @param v 待判断数据. + * @param consumer 消费者. + */ + public static void doIfNotBlank(String v, Consumer consumer) { + if (StringUtils.isNotBlank(v)) { + consumer.accept(v); + } + } + + /** + * 当为null的时候处理. + * + * @param v 待判断数据. + * @param runnable 执行器. + * @param 数据类型. + */ + public static void doIfNull(T v, Runnable runnable) { + if (v == null) { + runnable.run(); + } + } + + /** + * 当字符串为空的时候处理. + * + * @param v 待判断数据. + * @param runner 执行函数. + */ + public static void doIfBlank(String v, Runnable runner) { + if (StringUtils.isBlank(v)) { + runner.run(); + } + } + + /** + * 懒加载,获取一个对象 + * + * @param target 待获取的目标 + * @param supplier 如果目标为null, 则执行supplier获取 + * @param consumer 获取后回调该方法, 用于设置属性 + * @param 延迟加载的类型 + * @return 延迟加载的结果 + */ + public static X lazyGet(X target, Supplier supplier, Consumer consumer) { + if (target != null) { + return target; + } + X newTarget = supplier.get(); + consumer.accept(newTarget); + return newTarget; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/VersionUtils.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/VersionUtils.java index 9d36debafc..7ffbb0ede8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/VersionUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/util/VersionUtils.java @@ -6,6 +6,11 @@ package modelengine.fit.jober.aipp.util; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; + +import modelengine.fitframework.util.StringUtils; + /** * 版本号的工具类 * @@ -65,4 +70,34 @@ public static String buildPreviewVersion(String version) { String subUuid = (uuid.length() > PREVIEW_UUID_LEN) ? uuid.substring(0, PREVIEW_UUID_LEN) : uuid; return version + CONNECTION_SIGN + subUuid; } + + /** + * 比较两个版本. + * + * @param v1 版本1. + * @param v2 版本2. + * @return 若v1比v2版本大,则返回1;若v1比v2小,则返回-1;否则返回0. + */ + public static int compare(String v1, String v2) { + if (!VersionUtils.isValidVersion(v1) || !VersionUtils.isValidVersion(v2)) { + throw new AippException(AippErrCode.INVALID_VERSION_NAME); + } + if (StringUtils.equals(v1, v2)) { + // 在这里,与旧版本号相同的版本号被认为是最新的 + return 0; + } + String[] oldPart = v1.split("\\."); + String[] newPart = v2.split("\\."); + for (int i = 0; i < oldPart.length; i++) { + int oldV = Integer.parseInt(oldPart[i]); + int newV = Integer.parseInt(newPart[i]); + if (oldV > newV) { + return 1; + } + if (newV > oldV) { + return -1; + } + } + return 0; + } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/validation/impl/FormFileValidatorImpl.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/validation/impl/FormFileValidatorImpl.java index 4aaebbd269..afd8760ca8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/validation/impl/FormFileValidatorImpl.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/validation/impl/FormFileValidatorImpl.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.validation.FormFileValidator; + import modelengine.fitframework.annotation.Component; import modelengine.fitframework.log.Logger; import modelengine.fitframework.util.ObjectUtils; @@ -31,37 +32,23 @@ @Component public class FormFileValidatorImpl implements FormFileValidator { private static final long IMAGE_MAX_SIZE = 1024 * 1024; - private static final String PARAMETERS = "parameters"; - private static final String SCHEMA = "schema"; - private static final String TYPE = "type"; - private static final String OBJECT = "object"; - private static final String REQUIRED = "required"; - private static final String PROPERTIES = "properties"; - - private static final List SCHEMA_LIST = Arrays.asList("string", "number", "integer", "array", "boolean", - "null", "object"); - + private static final List SCHEMA_LIST = + Arrays.asList("string", "number", "integer", "array", "boolean", "null", "object"); private static final String ARRAY = "array"; - private static final String ITEMS = "items"; - private static final String ENUM = "enum"; - private static final String RETURN = "return"; - private static final String ORDER = "order"; - private static final List BUILD_FILE_LIST = Arrays.asList("index.html", "index.js"); - - private static final List DISALLOWED_FILE_TYPE = Arrays.asList(".php", ".exe", ".bat", ".sh", ".dll", - ".bin", ".zip", ".tar", ".gz", ".rar", ".7z", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"); - + private static final List DISALLOWED_FILE_TYPE = + Arrays.asList(".php", ".exe", ".bat", ".sh", ".dll", ".bin", ".zip", ".tar", ".gz", ".rar", + ".7z", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"); private static final Logger log = Logger.get(FormFileValidatorImpl.class); @Override @@ -160,7 +147,8 @@ private void validateArrayTypeProperty(String fieldName, Map fie Map items = cast(itemsObject); if (items != null) { if (!items.containsKey(TYPE)) { - throw new AippException(AippErrCode.FORM_SCHEMA_PROPERTY_MISSING_KEY, fieldName + " (array items)", + throw new AippException(AippErrCode.FORM_SCHEMA_PROPERTY_MISSING_KEY, + fieldName + " (array items)", TYPE); } Object type = items.get(TYPE); @@ -173,7 +161,8 @@ private void validateArrayTypeProperty(String fieldName, Map fie private void validateArrayItem(String fieldName, Map item) { if (!item.containsKey(TYPE) && !item.containsKey(ENUM)) { - throw new AippException(AippErrCode.FORM_SCHEMA_PROPERTY_MISSING_KEY, fieldName + " (array items)", + throw new AippException(AippErrCode.FORM_SCHEMA_PROPERTY_MISSING_KEY, + fieldName + " (array items)", TYPE + "/" + ENUM); } if (item.containsKey(TYPE)) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippInstanceVO.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippInstanceVO.java index ec48a82154..80a4414201 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippInstanceVO.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippInstanceVO.java @@ -26,22 +26,13 @@ @AllArgsConstructor public class AippInstanceVO { private List ancestors; - private String aippInstanceId; - private String tenantId; - private String aippInstanceName; - private String status; - private String formMetadata; - private Map formArgs; - private String startTime; - private String endTime; - private List aippInstanceLogs; } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippLogVO.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippLogVO.java index 145100e268..11fe6a209d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippLogVO.java +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/vo/AippLogVO.java @@ -6,13 +6,14 @@ package modelengine.fit.jober.aipp.vo; +import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.util.AippLogUtils; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; -import modelengine.fit.jober.aipp.enums.AippInstLogType; -import modelengine.fit.jober.aipp.util.AippLogUtils; import modelengine.fitframework.util.StringUtils; import java.util.Collections; @@ -82,10 +83,10 @@ public static AippLogVO fromCreateDto(AippLogCreateDto dto) { * @return true/false.true,代表需要在前端展示;false,表示不需要在前端展示. */ public boolean displayable() { - return !(StringUtils.equals(AippInstLogType.FORM.name(), this.logType) || StringUtils.equals( - AippInstLogType.HIDDEN_MSG.name(), this.logType) || StringUtils.equals( - AippInstLogType.HIDDEN_QUESTION.name(), this.logType) || StringUtils.equals( - AippInstLogType.HIDDEN_FORM.name(), this.logType)); + return !(StringUtils.equals(AippInstLogType.FORM.name(), this.logType) + || StringUtils.equals(AippInstLogType.HIDDEN_MSG.name(), this.logType) || StringUtils.equals( + AippInstLogType.HIDDEN_QUESTION.name(), + this.logType) || StringUtils.equals(AippInstLogType.HIDDEN_FORM.name(), this.logType)); } /** diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml index 415007c36a..345ab5e995 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/application-prod.yml @@ -38,7 +38,8 @@ app-engine: exclude-names: - 'i18n_appBuilder_*demo*' resource: - path-prefix: /static/ + url-prefix: /static/ + path: /var/share form: create: maximumNum: 400 @@ -51,6 +52,14 @@ app-engine: file: upload: maxStorageRatio: 0.9 + ttl: + businessData: 15 + nonBusinessData: 1 + max-number: 200 + question: + max-length: 20000 + user-context: + max-length: 500 elsa: endpoint: elsaKey: @@ -73,9 +82,19 @@ tool: TEXTTOIMAGENODESTATE: textToImageNodeState FILEEXTRACTNODESTATE: fileExtractionNodeState LOOPNODESTATE: loopNodeState + PARALLELNODESTATE: parallelNodeState export-meta: version: 1.0.1 sensitive: replace: - pattern: "\"timestamp\":\"[^\"]+\"" - to: "\"timestamp\":\"***\"" \ No newline at end of file + to: "\"timestamp\":\"***\"" +aipp: + instance: + log: + file: + path: /var/share/backup/aipp-instance-log/ +chat: + session: + file: + path: /var/share/backup/chat-session/ \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_en.json b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_en.json index e553ae4839..e6d7934496 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_en.json +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_en.json @@ -69,6 +69,11 @@ "name": "Loop", "uniqueName": "" }, + { + "type": "parallelNodeState", + "name": "Parallel", + "uniqueName": "" + }, { "type": "manualCheckNodeState", "name": "Manual Form", diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_zh.json b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_zh.json index a3f33fe488..5de8366e3c 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_zh.json +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/basic_node_zh.json @@ -69,6 +69,11 @@ "name": "循环", "uniqueName": "" }, + { + "type": "parallelNodeState", + "name": "并行", + "uniqueName": "" + }, { "type": "manualCheckNodeState", "name": "人工表单", diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/finance_prompt.txt b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/finance_prompt.txt new file mode 100644 index 0000000000..3a835f63ac --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/component/finance_prompt.txt @@ -0,0 +1,27 @@ +你是一个聪明的助手,你的任务是根据用户问题和补充信息,改写用户问题以包含时间信息。 + + +1、用户问题: +xx产品市场收入? +补充信息: +查询xx产品市场收入。其中,具体时间为2024年02月,xx产品是指重量级团队LV1名称包含“yy”,采取人民币单位,采取经营双算口径。 +改写问题: +2024年02月,yy市场收入是多少? + +2、用户问题: +xx的收入? +补充信息: +查询xx的收入。其中,具体时间为2024年02月,xx是指名称为“xx”,采取人民币单位,采取经营双算口径。 +改写问题: +2024年02月,xx的收入是多少? + + +只输出改写问题内容。 +只附加时间信息。 +纠正用户问题中的错别字 + +<用户问题> +{{query}} + +<补充信息> +{{complete}} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/i18n/messages_en.properties b/app-builder/jane/plugins/aipp-plugin/src/main/resources/i18n/messages_en.properties index d737014239..51568f6565 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/i18n/messages_en.properties +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/i18n/messages_en.properties @@ -101,43 +101,43 @@ 90002998=The license has expired. 90002980=Invalid path format. 90002999=Aipp type illegal parameter. -90002101=to be translated. -90002102=to be translated. -90002103=to be translated. -90002104=to be translated. -90002105=to be translated. -90002106=to be translated. -90002107=to be translated. -90002108=to be translated. -90002109=to be translated. -90002110=to be translated. -90002111=to be translated. -90002112=to be translated. -90002113=to be translated. -90002114=to be translated. -90002115=to be translated. -90002116=to be translated. -90002117=to be translated. -90002118=to be translated. -90002119=to be translated. -90002120=to be translated. -90002121=to be translated. -90002122=to be translated. -90002123=to be translated. -90002124=to be translated. -90002125=to be translated. -90002126=to be translated. -90002127=to be translated. -90002128=to be translated. -90002129=to be translated. -90002130=to be translated. -90002131=to be translated. -90002132=to be translated. -90002133=to be translated. -90002134=to be translated. -90002135=to be translated. -90002136=to be translated. -90002137=to be translated. -90002138=to be translated. -90002139=to be translated. +90002101=The uploaded file cannot be empty. +90002102=The uploaded form file format needs to be zip. +90002103=System error, failed to save the form file, please try again or contact the administrator. +90002104=The file exceeds the maximum limit of 6M, please upload again. +90002105=The form file content cannot be empty, please upload again. +90002106=The form file is incomplete and is missing {0}. Please upload it again. +90002107=Only one form thumbnail file can be uploaded. Please upload it again. +90002108=The form thumbnail exceeds the maximum limit of 1M, please upload again. +90002109=The form schema is missing a required field: {0}. Please upload again. +90002110=The parameters of the form schema need to include the field: type, value: object. Please upload again. +90002111=The parameters of the form schema are missing a required field: {0}. Please upload again. +90002112=The form schema field {0} is missing {1} definition. Please upload again. +90002113=The type value of the field {0} in the form schema must be string. Please upload again. +90002114=The value of type for the field {0} in the form schema can only be [string, number, integer, array, boolean, null, object]. Please upload again. +90002115=The type value of the list type field {0} in the form schema must be string. Please upload again. +90002116=The value type of the enum type item of the list type field {0} in the form schema must be list. Please upload again. +90002117=The number of parameters in the form schema {0} cannot be greater than the number of input parameters. Please upload again. +90002118=The field {0} of the form schema cannot contain parameter names other than those under properties. Please upload again. +90002119=The form build folder content is empty, please upload again. +90002120=The form build folder is missing the {0} file. Please upload it again. +90002121=The json format of the form schema is incorrect, please check and upload again. +90002122=The current storage space utilization has reached {0}%. Please clear some files. +90002123=System error. Failed to verify the system physical storage limit. Please contact the administrator. +90002124=The component file contains a disallowed file type, file name: {0}. +90002125=The component file contains files other than the build folder, config.json, and form preview image. Please upload it again. +90002126=Failed to create the form directory, please upload again. +90002127=Failed to write the form file, please upload it again. +90002128=The operation failed. The creation form information is empty. Please try again. +90002129=Operation failed. The form name length should be less than 64 characters. +90002130=Operation failed. The form description length should be less than 300 characters. +90002131=The operation failed, {0} information is missing. +90002132=Update failed. The update form information is empty. Please try again. +90002133=Update failed. The form does not exist. Please try again or contact the administrator. +90002134=Creation failed. The number of tables cannot exceed {0}. +90002135=Operation failed, form name already exists, please use another name. +90002136=Deletion failed. The form does not exist or has been deleted. +90002137=System error. Failed to obtain form configuration information. Please contact the administrator. +90002138=Failed to run. The form does not exist or has been deleted. +90002139=Failed to call the large model service, {0}. 90002140=Failed to generate {0} by model, please change default model. reason: {1}. \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml index b9b61ead42..ea4c1a67c7 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml @@ -15,6 +15,28 @@ + + + + + + + + + + + + + + + + + + + + + + chat_id, app_id, app_version, name, attributes, create_at, create_by, update_at, update_by, status @@ -58,12 +80,16 @@ and chat_id = #{chatId} and status != 1 + and attributes ->> 'state' = #{requestParam.appState} + and create_by = #{createBy} order by update_at desc - offset #{requestParam.offset} limit #{requestParam.limit} + + offset #{requestParam.offset} limit #{requestParam.limit} + + + + + + DELETE FROM + t_chat_session + where chat_id in + + #{item} + + + + + DELETE FROM + t_chat_session_task_instance_wide_relationship + where chat_id in + + #{item} + + + + + + \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml index e4ede1eca0..c278fa060f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippLogMapper.xml @@ -135,8 +135,7 @@ - + insert into aipp_instance_log (aipp_id, version, aipp_type, instance_id, log_data, log_type, create_by, path) values (#{aippId}, #{version}, #{aippType}, #{instanceId}, #{logData}::jsonb, #{logType}, #{createUserAccount}, #{path}) @@ -263,4 +262,37 @@ #{item} + + + + + DELETE FROM + aipp_instance_log + where log_id in + + #{item} + + + + \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderAppMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderAppMapper.xml index 3a0a97855f..f5e02df5f8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderAppMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderAppMapper.xml @@ -4,6 +4,7 @@ + @@ -18,11 +19,15 @@ + + + + + - id - , name, config_id, flow_graph_id, tenant_id, type, version, attributes, path, state, app_built_type, app_category, create_by, create_at, update_by, update_at, app_type + id, name, app_id, app_suite_id, config_id, flow_graph_id, tenant_id, type, version, attributes, path, state, app_built_type, app_category, create_by, create_at, update_by, update_at, is_active, status, unique_name, publish_at, app_type @@ -60,6 +65,15 @@ limit 1 + + @@ -187,8 +210,8 @@ insert into app_builder_app ( ) - values (#{id}, #{name}, #{configId}, #{flowGraphId}, #{tenantId}, #{type}, #{version}, #{attributes}::jsonb, - #{path}, #{state}, #{appBuiltType}, #{appCategory}, #{createBy}, #{createAt}, #{updateBy}, #{updateAt}, #{appType}) + values (#{id}, #{name}, #{appId}, #{appSuiteId}, #{configId}, #{flowGraphId}, #{tenantId}, #{type}, #{version}, #{attributes}::jsonb, + #{path}, #{state}, #{appBuiltType}, #{appCategory}, #{createBy}, #{createAt}, #{updateBy}, #{updateAt}, #{isActive}, #{status}, #{uniqueName}, #{publishAt}, #{appType}) @@ -197,6 +220,12 @@ name = #{name}, + + app_id = #{appId}, + + + app_suite_id = #{appSuiteId}, + config_id = #{configId}, @@ -237,7 +266,19 @@ path = #{path}, - app_type = #{appType} + app_type = #{appType}, + + + is_active = #{isActive}, + + + status = #{status}, + + + unique_name = #{uniqueName}, + + + publish_at = #{publishAt} where id = #{id} diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderFormMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderFormMapper.xml index 858ebeccdb..3c5d8f8fc5 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderFormMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderFormMapper.xml @@ -90,8 +90,7 @@ where name = #{name} and tenant_id = #{tenantId} and is_deleted = 0 - select from app_builder_form where is_deleted = 0 diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml index b85dbd4178..5019761064 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppBuilderRuntimeInfoMapper.xml @@ -88,4 +88,24 @@ where trace_id = #{traceId} order by start_time asc + + + + + DELETE + FROM app_builder_runtime_info + where id in + + #{item} + + \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppChatNumMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppChatNumMapper.xml index 9c38c6d715..290beb6577 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppChatNumMapper.xml +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AppChatNumMapper.xml @@ -9,4 +9,8 @@ update app_chat_num set chat_num=chat_num - 1 where app_id=#{appId} and chat_mode=#{chatMode} + + + update app_chat_num set chat_num=0 + \ No newline at end of file diff --git a/app-builder/jane/jober/sql/jade/06_appbuilder_insert.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql similarity index 80% rename from app-builder/jane/jober/sql/jade/06_appbuilder_insert.sql rename to app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql index 9d26edd717..93aa078d4f 100644 --- a/app-builder/jane/jober/sql/jade/06_appbuilder_insert.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql @@ -1,27 +1,6 @@ --- 任务模板 -INSERT INTO "public"."task_template" ("id", "name", "description", "tenant_id") VALUES ('4f91b69973d1453aadf384d508aed894', '普通任务', '最基础的默认模板', '00000000000000000000000000000000') ON CONFLICT (id) DO NOTHING; -INSERT INTO "public"."task_template" ("id", "name", "description", "tenant_id") VALUES ('00000000000000000000000000000000', 'empty', 'empty', '00000000000000000000000000000000') ON CONFLICT (id) DO NOTHING; - - -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('08f8fc32484d408aacbcff35bf8be687', '4f91b69973d1453aadf384d508aed894', 'id', 'TEXT', 1) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('abb064f84f36496bb769321b1f3443c9', '4f91b69973d1453aadf384d508aed894', 'decomposed_from', 'TEXT', 2) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('be686a0325694aadb688539f7523f88a', '4f91b69973d1453aadf384d508aed894', 'title', 'TEXT', 3) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('59e0d16c3fe541899778beb76e56b0e3', '4f91b69973d1453aadf384d508aed894', 'owner', 'TEXT', 4) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('e2af84d4a8714ce8a87be79d19a5eb88', '4f91b69973d1453aadf384d508aed894', 'created_by', 'TEXT', 5) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('4aebbf316cf443d791e0280f56f435d6', '4f91b69973d1453aadf384d508aed894', 'created_date', 'DATETIME', 1) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('387c16ae70ac49f68f670f54e4456fc8', '4f91b69973d1453aadf384d508aed894', 'modified_by', 'TEXT', 6) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('354944b0e9234429aabfbafb08059c2e', '4f91b69973d1453aadf384d508aed894', 'modified_date', 'DATETIME', 2) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('c41b4da4013e484ead6fc8b03192dee2', '4f91b69973d1453aadf384d508aed894', 'status', 'TEXT', 7) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('34a33a519dd64dc5aa4b3c98c70a5989', '4f91b69973d1453aadf384d508aed894', 'target_url', 'TEXT', 8) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('456bc8b7b9754708828c68221af977c9', '4f91b69973d1453aadf384d508aed894', 'progress_feedback', 'TEXT', 9) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('d8e5fad3feed41fe8400bd2f35048abe', '4f91b69973d1453aadf384d508aed894', 'risk', 'TEXT', 10) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('e8cf80fdb51a4d4e9c9a9c660714efc7', '4f91b69973d1453aadf384d508aed894', 'priority', 'TEXT', 11) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('819adccd6214437fadec3936b297007b', '4f91b69973d1453aadf384d508aed894', 'finish_time', 'DATETIME', 3) ON CONFLICT (id) DO NOTHING; -INSERT INTO task_template_property ("id", "task_template_id", "name", "data_type", "sequence") VALUES ('aac65de27f014c3895228fdb5d32573e', '4f91b69973d1453aadf384d508aed894', 'tag', 'TEXT', 12) ON CONFLICT (id) DO NOTHING; - -INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path") VALUES ('df87073b9bc85a48a9b01eccc9afccc4', 'i18n_appBuilder_{llm_application_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5d', '9997f1555b41452b924d1a8b2229bde3', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is llm description", "icon": "http://ab", "greeting": "hello", "app_type": "i18n_appBuilder_{interview_assistant}"}', 'inactive', 'basic', 'chatbot', NULL) ON CONFLICT (id) DO NOTHING; -INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path") VALUES ('df87073b9bc85a48a9b01eccc9afccc5', 'i18n_appBuilder_{llm_application_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5f', '9997f1555b41452b924d1a8b2229bde5', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is llm description", "icon": "http://ab", "greeting": "hello", "app_type": "i18n_appBuilder_{interview_assistant}"}', 'inactive', 'workflow', 'chatbot', NULL) ON CONFLICT (id) DO NOTHING; -INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path") VALUES ('df87073b9bc85a48a9b01eccc9afccc3', 'i18n_appBuilder_{waterflow_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5e', '9997f1555b41452b924d1a8b2229bde4', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is tool description"}', 'inactive', 'workflow', 'chatbot', NULL) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path", "app_id") VALUES ('df87073b9bc85a48a9b01eccc9afccc4', 'i18n_appBuilder_{llm_application_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5d', '9997f1555b41452b924d1a8b2229bde3', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is llm description", "icon": "http://ab", "greeting": "hello", "app_type": "i18n_appBuilder_{interview_assistant}"}', 'inactive', 'basic', 'chatbot', NULL, 'df87073b9bc85a48a9b01eccc9afccc4') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path", "app_id") VALUES ('df87073b9bc85a48a9b01eccc9afccc5', 'i18n_appBuilder_{llm_application_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5f', '9997f1555b41452b924d1a8b2229bde5', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is llm description", "icon": "http://ab", "greeting": "hello", "app_type": "i18n_appBuilder_{interview_assistant}"}', 'inactive', 'workflow', 'chatbot', NULL, 'df87073b9bc85a48a9b01eccc9afccc5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "path", "app_id") VALUES ('df87073b9bc85a48a9b01eccc9afccc3', 'i18n_appBuilder_{waterflow_template}', 'wzc', '2024-04-17 19:42:35', 'wzc', '2024-04-17 19:42:38', '45515ca5da9d04428f41086e291b4d5e', '9997f1555b41452b924d1a8b2229bde4', '31f20efc7e0848deab6a6bc10fc3021e', 'template', '1.0.0', '{"description:":"this is tool description"}', 'inactive', 'workflow', 'chatbot', NULL, 'df87073b9bc85a48a9b01eccc9afccc3') ON CONFLICT (id) DO NOTHING; INSERT INTO "public"."app_builder_config" ("id", "form_id", "app_id", "tenant_id", "create_by", "create_at", "update_by", "update_at") VALUES ('45515ca5da9d04428f41086e291b4d5d', 'b8986770a6ffef44bbf2a9f26d6fc1bc', '847907b5da7ea644abe159a913dba403', '31f20efc7e0848deab6a6bc10fc3021e', 'wzc', '2024-04-17 19:57:34', 'wzc', '2024-04-17 19:57:41') ON CONFLICT (id) DO NOTHING; INSERT INTO "public"."app_builder_config" ("id", "form_id", "app_id", "tenant_id", "create_by", "create_at", "update_by", "update_at") VALUES ('45515ca5da9d04428f41086e291b4d5f', 'b8986770a6ffef44bbf2a9f26d6fc1be', '847907b5da7ea644abe159a913dba405', '31f20efc7e0848deab6a6bc10fc3021e', 'wzc', '2024-04-17 19:57:34', 'wzc', '2024-04-17 19:57:41') ON CONFLICT (id) DO NOTHING; @@ -95,7 +74,7 @@ INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance") VALUES ('9997f1555b41452b924d1a8b2229bde4', 'tool模板', 'wzc', '2024-04-17 20:41:29', 'wzc', '2024-04-17 20:41:42', - '{"id":"9fd1e6736e734fc085a5ea34f4ba1b21","title":"9fd1e6736e734fc085a5ea34f4ba1b21","source":"elsa","type":"jadeFlowGraph","tenant":"default","setting":{"borderColor":"#047bfc","shadow":"","underline":false,"selectable":true,"fontFace":"arial","pDock":"none","headColor":"steelblue","itemScroll":{"x":0,"y":0},"vAlign":"top","cornerRadius":4,"rotateAble":true,"pad":10,"infoType":{"next":"INFORMATION","name":"none"},"focusMargin":0,"progressStatus":{"next":"UNKNOWN","color":"gray","name":"NONE"},"focusBackColor":"whitesmoke","tag":{},"captionfontColor":"whitesmoke","fontWeight":"lighter","captionlineHeight":1,"margin":25,"itemPad":[5,5,5,5],"autoHeight":false,"visible":true,"rotateDegree":0,"focusBorderColor":"#047bfc","priority":0,"fontStyle":"normal","backColor":"whitesmoke","captionfontFace":"arial black","dockMode":"none","dashWidth":0,"fontSize":12,"mouseInFontColor":"orange","resizeable":true,"captionfontWeight":"lighter","mouseInBorderColor":"#047bfc","autoText":false,"showedProgress":false,"shared":false,"code":"","hAlign":"center","deletable":true,"numberedList":false,"shadowData":"2px 2px 4px","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","allowLink":true,"lineWidth":2,"captionhAlign":"center","dragable":true,"focusShadow":"","emphasized":false,"borderWidth":1,"globalAlpha":1,"bulletedList":false,"enableAnimation":false,"autoWidth":false,"captionfontSize":14,"progressPercent":0.65,"strikethrough":false,"backAlpha":0.15,"focusFontColor":"darkorange","outstanding":false,"editable":true,"moveable":true,"captionfontStyle":"normal","mouseInColor":"orange","bulletSpeed":1,"enableSocial":true,"scrollLock":{"x":false,"y":false},"lineHeight":1.5,"fontColor":"steelblue","mouseInBackColor":"whitesmoke"},"pages":[{"x":297.46031746031747,"y":121.01190476190482,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":false,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc","shapes":[{"x":776.6424894555158,"y":415.53564816787656,"id":"jade1p0cdu","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-98,"textX":0,"textY":0,"width":314.07170053851075,"hAlign":"center","height":-20.607221568854584,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadewdnjbq","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E","runnable":true},{"x":-88.39275686449133,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":103,"width":360,"height":700,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","description":"这是用户输入的问题","disableModifiable":true}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":true},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"x":416.64248945551583,"y":-79.46435183212344,"id":"jadewdnjbq","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":990,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"Qwen1.5-32B-Chat"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"INTERNAL"}]}, {"id":"6c414e75-971e-403a-b2b1-c6850f128cc4","from":"Input","name":"model","type":"String","value":"Qwen1.5-32B-Chat"},{"id":"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"88f74d78-4711-4f81-a2e7-74d0034c5e88","from":"Expand","name":"prompt","type":"Object","value":[{"id":"35a710cf-1b79-4523-b16f-b50878d677fe","from":"Input","name":"template","type":"String","value":"请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n上下文信息:\n\n问题:{{query}}"},{"id":"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d","from":"Expand","name":"variables","type":"Object","value":[{"id":"eee66922-4304-4209-89fc-b13ffa101630","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"a6865419-867c-4bfb-855c-f5c1876c965a","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"308e2023-a8e9-486e-9784-8680addbb786","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"68f92923-d5da-42ce-8478-d7ac7d90664e","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"58852742-5484-4870-9da0-65a7e41565ca","name":"maxMemoryRounds","type":"Integer","from":"input","value":"0"},{"id":"112405a9-cccb-4e3a-968e-0ef7ae81667a","from":"input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{"id":"95d84d67-3198-415e-a63c-bc9a2da8d821","from":"Expand","name":"output","type":"Object","value":[{"id":"272c927a-9e25-48b6-a921-6a8ab20267a4","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"x":1090.7141899940266,"y":248.92842659902198,"id":"jadesoux5i","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":true,"index":106,"width":360,"height":292,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"fae1b650-075d-497d-a2b3-755ab315dca9","from":"Reference","name":"finalOutput","type":"String", "referenceNode":"jadewdnjbq","referenceId":"272c927a-9e25-48b6-a921-6a8ab20267a4","referenceKey":"llmOutput","value":["output","llmOutput"],"editable":true,"isRequired":false},{"id": "c4469c16-88a7-4575-b339-9a06e3305f3b","from": "Input","name": "enableLog","type": "Boolean","value": false}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"type":"jadeEvent","container":"elsa-page:tvp1s6","id":"2bqgxs","text":"","namespace":"flowable","x":271.60724313550867,"y":382.5,"width":145.03524632000716,"height":33.03564816787656,"bold":false,"italic":false,"pad":0,"margin":20,"backColor":"white","hideText":true,"beginArrow":false,"beginArrowEmpty":false,"beginArrowSize":4,"endArrow":true,"endArrowEmpty":false,"endArrowSize":4,"textX":0,"textY":0,"hAlign":"center","lineWidth":2,"fromShape":"jade6qm5eg","toShape":"jadewdnjbq","definedFromConnector":"E","definedToConnector":"W","arrowBeginPoint":{"x":0,"y":0},"arrowEndPoint":{"x":0,"y":0},"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"lineMode":{"type":"auto_curve"},"allowSwitchLineMode":false,"allowLink":false,"borderWidth":1,"borderColor":"#B1B1B7","mouseInBorderColor":"#B1B1B7","runnable":true,"index":-96,"dirty":true}]}],"enableText":false,"flowMeta":{"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"enableOutputScope":true,"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}}}') ON CONFLICT (id) DO NOTHING; + '{"id":"9fd1e6736e734fc085a5ea34f4ba1b21","title":"9fd1e6736e734fc085a5ea34f4ba1b21","source":"elsa","type":"jadeFlowGraph","tenant":"default","setting":{"borderColor":"#047bfc","shadow":"","underline":false,"selectable":true,"fontFace":"arial","pDock":"none","headColor":"steelblue","itemScroll":{"x":0,"y":0},"vAlign":"top","cornerRadius":4,"rotateAble":true,"pad":10,"infoType":{"next":"INFORMATION","name":"none"},"focusMargin":0,"progressStatus":{"next":"UNKNOWN","color":"gray","name":"NONE"},"focusBackColor":"whitesmoke","tag":{},"captionfontColor":"whitesmoke","fontWeight":"lighter","captionlineHeight":1,"margin":25,"itemPad":[5,5,5,5],"autoHeight":false,"visible":true,"rotateDegree":0,"focusBorderColor":"#047bfc","priority":0,"fontStyle":"normal","backColor":"whitesmoke","captionfontFace":"arial black","dockMode":"none","dashWidth":0,"fontSize":12,"mouseInFontColor":"orange","resizeable":true,"captionfontWeight":"lighter","mouseInBorderColor":"#047bfc","autoText":false,"showedProgress":false,"shared":false,"code":"","hAlign":"center","deletable":true,"numberedList":false,"shadowData":"2px 2px 4px","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","allowLink":true,"lineWidth":2,"captionhAlign":"center","dragable":true,"focusShadow":"","emphasized":false,"borderWidth":1,"globalAlpha":1,"bulletedList":false,"enableAnimation":false,"autoWidth":false,"captionfontSize":14,"progressPercent":0.65,"strikethrough":false,"backAlpha":0.15,"focusFontColor":"darkorange","outstanding":false,"editable":true,"moveable":true,"captionfontStyle":"normal","mouseInColor":"orange","bulletSpeed":1,"enableSocial":true,"scrollLock":{"x":false,"y":false},"lineHeight":1.5,"fontColor":"steelblue","mouseInBackColor":"whitesmoke"},"pages":[{"x":297.46031746031747,"y":121.01190476190482,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":false,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc","shapes":[{"x":776.6424894555158,"y":415.53564816787656,"id":"jade1p0cdu","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-98,"textX":0,"textY":0,"width":314.07170053851075,"hAlign":"center","height":-20.607221568854584,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadewdnjbq","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E","runnable":true},{"x":-88.39275686449133,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":103,"width":360,"height":700,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","description":"这是用户输入的问题","disableModifiable":true}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":true},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"x":416.64248945551583,"y":-79.46435183212344,"id":"jadewdnjbq","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":990,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"#app_builder_default_model_name#"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"#app_builder_default_tag#"}]}, {"id":"6c414e75-971e-403a-b2b1-c6850f128cc4","from":"Input","name":"model","type":"String","value":"#app_builder_default_model_name#"},{"id":"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"88f74d78-4711-4f81-a2e7-74d0034c5e88","from":"Expand","name":"prompt","type":"Object","value":[{"id":"35a710cf-1b79-4523-b16f-b50878d677fe","from":"Input","name":"template","type":"String","value":"请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n上下文信息:\n\n问题:{{query}}"},{"id":"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d","from":"Expand","name":"variables","type":"Object","value":[{"id":"eee66922-4304-4209-89fc-b13ffa101630","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"a6865419-867c-4bfb-855c-f5c1876c965a","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"308e2023-a8e9-486e-9784-8680addbb786","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"68f92923-d5da-42ce-8478-d7ac7d90664e","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"58852742-5484-4870-9da0-65a7e41565ca","name":"maxMemoryRounds","type":"Integer","from":"input","value":"0"},{"id":"112405a9-cccb-4e3a-968e-0ef7ae81667a","from":"input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{"id":"95d84d67-3198-415e-a63c-bc9a2da8d821","from":"Expand","name":"output","type":"Object","value":[{"id":"272c927a-9e25-48b6-a921-6a8ab20267a4","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"x":1090.7141899940266,"y":248.92842659902198,"id":"jadesoux5i","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":true,"index":106,"width":360,"height":292,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"fae1b650-075d-497d-a2b3-755ab315dca9","from":"Reference","name":"finalOutput","type":"String", "referenceNode":"jadewdnjbq","referenceId":"272c927a-9e25-48b6-a921-6a8ab20267a4","referenceKey":"llmOutput","value":["output","llmOutput"],"editable":true,"isRequired":false},{"id": "c4469c16-88a7-4575-b339-9a06e3305f3b","from": "Input","name": "enableLog","type": "Boolean","value": false}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)","runnable":true},{"type":"jadeEvent","container":"elsa-page:tvp1s6","id":"2bqgxs","text":"","namespace":"flowable","x":271.60724313550867,"y":382.5,"width":145.03524632000716,"height":33.03564816787656,"bold":false,"italic":false,"pad":0,"margin":20,"backColor":"white","hideText":true,"beginArrow":false,"beginArrowEmpty":false,"beginArrowSize":4,"endArrow":true,"endArrowEmpty":false,"endArrowSize":4,"textX":0,"textY":0,"hAlign":"center","lineWidth":2,"fromShape":"jade6qm5eg","toShape":"jadewdnjbq","definedFromConnector":"E","definedToConnector":"W","arrowBeginPoint":{"x":0,"y":0},"arrowEndPoint":{"x":0,"y":0},"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"lineMode":{"type":"auto_curve"},"allowSwitchLineMode":false,"allowLink":false,"borderWidth":1,"borderColor":"#B1B1B7","mouseInBorderColor":"#B1B1B7","runnable":true,"index":-96,"dirty":true}]}],"enableText":false,"flowMeta":{"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"enableOutputScope":true,"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}}}') ON CONFLICT (id) DO NOTHING; INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance") VALUES ('9997f1555b41452b924d1a8b2229bde5', 'LLM模板', 'wzc', '2024-04-17 20:41:29', 'wzc', '2024-04-17 20:41:42', @@ -108,17 +87,12 @@ INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "creat "appearance") VALUES ('beefffd1168b4785a71f149a2a530d2f', 'tool模板', 'modelengine.jade', '2024-05-24 18:29:55.572', 'modelengine.jade', '2024-05-24 18:34:46.521', - '{"id":"beefffd1168b4785a71f149a2a530d2f","type":"jadeFlowGraph","pages":[{"x":176.21031746031747,"y":-46.488095238095184,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":true,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"shapes":[{"x":-145.8928571428571,"y":230,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":101,"width":360,"height":528,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"query","type":"String","value":"","description":"用户问题。"}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":2334.464285714285,"y":378.9285714285713,"id":"jadesoux5i","pad":6,"bold":false,"text":"财经问答结束","type":"endNodeEnd","dirty":true,"index":102,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output"],"referenceId":"output_fc952038-1a14-442b-bd78-8515e69449fa","referenceKey":"output","referenceNode":"jadecoarmv"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":291.28968253968253,"y":366.4880952380952,"id":"jade3axbnw","pad":6,"bold":false,"text":"问题路由","type":"toolInvokeNodeState","dirty":true,"index":102,"width":360,"height":259,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"query"}],"return":{"type":"object"},"uniqueName":"c6c2d272-90e5-4c06-b8fa-5d94930b7532"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"query_c958c2fd-b97e-481f-8cff-55e7d8564d5d","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}],"outputParams":[{"id":"output_2d3a958f-ac5c-413f-bf27-e39425597f72","name":"output","type":"Object","value":[{"id":"b290d85f-2c2e-426d-9789-1ffbdd492a2b","name":"result","type":"String","value":"String"},{"id":"4b5ec0f1-33e5-4bb4-bf83-df410be25b14","name":"matched","type":"Boolean","value":"Boolean"},{"id":"92a77575-7d95-4e51-ab3b-7ce14385b2d4","name":"completeQuery","type":"String","value":"String"}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1868.7896825396824,"y":343.9880952380952,"id":"jadecoarmv","pad":6,"bold":false,"text":"结果生成","type":"toolInvokeNodeState","dirty":true,"index":103,"width":360,"height":304,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"condition"},{"name":"query"}],"return":{"type":"string"},"uniqueName":"24474301-937a-4335-a155-1e86d1a48de0"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"condition_b63e38ea-b27e-4895-b2d8-de84384fae2e","from":"Reference","name":"condition","type":"String","value":["output","llmOutput"],"referenceId":"8e900cd6-d151-402b-b6b2-194790edf467","referenceKey":"llmOutput","referenceNode":"jade5wc1ee"},{"id":"query_88827f8c-a610-46b7-8b50-4531bb87763f","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}],"outputParams":[{"id":"output_fc952038-1a14-442b-bd78-8515e69449fa","name":"output","type":"String","value":[]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":726.2896825396825,"y":317.7380952380952,"id":"jadelh4543","pad":6,"bold":false,"text":"条件","type":"conditionNodeCondition","dirty":true,"index":104,"width":600,"height":357,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"triggerMode":"auto","conditionParams":{"branches":[{"id":"ad2e05b6-593a-4d86-81e0-1b1b504c2795","type":"if","conditions":[{"id":"5d96df3a-5192-4de8-9743-63cf9f0259ec","value":[{"id":"3fc094bc-8496-4e7e-a940-97b092d7e67d","from":"Reference","name":"left","type":"Boolean","value":["output","matched"],"referenceId":"4b5ec0f1-33e5-4bb4-bf83-df410be25b14","referenceKey":"matched","referenceNode":"jade3axbnw"},{"id":"f770e6fe-a612-4fb8-8c40-d51bcef05baf","from":"Reference","name":"right","type":"","value":"","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"is true"}],"conditionRelation":"and"}]},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1405.0396825396824,"y":56.488095238095184,"id":"jade5wc1ee","pad":6,"bold":false,"text":"财经问题专属模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":851,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"Qwen1.5-32B-Chat"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"INTERNAL"}]},{"id":"c490ace3-472c-4c95-bf29-760982f62c64","from":"Input","name":"model","type":"String","value":"Qwen-72B"},{"id":"c1ea0b9c-97c9-466e-bd42-b0eb6046dc3a","from":"Input","name":"temperature","type":"Number","value":"0"},{"id":"89cda9b5-f975-42ba-a426-a83a6cbdddb3","from":"Expand","name":"prompt","type":"Object","value":[{"id":"12fe982a-0fbd-4461-a159-51e09de31dc0","from":"Input","name":"template","type":"String","value":"{{query}}"},{"id":"6e78da7c-3d62-4df8-98cf-310b06691a4e","from":"Expand","name":"variables","type":"Object","value":[{"id":"1ec181db-eed1-4089-a580-f656de258322","from":"Reference","name":"query","type":"String","value":["output","result"],"referenceId":"b290d85f-2c2e-426d-9789-1ffbdd492a2b","referenceKey":"result","referenceNode":"jade3axbnw"}]}]},{"id":"a5f228f5-167e-4fbc-8114-69bcd9c76a4c","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"f548ab63-9add-4053-a3a6-cb7682c362af","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"4ff72b6e-18b8-430c-a509-d1bf8a53bbe5","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"8cc96b96-765d-4f13-ba10-d9dff76a2615","from":"Expand","name":"output","type":"Object","value":[{"id":"8e900cd6-d151-402b-b6b2-194790edf467","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1410.0396825396824,"y":1000.2380952380952,"id":"jaded1ufou","pad":6,"bold":false,"text":"普通问答模型","type":"llmNodeState","dirty":true,"index":106,"width":360,"height":851,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"Qwen1.5-32B-Chat"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"INTERNAL"}]},{"id":"f9586db3-72f9-4d33-9f01-893bbfa61277","from":"Input","name":"model","type":"String","value":"Qwen-72B"},{"id":"e0b992c2-739a-43b1-9cbc-b6747eb7592f","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"95ba86be-5c03-4258-b5ce-32b0eae17b5c","from":"Expand","name":"prompt","type":"Object","value":[{"id":"123d6c59-c525-4100-a66b-72b1a2c4e625","from":"Input","name":"template","type":"String","value":"{{query}}"},{"id":"892b3e12-2895-4e79-b668-5d6a6bd79fe6","from":"Expand","name":"variables","type":"Object","value":[{"id":"c779f80b-c15f-416d-ad1b-6628cb24fe8c","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}]}]},{"id":"a4e83ae4-4f4b-4407-a2bc-a9570886329e","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"08297d4d-4f38-44c0-b049-638a2008a884","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"f4fd8b0a-aeaf-47e2-8914-5501ba5bb4b1","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"9024f034-bacc-4ac0-812f-e270fa6997b9","from":"Expand","name":"output","type":"Object","value":[{"id":"6f0fdf98-24ee-4ef0-bd62-9d4f1b1cb6ac","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1901.2896825396824,"y":1336.4880952380952,"id":"jadela7k5y","pad":6,"bold":false,"text":"普通问答结束","type":"endNodeEnd","dirty":true,"index":107,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"e09784cb-3ae9-4581-aa63-1b83f09fd7ed","from":"Reference","name":"finalOutput","type":"String","value":["output","llmOutput"],"referenceId":"6f0fdf98-24ee-4ef0-bd62-9d4f1b1cb6ac","referenceKey":"llmOutput","referenceNode":"jaded1ufou"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":214.1071428571429,"y":494,"id":"4iyvh1","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-92,"textX":0,"textY":0,"width":77.18253968253964,"hAlign":"center","height":1.988095238095184,"italic":false,"margin":20,"toShape":"jade3axbnw","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":651.2896825396825,"y":495.9880952380952,"id":"6rh9x7","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-91,"textX":0,"textY":0,"width":75,"hAlign":"center","height":0.25,"italic":false,"margin":20,"toShape":"jadelh4543","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade3axbnw","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1313.2897283160496,"y":512.7380952380952,"id":"vosd6p","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-90,"textX":0,"textY":0,"width":91.74995422363281,"hAlign":"center","height":-30.75,"italic":false,"margin":20,"toShape":"jade5wc1ee","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelh4543","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0"},{"x":1765.0396825396824,"y":481.9880952380952,"id":"4qny17","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-89,"textX":0,"textY":0,"width":103.75,"hAlign":"center","height":14,"italic":false,"margin":20,"toShape":"jadecoarmv","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade5wc1ee","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2228.7896825396824,"y":495.9880952380952,"id":"0vxnrc","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-88,"textX":0,"textY":0,"width":105.67460317460245,"hAlign":"center","height":-10.05952380952391,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadecoarmv","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1313.2897283160496,"y":632.1599778674897,"id":"v3a2mn","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-87,"textX":0,"textY":0,"width":96.74995422363281,"hAlign":"center","height":793.5781173706055,"italic":false,"margin":20,"toShape":"jaded1ufou","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelh4543","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-2"},{"x":1770.0396825396824,"y":1425.7380952380952,"id":"fv8h4v","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-86,"textX":0,"textY":0,"width":131.25,"hAlign":"center","height":17.75,"italic":false,"margin":20,"toShape":"jadela7k5y","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jaded1ufou","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"}],"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc"}],"title":"beefffd1168b4785a71f149a2a530d2f","source":"elsa","tenant":"default","setting":{"pad":10,"tag":{},"code":"","pDock":"none","hAlign":"center","margin":25,"shadow":"","shared":false,"vAlign":"top","itemPad":[5,5,5,5],"visible":true,"autoText":false,"dockMode":"none","dragable":true,"editable":true,"fontFace":"arial","fontSize":12,"infoType":{"name":"none","next":"INFORMATION"},"moveable":true,"priority":0,"allowLink":true,"autoWidth":false,"backAlpha":0.15,"backColor":"whitesmoke","dashWidth":0,"deletable":true,"fontColor":"steelblue","fontStyle":"normal","headColor":"steelblue","lineWidth":2,"underline":false,"autoHeight":false,"emphasized":false,"fontWeight":"lighter","itemScroll":{"x":0,"y":0},"lineHeight":1.5,"resizeable":true,"rotateAble":true,"scrollLock":{"x":false,"y":false},"selectable":true,"shadowData":"2px 2px 4px","borderColor":"#047bfc","borderWidth":1,"bulletSpeed":1,"focusMargin":0,"focusShadow":"","globalAlpha":1,"outstanding":false,"bulletedList":false,"cornerRadius":4,"enableSocial":true,"mouseInColor":"orange","numberedList":false,"rotateDegree":0,"captionhAlign":"center","strikethrough":false,"focusBackColor":"whitesmoke","focusFontColor":"darkorange","progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"showedProgress":false,"captionfontFace":"arial black","captionfontSize":14,"enableAnimation":false,"progressPercent":0.65,"captionfontColor":"whitesmoke","captionfontStyle":"normal","focusBorderColor":"#047bfc","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBackColor":"whitesmoke","mouseInFontColor":"orange","captionfontWeight":"lighter","captionlineHeight":1,"mouseInBorderColor":"#047bfc"},"flowMeta":{"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}},"enableText":false}') ON CONFLICT (id) DO NOTHING; + '{"id":"beefffd1168b4785a71f149a2a530d2f","type":"jadeFlowGraph","pages":[{"x":176.21031746031747,"y":-46.488095238095184,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":true,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"shapes":[{"x":-145.8928571428571,"y":230,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":101,"width":360,"height":528,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"query","type":"String","value":"","description":"用户问题。"}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":2334.464285714285,"y":378.9285714285713,"id":"jadesoux5i","pad":6,"bold":false,"text":"财经问答结束","type":"endNodeEnd","dirty":true,"index":102,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output"],"referenceId":"output_fc952038-1a14-442b-bd78-8515e69449fa","referenceKey":"output","referenceNode":"jadecoarmv"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":291.28968253968253,"y":366.4880952380952,"id":"jade3axbnw","pad":6,"bold":false,"text":"问题路由","type":"toolInvokeNodeState","dirty":true,"index":102,"width":360,"height":259,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"query"}],"return":{"type":"object"},"uniqueName":"c6c2d272-90e5-4c06-b8fa-5d94930b7532"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"query_c958c2fd-b97e-481f-8cff-55e7d8564d5d","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}],"outputParams":[{"id":"output_2d3a958f-ac5c-413f-bf27-e39425597f72","name":"output","type":"Object","value":[{"id":"b290d85f-2c2e-426d-9789-1ffbdd492a2b","name":"result","type":"String","value":"String"},{"id":"4b5ec0f1-33e5-4bb4-bf83-df410be25b14","name":"matched","type":"Boolean","value":"Boolean"},{"id":"92a77575-7d95-4e51-ab3b-7ce14385b2d4","name":"completeQuery","type":"String","value":"String"}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1868.7896825396824,"y":343.9880952380952,"id":"jadecoarmv","pad":6,"bold":false,"text":"结果生成","type":"toolInvokeNodeState","dirty":true,"index":103,"width":360,"height":304,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"condition"},{"name":"query"}],"return":{"type":"string"},"uniqueName":"24474301-937a-4335-a155-1e86d1a48de0"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"condition_b63e38ea-b27e-4895-b2d8-de84384fae2e","from":"Reference","name":"condition","type":"String","value":["output","llmOutput"],"referenceId":"8e900cd6-d151-402b-b6b2-194790edf467","referenceKey":"llmOutput","referenceNode":"jade5wc1ee"},{"id":"query_88827f8c-a610-46b7-8b50-4531bb87763f","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}],"outputParams":[{"id":"output_fc952038-1a14-442b-bd78-8515e69449fa","name":"output","type":"String","value":[]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":726.2896825396825,"y":317.7380952380952,"id":"jadelh4543","pad":6,"bold":false,"text":"条件","type":"conditionNodeCondition","dirty":true,"index":104,"width":600,"height":357,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"triggerMode":"auto","conditionParams":{"branches":[{"id":"ad2e05b6-593a-4d86-81e0-1b1b504c2795","type":"if","conditions":[{"id":"5d96df3a-5192-4de8-9743-63cf9f0259ec","value":[{"id":"3fc094bc-8496-4e7e-a940-97b092d7e67d","from":"Reference","name":"left","type":"Boolean","value":["output","matched"],"referenceId":"4b5ec0f1-33e5-4bb4-bf83-df410be25b14","referenceKey":"matched","referenceNode":"jade3axbnw"},{"id":"f770e6fe-a612-4fb8-8c40-d51bcef05baf","from":"Reference","name":"right","type":"","value":"","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"is true"}],"conditionRelation":"and"}]},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1405.0396825396824,"y":56.488095238095184,"id":"jade5wc1ee","pad":6,"bold":false,"text":"财经问题专属模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":851,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"#app_builder_default_model_name#"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"#app_builder_default_tag#"}]},{"id":"c490ace3-472c-4c95-bf29-760982f62c64","from":"Input","name":"model","type":"String","value":"#app_builder_default_model_name#"},{"id":"c1ea0b9c-97c9-466e-bd42-b0eb6046dc3a","from":"Input","name":"temperature","type":"Number","value":"0"},{"id":"89cda9b5-f975-42ba-a426-a83a6cbdddb3","from":"Expand","name":"prompt","type":"Object","value":[{"id":"12fe982a-0fbd-4461-a159-51e09de31dc0","from":"Input","name":"template","type":"String","value":"{{query}}"},{"id":"6e78da7c-3d62-4df8-98cf-310b06691a4e","from":"Expand","name":"variables","type":"Object","value":[{"id":"1ec181db-eed1-4089-a580-f656de258322","from":"Reference","name":"query","type":"String","value":["output","result"],"referenceId":"b290d85f-2c2e-426d-9789-1ffbdd492a2b","referenceKey":"result","referenceNode":"jade3axbnw"}]}]},{"id":"a5f228f5-167e-4fbc-8114-69bcd9c76a4c","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"f548ab63-9add-4053-a3a6-cb7682c362af","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"4ff72b6e-18b8-430c-a509-d1bf8a53bbe5","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"8cc96b96-765d-4f13-ba10-d9dff76a2615","from":"Expand","name":"output","type":"Object","value":[{"id":"8e900cd6-d151-402b-b6b2-194790edf467","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1410.0396825396824,"y":1000.2380952380952,"id":"jaded1ufou","pad":6,"bold":false,"text":"普通问答模型","type":"llmNodeState","dirty":true,"index":106,"width":360,"height":851,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"#app_builder_default_model_name#"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"#app_builder_default_tag#"}]},{"id":"f9586db3-72f9-4d33-9f01-893bbfa61277","from":"Input","name":"model","type":"String","value":"#app_builder_default_model_name#"},{"id":"e0b992c2-739a-43b1-9cbc-b6747eb7592f","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"95ba86be-5c03-4258-b5ce-32b0eae17b5c","from":"Expand","name":"prompt","type":"Object","value":[{"id":"123d6c59-c525-4100-a66b-72b1a2c4e625","from":"Input","name":"template","type":"String","value":"{{query}}"},{"id":"892b3e12-2895-4e79-b668-5d6a6bd79fe6","from":"Expand","name":"variables","type":"Object","value":[{"id":"c779f80b-c15f-416d-ad1b-6628cb24fe8c","from":"Reference","name":"query","type":"String","value":["query"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"query","referenceNode":"jade6qm5eg"}]}]},{"id":"a4e83ae4-4f4b-4407-a2bc-a9570886329e","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"08297d4d-4f38-44c0-b049-638a2008a884","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"f4fd8b0a-aeaf-47e2-8914-5501ba5bb4b1","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"9024f034-bacc-4ac0-812f-e270fa6997b9","from":"Expand","name":"output","type":"Object","value":[{"id":"6f0fdf98-24ee-4ef0-bd62-9d4f1b1cb6ac","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1901.2896825396824,"y":1336.4880952380952,"id":"jadela7k5y","pad":6,"bold":false,"text":"普通问答结束","type":"endNodeEnd","dirty":true,"index":107,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"e09784cb-3ae9-4581-aa63-1b83f09fd7ed","from":"Reference","name":"finalOutput","type":"String","value":["output","llmOutput"],"referenceId":"6f0fdf98-24ee-4ef0-bd62-9d4f1b1cb6ac","referenceKey":"llmOutput","referenceNode":"jaded1ufou"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":214.1071428571429,"y":494,"id":"4iyvh1","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-92,"textX":0,"textY":0,"width":77.18253968253964,"hAlign":"center","height":1.988095238095184,"italic":false,"margin":20,"toShape":"jade3axbnw","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":651.2896825396825,"y":495.9880952380952,"id":"6rh9x7","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-91,"textX":0,"textY":0,"width":75,"hAlign":"center","height":0.25,"italic":false,"margin":20,"toShape":"jadelh4543","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade3axbnw","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1313.2897283160496,"y":512.7380952380952,"id":"vosd6p","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-90,"textX":0,"textY":0,"width":91.74995422363281,"hAlign":"center","height":-30.75,"italic":false,"margin":20,"toShape":"jade5wc1ee","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelh4543","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0"},{"x":1765.0396825396824,"y":481.9880952380952,"id":"4qny17","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-89,"textX":0,"textY":0,"width":103.75,"hAlign":"center","height":14,"italic":false,"margin":20,"toShape":"jadecoarmv","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade5wc1ee","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2228.7896825396824,"y":495.9880952380952,"id":"0vxnrc","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-88,"textX":0,"textY":0,"width":105.67460317460245,"hAlign":"center","height":-10.05952380952391,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadecoarmv","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1313.2897283160496,"y":632.1599778674897,"id":"v3a2mn","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-87,"textX":0,"textY":0,"width":96.74995422363281,"hAlign":"center","height":793.5781173706055,"italic":false,"margin":20,"toShape":"jaded1ufou","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelh4543","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-2"},{"x":1770.0396825396824,"y":1425.7380952380952,"id":"fv8h4v","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-86,"textX":0,"textY":0,"width":131.25,"hAlign":"center","height":17.75,"italic":false,"margin":20,"toShape":"jadela7k5y","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jaded1ufou","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"}],"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc"}],"title":"beefffd1168b4785a71f149a2a530d2f","source":"elsa","tenant":"default","setting":{"pad":10,"tag":{},"code":"","pDock":"none","hAlign":"center","margin":25,"shadow":"","shared":false,"vAlign":"top","itemPad":[5,5,5,5],"visible":true,"autoText":false,"dockMode":"none","dragable":true,"editable":true,"fontFace":"arial","fontSize":12,"infoType":{"name":"none","next":"INFORMATION"},"moveable":true,"priority":0,"allowLink":true,"autoWidth":false,"backAlpha":0.15,"backColor":"whitesmoke","dashWidth":0,"deletable":true,"fontColor":"steelblue","fontStyle":"normal","headColor":"steelblue","lineWidth":2,"underline":false,"autoHeight":false,"emphasized":false,"fontWeight":"lighter","itemScroll":{"x":0,"y":0},"lineHeight":1.5,"resizeable":true,"rotateAble":true,"scrollLock":{"x":false,"y":false},"selectable":true,"shadowData":"2px 2px 4px","borderColor":"#047bfc","borderWidth":1,"bulletSpeed":1,"focusMargin":0,"focusShadow":"","globalAlpha":1,"outstanding":false,"bulletedList":false,"cornerRadius":4,"enableSocial":true,"mouseInColor":"orange","numberedList":false,"rotateDegree":0,"captionhAlign":"center","strikethrough":false,"focusBackColor":"whitesmoke","focusFontColor":"darkorange","progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"showedProgress":false,"captionfontFace":"arial black","captionfontSize":14,"enableAnimation":false,"progressPercent":0.65,"captionfontColor":"whitesmoke","captionfontStyle":"normal","focusBorderColor":"#047bfc","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBackColor":"whitesmoke","mouseInFontColor":"orange","captionfontWeight":"lighter","captionlineHeight":1,"mouseInBorderColor":"#047bfc"},"flowMeta":{"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}},"enableText":false}') ON CONFLICT (id) DO NOTHING; INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance") VALUES ('b2ecde0b919841f5867d2e89bb46095c', 'LLM模板', 'modelengine.jade', '2024-05-24 18:29:52.043', 'modelengine.jade', '2024-05-24 18:40:48.545', - '{"id":"b2ecde0b919841f5867d2e89bb46095c","type":"jadeFlowGraph","pages":[{"x":297.46031746031747,"y":121.01190476190482,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":false,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"shapes":[{"x":189.1071428571429,"y":296.5,"id":"jade2zanyx","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-100,"textX":0,"textY":0,"width":86.642857142857,"hAlign":"center","height":96.85714285714278,"italic":false,"margin":20,"toShape":"jade0pg2ag","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":635.7499999999999,"y":393.3571428571428,"id":"jade5c5urs","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-99,"textX":0,"textY":0,"width":83.39285714285722,"hAlign":"center","height":-0.5714285714285552,"italic":false,"margin":20,"toShape":"jadewdnjbq","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0pg2ag","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1079.142857142857,"y":392.7857142857142,"id":"jade1p0cdu","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-98,"textX":0,"textY":0,"width":90.32142857142776,"hAlign":"center","height":20.642857142857054,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadewdnjbq","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":-170.8928571428571,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":103,"width":360,"height":528,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","description":"i18n_appBuilder_{flow_graph_question_description}","disableModifiable":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":275.7499999999999,"y":192.3571428571428,"id":"jade0pg2ag","pad":6,"bold":false,"text":"普通检索","type":"retrievalNodeState","dirty":true,"index":104,"width":360,"height":402,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","fitables":["modelengine.fit.jober.aipp.fitable.NaiveRAGComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"query_0ab55575-f21d-4b19-9676-57fcb4b0b783","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"},{"id":"knowledge_01c41edd-a22b-4289-b1cf-8db835833261","from":"Expand","name":"knowledge","type":"Array","value":[]},{"id":"maximum_2da115cd-c1ce-485f-ba98-b4c995f3d6ff","from":"Input","name":"maximum","type":"Integer","value":3}],"outputParams":[{"id":"output_cd5cbe89-0d9f-4cf1-9e09-afb325576b84","from":"Expand","name":"output","type":"Object","value":[{"id":"5c9c6535-c127-445a-862a-966cf1083929","from":"Input","name":"retrievalOutput","type":"String","value":"String"}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"retrievalComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":719.1428571428571,"y":-30.71403761434209,"id":"jadewdnjbq","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":892,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"Qwen1.5-32B-Chat"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"INTERNAL"}]},{"id":"6c414e75-971e-403a-b2b1-c6850f128cc4","from":"Input","name":"model","type":"String","value":"Qwen-72B"},{"id":"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4","from":"Input","name":"temperature","type":"Number","value":0.0},{"id":"88f74d78-4711-4f81-a2e7-74d0034c5e88","from":"Expand","name":"prompt","type":"Object","value":[{"id":"35a710cf-1b79-4523-b16f-b50878d677fe","from":"Input","name":"template","type":"String","value":"请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n上下文信息:\n\n{{knowledge}}\n\n问题:{{query}}"},{"id":"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d","from":"Expand","name":"variables","type":"Object","value":[{"id":"aeba7823-8d14-4750-9723-55265ae71c4e","from":"Reference","name":"knowledge","type":"String","value":["output","retrievalOutput"],"referenceId":"5c9c6535-c127-445a-862a-966cf1083929","referenceKey":"retrievalOutput","referenceNode":"jade0pg2ag"},{"id":"eee66922-4304-4209-89fc-b13ffa101630","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"a6865419-867c-4bfb-855c-f5c1876c965a","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"308e2023-a8e9-486e-9784-8680addbb786","from":"Expand","name":"workflows","type":"Array","value":[{"id":"ae0bd892-91fc-45ef-83ca-819294384386","from":"input","type":"String","value":"d613c69e-ad85-4edb-a126-8ccd6341cd64"}]},{"id":"68f92923-d5da-42ce-8478-d7ac7d90664e","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"95d84d67-3198-415e-a63c-bc9a2da8d821","from":"Expand","name":"output","type":"Object","value":[{"id":"272c927a-9e25-48b6-a921-6a8ab20267a4","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1169.4642857142849,"y":306.4285714285713,"id":"jadesoux5i","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":false,"index":106,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output","llmOutput"],"referenceId":"272c927a-9e25-48b6-a921-6a8ab20267a4","referenceKey":"llmOutput","referenceNode":"jadewdnjbq"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"}],"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc"}],"title":"b2ecde0b919841f5867d2e89bb46095c","source":"elsa","tenant":"default","setting":{"pad":10,"tag":{},"code":"","pDock":"none","hAlign":"center","margin":25,"shadow":"","shared":false,"vAlign":"top","itemPad":[5,5,5,5],"visible":true,"autoText":false,"dockMode":"none","dragable":true,"editable":true,"fontFace":"arial","fontSize":12,"infoType":{"name":"none","next":"INFORMATION"},"moveable":true,"priority":0,"allowLink":true,"autoWidth":false,"backAlpha":0.15,"backColor":"whitesmoke","dashWidth":0,"deletable":true,"fontColor":"steelblue","fontStyle":"normal","headColor":"steelblue","lineWidth":2,"underline":false,"autoHeight":false,"emphasized":false,"fontWeight":"lighter","itemScroll":{"x":0,"y":0},"lineHeight":1.5,"resizeable":true,"rotateAble":true,"scrollLock":{"x":false,"y":false},"selectable":true,"shadowData":"2px 2px 4px","borderColor":"#047bfc","borderWidth":1,"bulletSpeed":1,"focusMargin":0,"focusShadow":"","globalAlpha":1,"outstanding":false,"bulletedList":false,"cornerRadius":4,"enableSocial":true,"mouseInColor":"orange","numberedList":false,"rotateDegree":0,"captionhAlign":"center","strikethrough":false,"focusBackColor":"whitesmoke","focusFontColor":"darkorange","progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"showedProgress":false,"captionfontFace":"arial black","captionfontSize":14,"enableAnimation":false,"progressPercent":0.65,"captionfontColor":"whitesmoke","captionfontStyle":"normal","focusBorderColor":"#047bfc","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBackColor":"whitesmoke","mouseInFontColor":"orange","captionfontWeight":"lighter","captionlineHeight":1,"mouseInBorderColor":"#047bfc"},"flowMeta":{"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}},"enableText":false}') ON CONFLICT (id) DO NOTHING; - --- 租户信息(不确定是否需要) -INSERT INTO "public"."tenant" ("id", "name", "description", "avatar_id", "created_by", "updated_by", "created_at", "updated_at", "access_level") VALUES ('31f20efc7e0848deab6a6bc10fc3021e', 'appbuilder默认租户', '', ' ', '180875152679256', '180875152679256', '2023-12-29 06:46:55.681', '2023-12-29 06:49:49.632', 'PUBLIC') ON CONFLICT (id) DO NOTHING; -INSERT INTO "public"."tenant" ("id", "name", "description", "avatar_id", "created_by", "updated_by", "created_at", "updated_at", "access_level") VALUES ('727d7157b3d24209aefd59eb7d1c49ff', 'appbuilder默认租户1', '', ' ', '180875152679256', '180875152679256', '2023-12-29 06:46:55.681', '2023-12-29 06:49:49.632', 'PUBLIC') ON CONFLICT (id) DO NOTHING; - + '{"id":"b2ecde0b919841f5867d2e89bb46095c","type":"jadeFlowGraph","pages":[{"x":297.46031746031747,"y":121.01190476190482,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":false,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.8,"scaleY":0.8,"shapes":[{"x":189.1071428571429,"y":296.5,"id":"jade2zanyx","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-100,"textX":0,"textY":0,"width":86.642857142857,"hAlign":"center","height":96.85714285714278,"italic":false,"margin":20,"toShape":"jade0pg2ag","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":635.7499999999999,"y":393.3571428571428,"id":"jade5c5urs","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-99,"textX":0,"textY":0,"width":83.39285714285722,"hAlign":"center","height":-0.5714285714285552,"italic":false,"margin":20,"toShape":"jadewdnjbq","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0pg2ag","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1079.142857142857,"y":392.7857142857142,"id":"jade1p0cdu","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":-98,"textX":0,"textY":0,"width":90.32142857142776,"hAlign":"center","height":20.642857142857054,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadewdnjbq","lineWidth":2,"namespace":"flowable","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[{"x":50,"y":0},{"x":50,"y":80}],"endArrowSize":4,"arrowEndPoint":{"x":96,"y":80,"direction":{"ax":"x","key":"W","color":"whitesmoke","value":"W","cursor":"ew-resize","vector":-1}},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0,"direction":{"ax":"x","key":"E","color":"whitesmoke","value":"E","cursor":"ew-resize","vector":1}},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":-170.8928571428571,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":true,"index":103,"width":360,"height":528,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","description":"i18n_appBuilder_{flow_graph_question_description}","disableModifiable":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":275.7499999999999,"y":192.3571428571428,"id":"jade0pg2ag","pad":6,"bold":false,"text":"普通检索","type":"retrievalNodeState","dirty":true,"index":104,"width":360,"height":402,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","fitables":["modelengine.fit.jober.aipp.fitable.NaiveRAGComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"query_0ab55575-f21d-4b19-9676-57fcb4b0b783","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"},{"id":"knowledge_01c41edd-a22b-4289-b1cf-8db835833261","from":"Expand","name":"knowledge","type":"Array","value":[]},{"id":"maximum_2da115cd-c1ce-485f-ba98-b4c995f3d6ff","from":"Input","name":"maximum","type":"Integer","value":3}],"outputParams":[{"id":"output_cd5cbe89-0d9f-4cf1-9e09-afb325576b84","from":"Expand","name":"output","type":"Object","value":[{"id":"5c9c6535-c127-445a-862a-966cf1083929","from":"Input","name":"retrievalOutput","type":"String","value":"String"}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"retrievalComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":719.1428571428571,"y":-30.71403761434209,"id":"jadewdnjbq","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":true,"index":105,"width":360,"height":892,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","name":"accessInfo","type":"Object","from":"Expand","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","name":"serviceName","type":"String","from":"Input","value":"#app_builder_default_model_name#"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","name":"tag","type":"String","from":"Input","value":"#app_builder_default_tag#"}]},{"id":"6c414e75-971e-403a-b2b1-c6850f128cc4","from":"Input","name":"model","type":"String","value":"#app_builder_default_model_name#"},{"id":"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4","from":"Input","name":"temperature","type":"Number","value":0.0},{"id":"88f74d78-4711-4f81-a2e7-74d0034c5e88","from":"Expand","name":"prompt","type":"Object","value":[{"id":"35a710cf-1b79-4523-b16f-b50878d677fe","from":"Input","name":"template","type":"String","value":"请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n上下文信息:\n\n{{knowledge}}\n\n问题:{{query}}"},{"id":"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d","from":"Expand","name":"variables","type":"Object","value":[{"id":"aeba7823-8d14-4750-9723-55265ae71c4e","from":"Reference","name":"knowledge","type":"String","value":["output","retrievalOutput"],"referenceId":"5c9c6535-c127-445a-862a-966cf1083929","referenceKey":"retrievalOutput","referenceNode":"jade0pg2ag"},{"id":"eee66922-4304-4209-89fc-b13ffa101630","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"a6865419-867c-4bfb-855c-f5c1876c965a","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"308e2023-a8e9-486e-9784-8680addbb786","from":"Expand","name":"workflows","type":"Array","value":[{"id":"ae0bd892-91fc-45ef-83ca-819294384386","from":"input","type":"String","value":"d613c69e-ad85-4edb-a126-8ccd6341cd64"}]},{"id":"68f92923-d5da-42ce-8478-d7ac7d90664e","from":"Input","name":"systemPrompt","type":"String","value":""}],"outputParams":[{"id":"95d84d67-3198-415e-a63c-bc9a2da8d821","from":"Expand","name":"output","type":"Object","value":[{"id":"272c927a-9e25-48b6-a921-6a8ab20267a4","from":"Input","name":"llmOutput","type":"String","value":"","description":""}]}]}}},"triggerMode":"auto","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1}},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":1169.4642857142849,"y":306.4285714285713,"id":"jadesoux5i","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":false,"index":106,"width":360,"height":214,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","sourcePlatform":"official","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output","llmOutput"],"referenceId":"272c927a-9e25-48b6-a921-6a8ab20267a4","referenceKey":"llmOutput","referenceNode":"jadewdnjbq"}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","enableAnimation":false,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBorderColor":"rgba(28,31,35,.08)"}],"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc"}],"title":"b2ecde0b919841f5867d2e89bb46095c","source":"elsa","tenant":"default","setting":{"pad":10,"tag":{},"code":"","pDock":"none","hAlign":"center","margin":25,"shadow":"","shared":false,"vAlign":"top","itemPad":[5,5,5,5],"visible":true,"autoText":false,"dockMode":"none","dragable":true,"editable":true,"fontFace":"arial","fontSize":12,"infoType":{"name":"none","next":"INFORMATION"},"moveable":true,"priority":0,"allowLink":true,"autoWidth":false,"backAlpha":0.15,"backColor":"whitesmoke","dashWidth":0,"deletable":true,"fontColor":"steelblue","fontStyle":"normal","headColor":"steelblue","lineWidth":2,"underline":false,"autoHeight":false,"emphasized":false,"fontWeight":"lighter","itemScroll":{"x":0,"y":0},"lineHeight":1.5,"resizeable":true,"rotateAble":true,"scrollLock":{"x":false,"y":false},"selectable":true,"shadowData":"2px 2px 4px","borderColor":"#047bfc","borderWidth":1,"bulletSpeed":1,"focusMargin":0,"focusShadow":"","globalAlpha":1,"outstanding":false,"bulletedList":false,"cornerRadius":4,"enableSocial":true,"mouseInColor":"orange","numberedList":false,"rotateDegree":0,"captionhAlign":"center","strikethrough":false,"focusBackColor":"whitesmoke","focusFontColor":"darkorange","progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"showedProgress":false,"captionfontFace":"arial black","captionfontSize":14,"enableAnimation":false,"progressPercent":0.65,"captionfontColor":"whitesmoke","captionfontStyle":"normal","focusBorderColor":"#047bfc","focusBorderWidth":1,"outlineWidth":10,"outlineColor":"rgba(74,147,255,0.12)","mouseInBackColor":"whitesmoke","mouseInFontColor":"orange","captionfontWeight":"lighter","captionlineHeight":1,"mouseInBorderColor":"#047bfc"},"flowMeta":{"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"],"callback":{"fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"],"name":"通知回调","type":"general_callback"}},"enableText":false}') ON CONFLICT (id) DO NOTHING; INSERT INTO "flow_graph" ("id", "version", "tenant", "status", "name", "data", "created_by", "created_at", "updated_by", "updated_at", "previous", "is_deleted") diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/tr_t_interview.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/tr_t_interview.sql new file mode 100644 index 0000000000..4ce7779bfb --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/tr_t_interview.sql @@ -0,0 +1,70 @@ +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "collection_usr_cnt", "is_deleted", "path", "app_type", "app_suite_id", "is_active", "status", "unique_name", "publish_at", "app_id") VALUES ('fd8166b5005e4d66a77d318f3b1dd5e5', '面试助手-v3', 'Jade', '2025-04-28 08:36:13.767496', 'Jade', '2025-04-28 08:37:15.197182', '68da40b4e47e4743a59c2beac9002dc7', '24f72de428124eb19fd12db36ebcfd34', '31f20efc7e0848deab6a6bc10fc3021e', 'app', '1.0.0', '{"icon": "", "name": "面试助手-v3", "greeting": "", "store_id": "e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf", "is_update": false, "description": "", "publishedUpdateLog": "", "publishedDescription": ""}', 'active', 'workflow', 'chatbot', 0, 0, '3v8ZU1cMRV0oRcqu', '4db152b24f94473ab683b1acbfe3c865', 'ec6f8e93a80541bb930fc22678ef7043', 't', 'published', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', '2025-04-28 08:39:03', 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config" ("id", "form_id", "app_id", "tenant_id", "create_by", "create_at", "update_by", "update_at", "is_deleted") VALUES ('68da40b4e47e4743a59c2beac9002dc7', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'fd8166b5005e4d66a77d318f3b1dd5e5', '31f20efc7e0848deab6a6bc10fc3021e', 'Jade', '2025-04-28 08:36:13.767496', 'Jade', '2025-04-28 08:37:15.197182', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('ad49ec27f32542a8b3fda35f236c8d8f', 'jadewdnjbq', '64a7ca8b22414bf28abb49a75e0f1f1c', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('adc7049507694fe3aa4c9ce790d999a6', 'jadewdnjbq', '47619270aade4e828931abeccd2ee812', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('a31810d0902d4ea0bb6bb44c86a7a7a7', 'jade6qm5eg', '758521d79cf744398245162ef476708b', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('356f002b79454dc9bc20e77dfff3bb6d', 'jade0pg2ag', 'fde8766a85d44686810fe39d0be3e1b4', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('ce6c7a83b4b446e1ab36401dc6f40ceb', 'jadewdnjbq', '14d07bac14ee4621bbc49906cb9fc51a', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('dbc3c401545b4555b0387746e41744f6', 'jadewdnjbq', '705c9988eaf54f9aa9abb932c14a4232', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('47a7234ce9bb4a6b93080835045f4ef5', 'jadewdnjbq', '082cfb50221749e2b968b3e291e97223', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('88489401cddf4632ab859d7d4f6f2749', 'jadewdnjbq', 'ddf6e55b8bf44eb1a10001838906b06c', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('91247a8130cf401ab81133853cc678a5', NULL, '1aa4a8c3178c473c8fdabd37af5f0002', '68da40b4e47e4743a59c2beac9002dc7', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance", "is_deleted") VALUES ('24f72de428124eb19fd12db36ebcfd34', 'LLM模板', 'Jade', '2025-04-28 08:36:13.767496', 'Jade', '2025-04-28 08:37:15.197182', '{"id": "24f72de428124eb19fd12db36ebcfd34", "type": "jadeFlowGraph", "pages": [{"x": -3265.2857142857183, "y": 2282.8690476190486, "id": "elsa-page:tvp1s6", "bold": false, "mode": "configuration", "text": "newFlowPage", "type": "jadeFlowPage", "dirty": true, "index": 0, "width": 1600, "hAlign": "left", "height": 800, "isPage": true, "italic": false, "scaleX": 0.40000000000000013, "scaleY": 0.40000000000000013, "shapes": [{"x": -170.8928571428571, "y": 32.5, "id": "jade6qm5eg", "pad": 6, "bold": false, "text": "开始", "type": "startNodeStart", "dirty": false, "index": 0, "width": 360, "height": 225, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": true}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}], "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": false, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "startComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "mouseInBorderColor": "rgba(28,31,35,.08)"}, {"x": 2726.964285714285, "y": 97.26190476190459, "id": "jadesoux5i", "pad": 6, "bold": false, "text": "结束", "type": "endNodeEnd", "dirty": false, "index": 1, "width": 360, "height": 181, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "54dab89c-5693-4082-baa7-12c648d812f7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "ffad80c2-3f60-4d57-93b2-c2362a5dab9c", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output", "errorMessage"], "editable": true, "isRequired": true, "description": "", "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "c4469c16-88a7-4575-b339-9a06e3305f3b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 886.0342890059239, "y": -29.654761904761983, "id": "jadelk78r1", "pad": 6, "bold": false, "text": "AI简历解析插件", "type": "toolInvokeNodeState", "dirty": false, "index": 2, "width": 360, "height": 185, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "fileUrl"}, {"name": "instanceId"}], "return": {"type": "object"}, "uniqueName": "8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "fileUrl_edfec05d-9812-429f-ae55-24df5d2e1216", "from": "Reference", "name": "fileUrl", "type": "String", "value": ["output", "fileUrl"], "isRequired": true, "description": "简历文件URL", "referenceId": "45fafbfd-5966-47ba-91af-baebe09bdab1", "referenceKey": "fileUrl", "referenceNode": "jadeemu770"}, {"id": "instanceId_24e98795-b3aa-4a0f-b78f-d4b059a59768", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "description": "实例ID", "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_5315b59e-1d27-4278-a06e-f89c7627782a", "name": "output", "type": "Object", "value": [{"id": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "name": "isFileHandled", "type": "Boolean", "value": "Boolean"}, {"id": "9e3844fd-864d-4504-8f2b-dba7e919135b", "name": "cvAnalyzerPrompt", "type": "String", "value": "String"}, {"id": "50617d76-27e1-49aa-a653-1947168d8937", "name": "errorMessage", "type": "String", "value": "String"}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 337.57000329163884, "y": -111.01190476190482, "id": "jadeemu770", "pad": 6, "bold": false, "text": "代码", "type": "codeNodeState", "dirty": false, "index": 3, "width": 368, "height": 251, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "98a0c373-6b03-4fd0-bc1d-101c9f1b1fd2", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "a15b25db-133c-41b4-a329-4b9bd257ad3e", "from": "Reference", "name": "fileUrls", "type": "Array", "value": ["fileUrls"], "referenceId": "fileUrls", "referenceKey": "fileUrls", "referenceNode": "_systemEnv"}]}, {"id": "6a95280d-156a-45b2-b235-9ca2b9015d2f", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n ret: Output = {\n \"fileUrl\": args[''fileUrls''][0] if args[''fileUrls''] else \"\"\n }\n return ret", "language": "python"}, {"id": "b770fd0c-d5ae-4f2a-8129-52839becb775", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "1e2ee2b3-0106-44ac-89db-1684220c5ad4", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "object", "properties": {"fileUrl": {"type": "string", "description": ""}}, "description": ""}}}}], "outputParams": [{"id": "ebdfcbcb-4dbd-47dc-8bd6-a5752edb2487", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "45fafbfd-5966-47ba-91af-baebe09bdab1", "from": "Input", "name": "fileUrl", "type": "String", "value": "", "description": ""}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "codeComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 189.1071428571429, "y": 145, "id": "jadexi4d2i", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 4, "textX": 0, "textY": 0, "width": 148.46286043449595, "hAlign": "center", "height": -130.51190476190482, "italic": false, "margin": 20, "toShape": "jadeemu770", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade6qm5eg", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 705.5700032916388, "y": 14.488095238095184, "id": "jade3ccnk1", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 5, "textX": 0, "textY": 0, "width": 180.4642857142851, "hAlign": "center", "height": 48.35714285714283, "italic": false, "margin": 20, "toShape": "jadelk78r1", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeemu770", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1502.2128604344966, "y": -113.58333333333326, "id": "jade4h5wks", "pad": 6, "bold": false, "text": "条件", "type": "conditionNodeCondition", "dirty": false, "index": 6, "width": 600, "height": 283, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "70695434-91fb-4493-87a4-e681e99d985e", "type": "if", "runnable": true, "conditions": [{"id": "a5aef4c1-af83-475e-9805-2dbff919de10", "value": [{"id": "d9df8a7c-4cc6-4147-b73e-a9db324b1bde", "from": "Reference", "name": "left", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "4a915ffb-6082-4fca-a622-71d875b34412", "from": "Input", "name": "right", "type": "Boolean", "value": true, "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}, {"id": "8e60cd88-0799-4f12-9fcb-e20ccbc7ecd0", "value": [{"id": "f2552657-58f6-4def-a3c7-cab73575e5b0", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "9c8f2868-6722-4ed6-b388-3215bfbcacca", "from": "Input", "name": "right", "type": "String", "value": "请确认 上传的文件是一份简历", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "contains"}], "conditionRelation": "or"}, {"id": "32f55d87-ebf2-464f-9f78-ecd62e36f2dc", "type": "if", "runnable": true, "conditions": [{"id": "5e19d158-03a3-4315-b45c-ff2a0b384db0", "value": [{"id": "847d2cf6-5201-4628-a183-ca9ba62745f2", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "9bab7914-26b4-487e-b609-d9776b13e608", "from": "Reference", "name": "right", "type": "", "value": [], "referenceNode": ""}], "condition": "is not empty string"}], "conditionRelation": "and"}, {"id": "8650733f-4401-4155-8633-048a5726e6fe", "type": "else", "runnable": true, "conditions": [{"id": "4e7f3b9e-f609-4178-bd27-459d0683cac9", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2593.820003291639, "y": -446.0119047619048, "id": "jade2es9ti", "pad": 6, "bold": false, "text": "条件_1", "type": "conditionNodeCondition", "dirty": false, "index": 7, "width": 600, "height": 227, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "e2cefcda-b6e2-4713-ac74-f2266a10bb19", "type": "if", "runnable": true, "conditions": [{"id": "2f466536-4151-4013-b008-bf5af8a37edb", "value": [{"id": "c8601563-bbeb-497b-91f4-a1f81e08ad32", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "3543673f-ee62-4e18-82ef-c9e0ecc2e9a2", "from": "Reference", "name": "right", "type": "", "value": "", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "is empty string"}], "conditionRelation": "and"}, {"id": "99f4f4da-c3fd-4296-9b83-b0ff5a3ef595", "type": "else", "runnable": true, "conditions": [{"id": "d9733cad-0646-41f9-be5b-f39c9ac4d220", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2095.5463717870352, "y": 7.0834256807963385, "id": "jade9gqfjk", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 8, "textX": 0, "textY": 0, "width": 498.2736315046036, "hAlign": "center", "height": -339.59533044270114, "italic": false, "margin": 20, "toShape": "jade2es9ti", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade4h5wks", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-1|70695434-91fb-4493-87a4-e681e99d985e"}, {"x": 3641.320003291639, "y": -385.0119047619048, "id": "jade758stt", "pad": 6, "bold": false, "text": "智能表单", "type": "manualCheckNodeState", "dirty": true, "index": 9, "width": 360, "height": 352, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/e85bd769-0212-4305-b56b-01e77faa14ff/form.png", "taskId": "115b557320ac41e1b449b8107aaa1781", "formName": "面试助手3", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "isCvFile_337878e3-7720-4ece-bf92-40d4a402625c", "from": "Input", "name": "isCvFile", "type": "String", "value": "null", "isRequired": true, "referenceId": null, "referenceKey": null, "referenceNode": null}, {"id": "instanceId_59f311b0-98b3-4292-8887-aba60c48e3e4", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_e5ae0061-8304-4327-958a-a6cd0f80100d", "name": "output", "type": "Object", "value": [{"id": "ab8bf709-7374-4f33-90d6-72a64254f07e", "name": "isCvFile", "type": "String", "value": "String"}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "manualCheckComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3187.1533525195437, "y": -278.34520249139706, "id": "jadepke1tr", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 10, "textX": 0, "textY": 0, "width": 454.1666507720952, "hAlign": "center", "height": 69.33329772949224, "italic": false, "margin": 20, "toShape": "jade758stt", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade2es9ti", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 2777.570003291639, "y": 571.4880952380952, "id": "jadey32p7b", "pad": 6, "bold": false, "text": "AI提示词拼接工具", "type": "toolInvokeNodeState", "dirty": false, "index": 11, "width": 360, "height": 185, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "appId"}, {"name": "instanceId"}, {"name": "input"}], "return": {"type": "string"}, "uniqueName": "bdc009dc-969e-4839-b5d7-e9599009d50d"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "appId_0293cca7-c1ea-45b0-8ca8-b37a55f99303", "from": "Reference", "name": "appId", "type": "String", "value": ["appId"], "isRequired": true, "description": "应用ID", "referenceId": "appId", "referenceKey": "appId", "referenceNode": "_systemEnv"}, {"id": "instanceId_93f70f4f-e2b6-4518-a53e-af0ccdc99d2c", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "description": "实例ID", "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}, {"id": "input_4ac2267a-fa7a-462c-ad8d-7b2936e0fac3", "from": "Reference", "name": "input", "type": "String", "value": ["Question"], "isRequired": true, "description": "用户输入", "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}], "outputParams": [{"id": "output_65d89d72-205f-4bf6-bb6e-a11bebaa8497", "name": "output", "type": "String", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3412.570003291639, "y": -1506.0119047619048, "id": "jade39q3lc", "pad": 6, "bold": false, "text": "大模型", "type": "llmNodeState", "dirty": false, "index": 12, "width": 360, "height": 343, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "009c2461-9c7e-4f85-bf43-ad887acd6a8a", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f78bde02-c766-4be5-bab6-70d2f6e6de6c", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "af11f474-c1ae-4e07-ad7e-92f378a9d19d", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "5fe88d69-b7bb-4ff3-abb1-a2fbeb4ec240", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "b23e18ed-c36f-4692-9917-4971f9c1659b", "from": "Input", "name": "temperature", "type": "Number", "value": 0.7}, {"id": "cd4194cd-db96-4164-8a45-9ca4ea30cd81", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "184c22f2-c6f5-4bb6-81ca-d214eb266b35", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"}, {"id": "30eacd51-29f6-4a90-8e17-4e4ffe1ddcc6", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "155a2932-e37e-4c18-b14b-6d44b2a43ae4", "from": "Reference", "name": "isFileHandled", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "950c301c-81ba-41a6-bda5-aa37360e92a5", "from": "Reference", "name": "cvAnalyzerprompt", "type": "String", "value": ["output", "cvAnalyzerPrompt"], "referenceId": "9e3844fd-864d-4504-8f2b-dba7e919135b", "referenceKey": "cvAnalyzerPrompt", "referenceNode": "jadelk78r1"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "a7b687f8-e993-4e5a-9a10-bb6df8df25d9", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "6ec07829-b51c-4397-8670-59659e43036f", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "3733d6f2-0ea2-4dab-9f5b-423352d4af0b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "091115ca-4fd9-4265-8280-5d70cfcdcfca", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "b0a3bdd3-7a3c-4657-8288-4cf8a98d33a0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "51b4e7bb-19f5-4477-a2df-b974f8c97ba7", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3187.1533525195437, "y": -325.3452101207916, "id": "jadeqraasb", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 13, "textX": 0, "textY": 0, "width": 225.41665077209518, "hAlign": "center", "height": -1009.1666946411133, "italic": false, "margin": 20, "toShape": "jade39q3lc", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade2es9ti", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-0|e2cefcda-b6e2-4713-ac74-f2266a10bb19"}, {"x": 4292.748574720212, "y": -233.29761904761904, "id": "jadegqixt5", "pad": 6, "bold": false, "text": "条件_2", "type": "conditionNodeCondition", "dirty": false, "index": 14, "width": 600, "height": 227, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "bb4f8032-19cc-445b-a2ff-5cda77ae59e3", "type": "if", "runnable": true, "conditions": [{"id": "8085598f-b128-4749-940c-b4bda2619cba", "value": [{"id": "9a656755-91fe-47b6-a54c-4b2be274978a", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "37c01598-663a-476c-9a99-202b73131d4b", "from": "Input", "name": "right", "type": "String", "value": "", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "is not empty string"}], "conditionRelation": "and"}, {"id": "319689dd-4250-4f0d-9c1b-d071013ac712", "type": "else", "runnable": true, "conditions": [{"id": "737ba1f5-dbb5-401c-9bf5-e18597f02123", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 5110.605717577355, "y": -790.4761904761904, "id": "jadem1n9u5", "pad": 6, "bold": false, "text": "大模型_1", "type": "llmNodeState", "dirty": false, "index": 15, "width": 360, "height": 343, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "b8519f34-6d5f-42cd-b949-438f44d3210e", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "8c18f95c-1585-4e11-be07-de595ad13a92", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "8fd16d6c-b043-46dd-8b55-f0b8dee2615c", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "bd6f983c-f8e8-4538-ac66-60024d09c0a1", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "0263e85d-6125-4e27-ab7a-d2bc351cf414", "from": "Input", "name": "temperature", "type": "Number", "value": 0.6}, {"id": "1cf0a04f-2ca7-41fd-bec4-f9a1dd016c7f", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "a1335ac5-b02b-47c5-91d4-ecb945833ef3", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"}, {"id": "d6309a8b-d15b-4380-b6c6-ef000a9e5201", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "25e56c39-1668-4428-8174-116b1a906bbd", "from": "Reference", "name": "isFileHandled", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "498b5c92-92ef-4ebf-9346-f2847fb88a3a", "from": "Reference", "name": "cvAnalyzerprompt", "type": "String", "value": ["output", "cvAnalyzerPrompt"], "referenceId": "9e3844fd-864d-4504-8f2b-dba7e919135b", "referenceKey": "cvAnalyzerPrompt", "referenceNode": "jadelk78r1"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "e1ad1e33-6a7c-451d-ba5a-158595cb2af8", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "3c3f111b-85ec-42c0-af8e-f3352518a29b", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "1cd6884e-c3e3-483b-baeb-d592c4f58ebf", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "7d01bbd3-df9c-4884-8d02-54b195096857", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "9e2fb06e-0276-48c8-8606-c36a31cf93b0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "3eab6484-fa97-4e06-a0cc-263d3ed5274a", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "d438bc29-a627-44c2-bd57-4bf64eceba5a", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 4886.081819043942, "y": -112.63091010139105, "id": "jadee79arc", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 16, "textX": 0, "textY": 0, "width": 224.52389853341265, "hAlign": "center", "height": -506.3452803747993, "italic": false, "margin": 20, "toShape": "jadem1n9u5", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadegqixt5", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-0|bb4f8032-19cc-445b-a2ff-5cda77ae59e3"}, {"x": 3451.3200032916393, "y": 321.4880952380952, "id": "jadedzi58q", "pad": 6, "bold": false, "text": "大模型_2", "type": "llmNodeState", "dirty": false, "index": 17, "width": 360, "height": 343, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "1d11a986-7dc9-4ba3-84d8-3f15810ae587", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "4ebabb28-8981-46d6-a458-953082c01f9a", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "5af05d34-4b6e-435f-872c-96fa990a1476", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "ea9b2985-96e6-480c-b065-c7cce122b57a", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "32801848-b75f-4ac0-b050-f3dfbd57e3ad", "from": "Input", "name": "temperature", "type": "Number", "value": 0.7}, {"id": "1891af57-83ff-458d-ac70-d35a7af89705", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "e9e29b94-85e2-47c8-84d2-055a77660b88", "from": "Input", "name": "template", "type": "String", "value": "{{query}}"}, {"id": "29e702cd-b721-442a-9893-81298cfa2953", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "c34fda11-8f17-4c85-96aa-35c5bb311e2a", "from": "Reference", "name": "query", "type": "String", "value": ["output"], "referenceId": "output_65d89d72-205f-4bf6-bb6e-a11bebaa8497", "referenceKey": "output", "referenceNode": "jadey32p7b"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "2b06515f-c9d5-41bd-bfbe-e1b1382d6ec1", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "0b5e2451-ed5f-46b2-8600-8893963296e4", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "acb67126-1a1b-4430-946e-30e768712333", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "f7d670ab-1cad-4028-a9c8-c84a5a170ac1", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "29ed2025-1eaa-462a-a37a-e05677eef8c4", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "451cf45e-6250-4f08-9d73-51d94433d749", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "dab1caf6-f171-4f6d-beb7-97c913fa1637", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3137.570003291639, "y": 663.9880952380952, "id": "jadeqdcs4x", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 18, "textX": 0, "textY": 0, "width": 313.75000000000045, "hAlign": "center", "height": -171, "italic": false, "margin": 20, "toShape": "jadedzi58q", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadey32p7b", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4101.320003291639, "y": 470.2380952380952, "id": "jadefw1zfk", "pad": 6, "bold": false, "text": "结束_2", "type": "endNodeEnd", "dirty": false, "index": 19, "width": 360, "height": 181, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "b6d32b12-26db-4a72-974b-7e197326e653", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "5c0e817c-8851-4350-9c5b-0ac5792a4e71", "from": "Reference", "name": "output", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "451cf45e-6250-4f08-9d73-51d94433d749", "referenceKey": "llmOutput", "referenceNode": "jadedzi58q"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "5e7a8388-8193-4f2d-a44a-094ab6fc576b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 3811.3200032916393, "y": 492.9880952380952, "id": "jadecvs6at", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 20, "textX": 0, "textY": 0, "width": 290, "hAlign": "center", "height": 67.75, "italic": false, "margin": 20, "toShape": "jadefw1zfk", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadedzi58q", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 5366.320003291639, "y": 447.7380952380952, "id": "jadexn70e2", "pad": 6, "bold": false, "text": "结束_4", "type": "endNodeEnd", "dirty": false, "index": 21, "width": 360, "height": 181, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "5268f248-07c8-4871-8732-e007316214fc", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "1434e18b-bd1b-4286-bc2f-cfb2cb1e9c1e", "from": "Reference", "name": "output", "type": "String", "value": ["output", "errorMessage"], "editable": true, "isRequired": true, "description": "", "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "e0c88e2d-1373-4606-8aaf-2d8ff330a27f", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 4886.081819043942, "y": -65.6309930710566, "id": "jade2u3je1", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 22, "textX": 0, "textY": 0, "width": 480.23818424769706, "hAlign": "center", "height": 603.8690883091517, "italic": false, "margin": 20, "toShape": "jadexn70e2", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadegqixt5", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 4001.320003291639, "y": -209.01190476190482, "id": "jadet1dvap", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 23, "textX": 0, "textY": 0, "width": 291.4285714285729, "hAlign": "center", "height": 89.21428571428578, "italic": false, "margin": 20, "toShape": "jadegqixt5", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade758stt", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 5660.4628604345, "y": -664.6309523809527, "id": "jade9puqh3", "pad": 6, "bold": false, "text": "文本提取", "type": "textExtractionNodeState", "dirty": false, "index": 24, "width": 360, "height": 333, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "正在生成推荐面试问题...", "inputParams": [{"id": "extractParam_e7356380-7e19-4def-a1b5-3f7fb62c06af", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_da536c71-b299-40f0-8bca-720364fe20a0", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "3eab6484-fa97-4e06-a0cc-263d3ed5274a", "referenceKey": "llmOutput", "referenceNode": "jadem1n9u5"}, {"id": "desc_ddf67a20-776a-4c29-b741-9b5ed31a5104", "from": "Input", "name": "desc", "type": "String", "value": ""}, {"id": "outputSchema_1d2b703c-237b-40c9-9286-0d427c2b7312", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"}, {"id": "0d0a1d90-49b7-4bc6-af15-4c1c2905dadd", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "928af3d3-8671-4d93-a091-fd1662b74474", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "2ee47e96-94a7-4ee4-b616-36201338a7e4", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_236f8466-2e34-4b41-a199-a8d2843261ac", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_88d92098-2b56-4757-9fb6-08f3628e19ce", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_91b53de6-83c0-44eb-bf92-2585268d7526", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_f77ab974-f9c6-4ad3-9bcd-818fad8d96e2", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_7894b9b6-55e0-4b6e-acf8-d430e396d00c", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_a59975ed-71a1-4647-83b1-20c3aefe44f1", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_643f02b9-d709-4fc9-a04b-f9e590d96786", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "28eac6e6-2877-426a-a22c-5e425bfd2b1e", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "fa30b424-be71-443d-b18d-fbd32144e049", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "c19b4740-ce95-4483-b789-47286a19096b", "from": "Input", "name": "questions", "type": "Array", "value": "", "description": "针对简历的问题列表"}]}, {"id": "success_67bf7577-82ac-47b8-b8fa-7d7ebc53513a", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "stageDesc": "正在生成推荐面试问题...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "textExtractionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 5470.605717577355, "y": -618.9761904761904, "id": "jade79wnvk", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 25, "textX": 0, "textY": 0, "width": 189.85714285714494, "hAlign": "center", "height": 120.84523809523762, "italic": false, "margin": 20, "toShape": "jade9puqh3", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadem1n9u5", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 2095.5463717870352, "y": 63.08343664805096, "id": "jadekuuju4", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 26, "textX": 0, "textY": 0, "width": 631.4179139272496, "hAlign": "center", "height": 124.67846811385363, "italic": false, "margin": 20, "toShape": "jadesoux5i", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade4h5wks", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-1|32f55d87-ebf2-464f-9f78-ecd62e36f2dc"}, {"x": 2095.5463717870352, "y": 110.08335367838541, "id": "jade1o7wna", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 27, "textX": 0, "textY": 0, "width": 682.0236315046036, "hAlign": "center", "height": 553.9047415597098, "italic": false, "margin": 20, "toShape": "jadey32p7b", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade4h5wks", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 6189.070031819352, "y": -567.4536159624079, "id": "jademrtwtm", "pad": 6, "bold": false, "text": "智能表单_1", "type": "manualCheckNodeState", "dirty": true, "index": 28, "width": 360, "height": 473, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png", "taskId": "d496c444a3174beabbcec5441aed40e2", "formName": "面试评价表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "questions_7e08f8cb-47ae-47df-b1a7-aaf62d7be03f", "from": "Reference", "name": "questions", "type": "Array", "value": ["output", "extractedParams", "questions"], "isRequired": true, "referenceId": "c19b4740-ce95-4483-b789-47286a19096b", "referenceKey": "questions", "referenceNode": "jade9puqh3"}], "outputParams": [{"id": "output_f00ac42b-c893-479b-b0ee-5cbe996c885a", "name": "output", "type": "Object", "value": [{"id": "27aa7c57-e31e-482a-98f0-bdbcc21a6138", "name": "qeMap", "type": "Array", "value": "Array"}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "manualCheckComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 6020.4628604345, "y": -498.13095238095275, "id": "jadem1n5oj", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 29, "textX": 0, "textY": 0, "width": 168.60717138485234, "hAlign": "center", "height": 167.17733641854488, "italic": false, "margin": 20, "toShape": "jademrtwtm", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade9puqh3", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 6915.736698486009, "y": -872.4536159624076, "id": "jadezaa7cy", "pad": 6, "bold": false, "text": "大模型_3", "type": "llmNodeState", "dirty": true, "index": 30, "width": 360, "height": 411, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "fcd5702a-23f8-482e-a193-945676ca99ef", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "3a0ce6b3-8e98-4045-9323-957415084fba", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "35594014-3ca3-46b7-ac71-acd76b443952", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f3d8a723-81d0-48e2-b671-224caf7f0f17", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "1bf48ee9-ac82-4df5-be74-df96958f3f05", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "eb7a528c-c012-4374-bbf0-4a88e4727334", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "aeb114f8-9032-4be6-b9f2-a0301fd5cf05", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"}, {"id": "c4b818ba-1f8a-4f18-b196-c31bba136185", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "c709baca-8d63-4dd4-bced-e2762386a86a", "from": "Reference", "name": "qeMap", "type": "Array", "value": ["output", "qeMap"], "referenceId": "27aa7c57-e31e-482a-98f0-bdbcc21a6138", "referenceKey": "qeMap", "referenceNode": "jademrtwtm"}]}]}, {"id": "59d76a4e-1fd8-4950-9f84-6ebc17b7553c", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "a5b51a1c-68d7-4d8b-8791-9f788cd52f22", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "e1322719-1397-4dc8-a21e-674e1fb0eec0", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "892cf7e4-85c2-428d-b45b-0112a5978bd3", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "417ec551-d27e-49bc-b8ee-002a4bbd5b48", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "58e76471-8fb4-456d-9415-88192b008bd0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "23891b11-11a1-4915-80d1-53c515335dd5", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "c6b85478-009a-4ade-8bc2-a288e226e6db", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 6549.070031819352, "y": -330.95361596240787, "id": "jadeeeqmda", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 31, "textX": 0, "textY": 0, "width": 366.66666666665697, "hAlign": "center", "height": -335.9999999999998, "italic": false, "margin": 20, "toShape": "jadezaa7cy", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jademrtwtm", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 7634.070031819344, "y": -579.1202826290745, "id": "jadeohika6", "pad": 6, "bold": false, "text": "结束_3", "type": "endNodeEnd", "dirty": false, "index": 32, "width": 360, "height": 181, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "73bad02b-26cb-417a-b1e0-85de07f398fa", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "aa9bc2e9-6dbf-4a6e-8d05-772f148da36e", "from": "Input", "name": "output", "type": "String", "value": "面试完成", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "519710b6-f147-4cdb-98e7-b60aa0600be5", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 7275.736698486009, "y": -666.9536159624076, "id": "jadey8ppgc", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 33, "textX": 0, "textY": 0, "width": 358.33333333333485, "hAlign": "center", "height": 178.33333333333314, "italic": false, "margin": 20, "toShape": "jadeohika6", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadezaa7cy", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4079.0700318193494, "y": -1339.1202826290773, "id": "jadelwn1gx", "pad": 6, "bold": false, "text": "文本提取_1", "type": "textExtractionNodeState", "dirty": false, "index": 34, "width": 360, "height": 333, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "正在生成推荐面试问题...", "inputParams": [{"id": "c5fcdff0-2822-49c1-805d-242d38acf952", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "0fc3e0ac-307a-4c9c-8446-da801ca2a86e", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2", "referenceKey": "llmOutput", "referenceNode": "jade39q3lc"}, {"id": "bb3f946b-a504-44c9-a9e6-7271052ca40d", "from": "Input", "name": "desc", "type": "String", "value": ""}, {"id": "e99d0bd6-62f8-4f1b-80e9-72f7a2a19687", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"}, {"id": "c89f3da6-f54f-46d5-bb2d-63c4126aa682", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "de26c850-03f2-4e24-80c2-aedd0482a463", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a8bde3eb-1f41-4e9c-b591-1ea8637da7c7", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,admin"}]}, {"id": "52e82f8a-d0ec-4448-a8fa-a8ecc021a80d", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "4fb27b26-d072-41d3-a6ab-7ed461198348", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "73c5d5a6-ee64-4eae-b8e0-6afef02ce1f1", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "301ee3a6-6e1d-4046-bb13-2ab2b29e5892", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "8d0d7545-9b23-41f1-9d58-3cdf5203ea71", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "bc1fbeed-2b96-4bfd-9810-45fd2f0e01df", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "64c7d8a7-daae-44d9-9d42-f7d7ead0f5f8", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "225af02f-2c58-4e8d-bcb9-d56c6eb4b426", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "13ee6c77-3f84-4758-9ea1-6d1a17923ae4", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "b56c96ec-0b9f-44d3-8e8b-b952d171c580", "from": "Input", "name": "questions", "type": "Array", "value": "", "description": "针对简历的问题列表"}]}, {"id": "e545a304-53b9-40de-a559-f7346aa69a01", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "stageDesc": "正在生成推荐面试问题...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "textExtractionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3772.570003291639, "y": -1334.5119047619048, "id": "jade9m2sga", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 35, "textX": 0, "textY": 0, "width": 306.5000285277106, "hAlign": "center", "height": 161.89162213282748, "italic": false, "margin": 20, "toShape": "jadelwn1gx", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade39q3lc", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4615.403365152686, "y": -1370.786949295743, "id": "jade22p95w", "pad": 6, "bold": false, "text": "智能表单_2", "type": "manualCheckNodeState", "dirty": true, "index": 36, "width": 360, "height": 314, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png", "taskId": "d496c444a3174beabbcec5441aed40e2", "formName": "面试评价表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "questions_f1544f8f-d6ec-4dc2-97d8-dbc05a5fd33b", "from": "Reference", "name": "questions", "type": "Array", "value": ["output", "extractedParams", "questions"], "isRequired": true, "referenceId": "b56c96ec-0b9f-44d3-8e8b-b952d171c580", "referenceKey": "questions", "referenceNode": "jadelwn1gx"}], "outputParams": [{"id": "output_4daf0f63-1cc3-46f9-b42a-e36e953141f7", "name": "output", "type": "Object", "value": [{"id": "6c87c01e-4cd3-483e-a646-6b1f63597e91", "name": "qeMap", "type": "Array", "value": "Array"}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "manualCheckComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 4439.070031819349, "y": -1172.6202826290773, "id": "jade5q1tkm", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 37, "textX": 0, "textY": 0, "width": 176.33333333333667, "hAlign": "center", "height": -41.166666666665606, "italic": false, "margin": 20, "toShape": "jade22p95w", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadelwn1gx", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 5333.0700318193485, "y": -1757.7869492957443, "id": "jadeuhtp3b", "pad": 6, "bold": false, "text": "大模型_4", "type": "llmNodeState", "dirty": false, "index": 38, "width": 360, "height": 411, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "a3bd214d-556f-4f1e-beac-726aec372264", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a2a846ac-8371-42f5-9617-aab697031aad", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "f313cfeb-6029-4d0d-b938-026ffa7cca1e", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "48dd4324-a11f-4d7e-bae8-f4874f526ab7", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "e05bdaeb-3cda-40a5-9144-e564f494b002", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "ee5b87db-2bb7-40a9-8836-658a2047ad98", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "41de7c20-0de4-4a75-b1a1-898c2ac9c2fd", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"}, {"id": "af7e6d67-5162-4c90-884f-9299e76ffd5a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "2e069130-be89-4292-8a00-9827309710dc", "from": "Reference", "name": "qeMap", "type": "Array", "value": ["output", "qeMap"], "referenceId": "6c87c01e-4cd3-483e-a646-6b1f63597e91", "referenceKey": "qeMap", "referenceNode": "jade22p95w"}]}]}, {"id": "52e1fdbe-6598-4a0e-8a5c-1bb8de01534e", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "9098e23e-62e6-4272-a746-7063e45710aa", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "f3dd4d91-9e84-45c4-bca2-5c27c4ae479a", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "d443924b-69a7-4519-a186-85b6bcd14edd", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "6ab5352d-1753-4f90-9c6b-2991d69dddc4", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "bf5d2b76-30ca-4cd6-bdfd-a61d70b1cf85", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "8bb45d25-fc74-45d5-8777-21670075be2e", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "2f382582-cd0c-4a4d-a994-58ba4b07dd97", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 4975.403365152686, "y": -1213.786949295743, "id": "jade5xr7fy", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 39, "textX": 0, "textY": 0, "width": 357.6666666666624, "hAlign": "center", "height": -338.50000000000136, "italic": false, "margin": 20, "toShape": "jadeuhtp3b", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade22p95w", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 6125.736698486013, "y": -1567.4536159624104, "id": "jadehvxxbs", "pad": 6, "bold": false, "text": "结束_1", "type": "endNodeEnd", "dirty": false, "index": 40, "width": 360, "height": 181, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "6d59e150-b944-42bc-a131-9e3d240f3d06", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "a1c67227-e131-4f88-93aa-e1840f3621ea", "from": "Reference", "name": "output", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "8bb45d25-fc74-45d5-8777-21670075be2e", "referenceKey": "llmOutput", "referenceNode": "jadeuhtp3b"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "b8e2ec79-b8a6-46b0-9e20-14662fabd988", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 5693.0700318193485, "y": -1552.2869492957443, "id": "jade3xtwme", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 41, "textX": 0, "textY": 0, "width": 432.66666666666424, "hAlign": "center", "height": 75.33333333333394, "italic": false, "margin": 20, "toShape": "jadehvxxbs", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeuhtp3b", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1246.034289005924, "y": 62.84523809523802, "id": "jade7dr3c3", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 42, "textX": 0, "textY": 0, "width": 256.1785714285727, "hAlign": "center", "height": -34.928571428571274, "italic": false, "margin": 20, "toShape": "jade4h5wks", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadelk78r1", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}], "vAlign": "top", "itemPad": [0, 0, 0, 0], "division": -1, "dockMode": "none", "fontFace": "arial", "fontSize": 18, "hideText": true, "moveable": true, "shapesAs": {}, "backColor": "#fbfbfc", "container": "elsa-page:tvp1s6", "dockAlign": "top", "fontColor": "#ECD0A7", "fontStyle": "normal", "itemSpace": 5, "namespace": "jadeFlow", "fontWeight": "bold", "itemScroll": {"x": 0, "y": 0}, "borderColor": "white", "focusBackColor": "#fbfbfc"}], "title": "24f72de428124eb19fd12db36ebcfd34", "source": "elsa", "tenant": "31f20efc7e0848deab6a6bc10fc3021e", "setting": {"pad": 10, "tag": {}, "code": "", "pDock": "none", "hAlign": "center", "margin": 25, "shadow": "", "shared": false, "vAlign": "top", "itemPad": [5, 5, 5, 5], "visible": true, "autoText": false, "dockMode": "none", "dragable": true, "editable": true, "fontFace": "arial", "fontSize": 12, "infoType": {"name": "none", "next": "INFORMATION"}, "moveable": true, "priority": 0, "allowLink": true, "autoWidth": false, "backAlpha": 0.15, "backColor": "whitesmoke", "dashWidth": 0, "deletable": true, "fontColor": "steelblue", "fontStyle": "normal", "headColor": "steelblue", "lineWidth": 2, "underline": false, "autoHeight": false, "emphasized": false, "fontWeight": "lighter", "itemScroll": {"x": 0, "y": 0}, "lineHeight": 1.5, "resizeable": true, "rotateAble": true, "scrollLock": {"x": false, "y": false}, "selectable": true, "shadowData": "2px 2px 4px", "borderColor": "#047bfc", "borderWidth": 1, "bulletSpeed": 1, "focusMargin": 0, "focusShadow": "", "globalAlpha": 1, "outstanding": false, "bulletedList": false, "cornerRadius": 4, "enableSocial": true, "mouseInColor": "orange", "numberedList": false, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "rotateDegree": 0, "captionhAlign": "center", "strikethrough": false, "focusBackColor": "whitesmoke", "focusFontColor": "darkorange", "progressStatus": {"name": "NONE", "next": "UNKNOWN", "color": "gray"}, "showedProgress": false, "allNodeNumLimit": 99, "captionfontFace": "arial black", "captionfontSize": 14, "enableAnimation": false, "progressPercent": 0.65, "captionfontColor": "whitesmoke", "captionfontStyle": "normal", "focusBorderColor": "#047bfc", "focusBorderWidth": 1, "mouseInBackColor": "whitesmoke", "mouseInFontColor": "orange", "captionfontWeight": "lighter", "captionlineHeight": 1, "mouseInBorderColor": "#047bfc", "sameTypeNodeNumLimit": 19}, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}, "enableText": false}', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('47619270aade4e828931abeccd2ee812', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'ability', 'String', 'null', 'none', 'workflow', '能力配置', 1, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('758521d79cf744398245162ef476708b', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'chat', 'String', 'null', 'none', 'workflow', '聊天设置', 3, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('fde8766a85d44686810fe39d0be3e1b4', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'memory', 'List', '["jade6qm5eg","memory"]', 'graph', 'chat', '多轮对话', 5, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('14d07bac14ee4621bbc49906cb9fc51a', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'opening', 'String', '"Hi~你好!"', 'input', 'chat', '开场白', 4, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('705c9988eaf54f9aa9abb932c14a4232', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'enterWorkflow', 'String', 'null', 'none', 'ability', '进入工作流编排', 2, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('082cfb50221749e2b968b3e291e97223', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'inspiration', 'object', '{"category":[{"id":"root","title":"root","children":[{"id":"a8bj40","title":"面试官灵感大全","parent":"root:a8bj40","children":[{"id":"0t579o","title":"综合能力问题","parent":"a8bj40:0t579o","children":[]},{"id":"lcp0bt","title":"总结面试意见","parent":"a8bj40:lcp0bt","children":[]},{"id":"tnzbnl","title":"C语言","parent":"a8bj40:tnzbnl","children":[]},{"id":"25qf59","title":"C++语言","parent":"a8bj40:25qf59","children":[]},{"id":"1189ke","title":"Java语言","parent":"a8bj40:1189ke","children":[]},{"id":"4w1hn2","title":"JavaScript语言","parent":"a8bj40:4w1hn2","children":[]},{"id":"h58abh","title":"Python语言","parent":"a8bj40:h58abh","children":[]},{"id":"68y8eo","title":"Go语言","parent":"a8bj40:68y8eo","children":[]},{"id":"c5zmf1","title":"通用软件知识","parent":"a8bj40:c5zmf1","children":[]},{"id":"gpk5lc","title":"计算机网络","parent":"a8bj40:gpk5lc","children":[]},{"id":"03wuou","title":"操作系统","parent":"a8bj40:03wuou","children":[]},{"id":"1mctx4","title":"数据库","parent":"a8bj40:1mctx4","children":[]},{"id":"zxm7kk","title":"缓存","parent":"a8bj40:zxm7kk","children":[]},{"id":"n4nrbs","title":"消息队列","parent":"a8bj40:n4nrbs","children":[]}]}]}],"dimension":false,"inspirations":[{"id":"kw0dn0","auto":false,"name":"编译时多态与运行时多态","prompt":"<步骤:生成编译时多态与运行时多态相关面试问题>","category":"a8bj40:c5zmf1","description":"编译时多态与运行时多态","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对编译时多态与运行时多态的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"pt4u70","auto":false,"name":"多线程环境避免数据竞争","prompt":"<步骤:生成多线程环境数据竞争相关面试问题>","category":"a8bj40:c5zmf1","description":"多线程环境避免数据竞争","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对多线程环境数据竞争的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"mzombx","auto":false,"name":"内存管理中的栈与堆","prompt":"<步骤:生成内存管理中的堆与栈相关面试问题>","category":"a8bj40:c5zmf1","description":"内存管理中的栈与堆","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对内存管理中的堆与栈的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"tx31bf","auto":false,"name":"C语言的指针和数组","prompt":"<步骤:生成C语言的指针和数组相关面试问题>","category":"a8bj40:tnzbnl","description":"C语言的指针和数组","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C语言的指针和数组的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"xhvq4g","auto":false,"name":"C语言的缓冲区溢出","prompt":"<步骤:生成C语言的缓冲区溢出相关面试问题>","category":"a8bj40:tnzbnl","description":"C语言的缓冲区溢出","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C语言的缓冲区溢出的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"mqbt1m","auto":false,"name":"C语言的内存对齐","prompt":"<步骤:生成C语言的内存对齐相关面试问题>","category":"a8bj40:tnzbnl","description":"C语言的内存对齐","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C语言的内存对齐的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"bpaqda","auto":false,"name":"C语言的volatile关键字","prompt":"<步骤:生成C语言的volatile关键字相关面试问题>","category":"a8bj40:tnzbnl","description":"C语言的volatile关键字","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C语言的volatile关键字的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"srl93z","auto":false,"name":"C++语言的RAII","prompt":"<步骤:生成C++语言的RAII相关面试问题>","category":"a8bj40:tnzbnl","description":"Resource Acquisition Is Initialization","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对RAII(Resource Acquisition Is Initialization)的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"7n0k0t","auto":false,"name":"C++语言的虚函数表","prompt":"<步骤:生成C++语言的虚函数表相关面试问题>","category":"a8bj40:25qf59","description":"C++语言的虚函数表","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C++语言的虚函数表的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"i0u7ja","auto":false,"name":"C++语言的移动和转发","prompt":"<步骤:生成C++语言的移动和转发相关面试问题>","category":"a8bj40:25qf59","description":"移动(std::move)和转发(std::forward)","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C++语言的移动(std::move)和转发(std::forward)的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"plk389","auto":false,"name":"C++语言的类定义五法则","prompt":"<步骤:生成C++语言的类定义五法则相关面试问题>","category":"a8bj40:25qf59","description":"C++语言的类定义五法则","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对C++语言的类定义五法则的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"v1diuh","auto":false,"name":"Java语言的垃圾回收","prompt":"<步骤:生成Java语言的垃圾回收机制相关面试问题>","category":"a8bj40:1189ke","description":"Java语言的垃圾回收","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Java语言的垃圾回收机制的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ubqahw","auto":false,"name":"Java语言的线程同步机制","prompt":"<步骤:生成Java语言的线程同步机制相关面试问题>","category":"a8bj40:1189ke","description":"Java语言的线程同步机制","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Java语言的线程同步机制的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ewa086","auto":false,"name":"Java语言的接口和抽象类","prompt":"<步骤:生成Java语言的接口和抽象类相关面试问题>","category":"a8bj40:1189ke","description":"Java语言的接口和抽象类","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Java语言的接口和抽象类的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"xrhicq","auto":false,"name":"Java语言的堆栈内存管理","prompt":"<步骤:生成Java语言的堆栈内存管理相关面试问题>","category":"a8bj40:1189ke","description":"Java语言的堆栈内存管理","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Java语言的堆栈内存管理的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"pqyb2c","auto":false,"name":"JavaScript语言的闭包","prompt":"<步骤:生成JavaScript语言的闭包相关面试问题>","category":"a8bj40:4w1hn2","description":"JavaScript语言的闭包","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对JavaScript语言的闭包的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"6onib9","auto":false,"name":"JavaScript语言的事件循环","prompt":"<步骤:生成JavaScript语言的事件循环相关面试问题>","category":"a8bj40:4w1hn2","description":"JavaScript语言的事件循环","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对JavaScript语言的事件循环的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"q5bcdi","auto":false,"name":"JavaScript语言的原型链","prompt":"<步骤:生成JavaScript语言的原型链相关面试问题>","category":"a8bj40:4w1hn2","description":"JavaScript语言的原型链","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对JavaScript语言的原型链的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"a76ukp","auto":false,"name":"JavaScript语言的this关键字","prompt":"<步骤:生成JavaScript语言的this关键字相关面试问题>","category":"a8bj40:4w1hn2","description":"JavaScript语言的this关键字","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对JavaScript语言的this关键字的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"xljl9w","auto":false,"name":"Python语言的生成器和迭代器","prompt":"<步骤:生成Python语言的生成器和迭代器相关面试问题>","category":"a8bj40:h58abh","description":"Python语言的生成器和迭代器","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Python语言的生成器和迭代器的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"75d89u","auto":false,"name":"Python语言的全局解释器锁","prompt":"<步骤:生成Python语言的全局解释器锁相关面试问题>","category":"a8bj40:h58abh","description":"Python语言的全局解释器锁","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Python语言的全局解释器锁的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"hhk1gi","auto":false,"name":"Python语言的元类","prompt":"<步骤:生成Python语言的元类相关面试问题>","category":"a8bj40:h58abh","description":"Python语言的元类","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Python语言的元类的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ai5b8k","auto":false,"name":"Python语言的装饰器","prompt":"<步骤:生成Python语言的装饰器相关面试问题>","category":"a8bj40:h58abh","description":"Python语言的装饰器","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Python语言的装饰器的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"tx9cn5","auto":false,"name":"Go语言的Goroutine","prompt":"<步骤:生成Go语言的Goroutine相关面试问题>","category":"a8bj40:68y8eo","description":"Go语言的Goroutine","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Go语言的Goroutine的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"fnrcdr","auto":false,"name":"Go语言的通道","prompt":"<步骤:生成Go语言的通道相关面试问题>","category":"a8bj40:68y8eo","description":"Go语言的通道","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Go语言的通道的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"95z27f","auto":false,"name":"Go语言的select语句工作机制","prompt":"<步骤:生成Go语言的select语句工作机制相关面试问题>","category":"a8bj40:68y8eo","description":"Go语言的select语句工作机制","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Go语言的select语句工作机制的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"xv3jv9","auto":false,"name":"Go语言避免数据竞争","prompt":"<步骤:生成Go语言避免数据竞争相关面试问题>","category":"a8bj40:68y8eo","description":"Go语言避免数据竞争","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对Go语言避免数据竞争的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"myfpax","auto":false,"name":"操作系统的基本概念","prompt":"<步骤:生成操作系统的基本概念相关面试问题>","category":"a8bj40:03wuou","description":"操作系统的基本概念","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对操作系统的基本概念的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"yed3j3","auto":false,"name":"操作系统的虚拟内存","prompt":"<步骤:生成操作系统的虚拟内存相关面试问题>","category":"a8bj40:03wuou","description":"操作系统的虚拟内存","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对操作系统的虚拟内存的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ixwt9n","auto":false,"name":"操作系统的CPU调度算法","prompt":"<步骤:生成操作系统的CPU调度算法相关面试问题>","category":"a8bj40:03wuou","description":"操作系统的CPU调度算法","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对操作系统的CPU调度算法的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"rclxix","auto":false,"name":"操作系统的内存分页与段式管理","prompt":"<步骤:生成操作系统的内存分页与段式管理相关面试问题>","category":"a8bj40:03wuou","description":"操作系统的内存分页与段式管理","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对操作系统的内存分页与段式管理的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ior55n","auto":false,"name":"SQL与NoSQL数据库","prompt":"<步骤:生成SQL与NoSQL数据库相关面试问题>","category":"a8bj40:1mctx4","description":"SQL与NoSQL数据库","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对SQL与NoSQL数据库的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"8nqr3h","auto":false,"name":"数据库事务的ACID特性","prompt":"<步骤:生成数据库事务的ACID特性相关面试问题>","category":"a8bj40:1mctx4","description":"原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对数据库事务的ACID特性的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"4eab7n","auto":false,"name":"数据库的索引","prompt":"<步骤:生成数据库的索引相关面试问题>","category":"a8bj40:1mctx4","description":"数据库的索引","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对数据库的索引的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"ebbn3s","auto":false,"name":"数据库的锁机制","prompt":"<步骤:生成数据库的锁机制相关面试问题>","category":"a8bj40:1mctx4","description":"数据库的锁机制","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对数据库的锁机制的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"9011sl","auto":false,"name":"缓存的基本概念","prompt":"<步骤:生成缓存的基本概念相关面试问题>","category":"a8bj40:zxm7kk","description":"缓存的基本概念","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对缓存的基本概念的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"as6tzg","auto":false,"name":"缓存击穿、缓存雪崩","prompt":"<步骤:生成缓存击穿、缓存雪崩相关面试问题>","category":"a8bj40:zxm7kk","description":"缓存击穿、缓存雪崩","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对缓存击穿、缓存雪崩的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"x9aqds","auto":false,"name":"缓存的常见淘汰策略","prompt":"<步骤:生成缓存的常见淘汰策略相关面试问题>","category":"a8bj40:zxm7kk","description":"缓存的常见淘汰策略","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对缓存的常见淘汰策略的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"mf7vb7","auto":false,"name":"消息队列的基本概念","prompt":"<步骤:生成消息队列的基本概念相关面试问题>","category":"a8bj40:n4nrbs","description":"消息队列的基本概念","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对消息队列的基本概念的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"wedsad","auto":false,"name":"消息队列的幂等性","prompt":"<步骤:生成消息队列的幂等性相关面试问题>","category":"a8bj40:n4nrbs","description":"消息队列的幂等性","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对消息队列的幂等性的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"7lk29w","auto":false,"name":"洞察力","prompt":"<步骤:生成考察洞察力的面试问题>","category":"a8bj40:0t579o","description":"洞察力","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的洞察力(系统思维与战略思维),请设计3个深入考察洞察力的面试问题,参考以下几个示例问题:\n1、请谈一下你在专业领域是如何做研究的?你是如何收集信息和数据的?这些信息和数据你是如何处理和分析的?你最终想要达成的目标是什么?你是如何规划实现这个目标的?在你的研究过程中,哪些因素发生了变化?你是如何处理的?\n2、请分享一次你在做研究或做项目过程中,发现了一些与你预想的目标相悖的情况,你是如何处理的?\n3、你的毕业研究课题是怎么选择和设计的?你收集和分析了哪些信息?你觉得你的研究结果对于未来的技术发展最大的贡献是什么?\n请结合我在之前几轮对话中给你提供的候选人项目经历,在每个问题的开头指明所针对的项目。\n4、你是如何准备这次应聘的?你觉得华为的优势和劣势分别是什么?在准备过程中,你的关键发现是什么?有没有注意到什么风险和机遇?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"n49s1v","auto":false,"name":"影响力","prompt":"<步骤:生成考察影响力的面试问题>","category":"a8bj40:0t579o","description":"影响力","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的影响力(领导他人、影响他人决策、与人连接的能力),请设计3个深入考察影响力的面试问题,参考以下几个示例问题:\n1、请分享一个你在项目中,成功说服他人的例子,你和对方的分歧在哪里?对方是如何坚持自己的观点的?你是如何说服TA的?你感觉TA接受了你的哪些观点或意见?中间对方有没有不高兴?你是如何解决的?\n2、你有没有说服别人跟你一起做一件事或者一个项目的经历?你当时的想法是什么?你去找了哪些人跟你一起去做?分别怎么去说服他们的?在争取支持或配合的过程中,遇到了哪些困难?你是如何克服的?\n3、你有没有碰到过需要比较高级别的人(例如学校领导、合作单位领导、德高望重的教授)支持的情况?你是怎么争取到他们的支持的?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"cb0zu4","auto":false,"name":"合作性","prompt":"<步骤:生成考察合作性的面试问题>","category":"a8bj40:0t579o","description":"合作性","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的团队合作能力,请设计3个深入考察团队合作能力的面试问题,参考以下几个示例问题:\n1、你在项目中和团队其他人是如何配合的?你们是如何一起制定项目计划和分工的?和不熟悉的团队成员是如何沟通的?你为团队成员提供了哪些帮助?你从团队成员那里得到了哪些帮助?\n2、请分享团队其他成员强烈反对你的观点和行为的经历,当时是怎样的情况?你是如何处理的?\n3、请分享一次你放弃自己观点的经历,你是出于什么样的考虑放弃自己的观点的?最后达成了什么目的?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"p44axb","auto":false,"name":"学习能力","prompt":"<步骤:生成考察学习能力的面试问题>","category":"a8bj40:0t579o","description":"学习能力","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的学习能力(适应新环境新业务的快速学习能力),请设计3个深入考察学习能力的面试问题,参考以下几个示例问题:\n1、你碰到过的最难理解和掌握的技术点是什么?是哪方面让你觉得很难理解和掌握?你是怎么克服困难的?最后的学习效果如何?运用的效果如何?\n2、请举一个例子说明,你是如何在项目中收集资料的?在大量的资料里,你是如何撷取关键要素的?对矛盾信息或碎片信息你通常会怎样甄别和处理?最后的结论是否得到验证和使用的?\n3、过往的经历中,你有没有发现过其他人没有察觉到的问题?你是如何发现这个问题的?发现问题后你查阅了哪些资料或者做了哪些事情去研究这个问题?最后是怎么解决这个问题的?用到了哪些新的知识和技术?解决的效果如何?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"lnlu51","auto":false,"name":"开放性","prompt":"<步骤:生成考察开放性的面试问题>","category":"a8bj40:0t579o","description":"开放性","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的开放性(拥抱新观点、新概念),请设计3个深入考察开放性的面试问题,参考以下几个示例问题:\n1、谈谈你在项目中是如何做决定的?请举一个例子,你在做决定时吸纳了哪些新的技术、理念和观点?其他人的意见你是否有征询?你是如何解决其他人的意见纳入到你的决定中去的?其他人强烈反对或不理解你的决定时,你是如何处理的?\n2、谈谈你在项目中接触到的一个新技术、新观点或新概念,你是怎么注意到的?你为什么会感兴趣?这对你原有的想法和行为带来了什么冲击和改变?\n3、在项目或生活中,遇到事情的发展与你的计划不符的情况下,你会怎么做?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"eee9xg","auto":false,"name":"成就导向","prompt":"<步骤:生成考察成就导向的面试问题>","category":"a8bj40:0t579o","description":"成就导向","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的成就导向(有强烈的上进心和抱负,追求卓越,挑战自我),请设计3个深入考察成就导向的面试问题,参考以下几个示例问题:\n1、请描述一下你觉得做得最失败的一个项目?你在其中做了哪些努力去推动项目的成功?发现了哪些问题?如何解决这些问题的?有哪些问题是你觉得再努力都无法解决的?最终项目为什么失败?你有什么反思?下一步计划是什么?\n2、在达成项目目标的过程中,你经历的最大一次干扰是什么?你是如何取舍的?是在什么样的情况下,你选择放弃既定的目标?\n3、你在学校参加了哪些社团?你希望从参与这些社团的过程中获得什么?每一个社团你的参与程度如何?有没有哪个社团你报名了但因为种种原因又中途退出了?退出的原因是什么?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"7nupin","auto":false,"name":"坚韧性","prompt":"<步骤:生成考察坚韧性的面试问题>","category":"a8bj40:0t579o","description":"坚韧性","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人的坚韧性(在持续的压力和挫折下能够始终坚韧不拔),请设计3个深入考察坚韧性的面试问题,参考以下几个示例问题:\n1、你做过的最艰难的、感觉做不下去的项目或实验是什么?难点是什么?那段时间你压力大吗?你是如何排解压力的?后来是怎么解决问题的?\n2、请举一个失败的项目或过往实习经历中遭遇的失败任务?你做了哪些努力?最后还是失败的原因是什么?\n3、有没有一件当时你觉得希望不大,但你坚信通过努力能完成的事情,并且最终取得了成功的例子?为什么当时希望不大,你还坚持?你回顾这段经历的收获是什么?你觉得最终成功的关键是什么?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"uihj2i","auto":false,"name":"价值观与文化适应性","prompt":"<步骤:生成考察价值观与文化适应性的面试问题>","category":"a8bj40:0t579o","description":"价值观与文化适应性","promptVarData":[],"promptTemplate":"我想让你担任软件开发岗位的综合面试官,专门考察候选人对华为核心价值观(以客户为中心,以奋斗者为本,长期艰苦奋斗)的认同感和对华为文化的适应性,请设计3个深入考察价值观与文化适应性的面试问题,参考以下几个示例问题:\n1、你有没有处理过超出你的职责范围的事情的经历?你觉得这件事应该是你去处理的吗?你当时为什么会去处理这件事情?你是怎么处理的?结果如何?\n2、请分享一次你觉得最委屈的经历,当时是什么情况?你为何觉得委屈?后来误会消除了吗?是怎么消除的?你觉得如何防止以后再出现这样的误会?\n3、请你谈一下你在过去遇到过的最愤怒的一件事?你觉得这件事是什么触碰到了你的底线?你做了哪些调整去克服它或者解决它?\n4、 你对华为“奋斗者”、“长期艰苦奋斗”、“坚持自我批判”的价值观是如何理解的?\n请结合我在之前几轮对话中给你提供的候选人简历信息。"},{"id":"u25dgh","auto":false,"name":"OSI网络模型","prompt":"<步骤:生成OSI网络模型相关面试问题>","category":"a8bj40:gpk5lc","description":"Open Systems Interconnection","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对OSI(Open Systems Interconnection)网络模型的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"hi86t0","auto":false,"name":"TCP网络协议","prompt":"<步骤:生成TCP网络协议相关面试问题>","category":"a8bj40:gpk5lc","description":"Transmission Control Protocol","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对TCP(Transmission Control Protocol)网络协议的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"n2wcy4","auto":false,"name":"UDP网络协议","prompt":"<步骤:生成UDP网络协议相关面试问题>","category":"a8bj40:gpk5lc","description":"User Datagram Protocol","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对UDP(User Datagram Protocol)网络协议的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"id":"wo4dg5","auto":false,"name":"DNS域名系统","prompt":"<步骤:生成DNS域名系统相关面试问题>","category":"a8bj40:gpk5lc","description":"Domain Name System","promptVarData":[],"promptTemplate":"我想让你担任通用软件开发工程师面试官,请帮我设计2个常见的技术面试问题,考察候选人对DNS(Domain Name System)域名系统的理解,并且提供简单的参考答案(每个问题的答案不超过150个token),不需要提供代码示例"},{"name":"总结综合面试意见","description":"一键总结成长基础、成长潜力、成长渴望、价值观与文化适应性评价以及综合评价意见","prompt":"<步骤:总结综合面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的善于归纳总结的面试官,你的任务是对用户的面试笔记进行优化总结,整理成专业的面试意见,具体任务说明如下:\n总结面试笔记的过程如下:\n1. 分析用户的面试笔记\n2. 将分析结果与成长基础、成长潜力、成长渴望、价值观与文化适应性4个维度进行关联总结,4个维度的定义如下\n * 成长基础主要从洞察力、合作性、影响力方面评价候选人能不能胜任岗位职责,比如候选人在过往经历中体现出来的系统和战略思考能力,对周边和社会上的影响力,团队协作能力等特征是这个维度的重点总结内容\n * 成长潜力主要从学习能力、开放性方面评价候选人能不能持续胜任岗位职责,比如候选人在过往经历中体现出来的在新环境或者学校中快速学习的能力,主动积极拥抱新技术新概念等特征是这个维度的重点总结内容\n * 成长渴望主要从责任心、上进心、自我挑战、坚韧性方面评价候选人愿不愿承担更多责任,比如候选人在过往经历中体现出来的勇于承担工作和学习任务,勇于挑战完成不可能完成的任务等特征是这个维度的重点总结内容\n * 价值观与文化适应性主要从华为核心价值观方面评价候选人对华为文化的适应性,比如候选人是否了解奋斗者文化和成就客户的导向,渴望成为华为一份子等特征是这个维度的重点总结内容\n3. 将关联总结的内容以每个维度为标题进行输出\n4. 根据4个维度的总结,输出汇总的综合评价意见\n\n你必须遵守如下规则:\n1. 以每个维度作为标题进行总结,字数最多不超过200个字\n2. 综合评价意见必须充分评价候选人在各个维度的能力,字数最多不超过500个字\n3. 从某个维度进行总结时,不必机械地点明每个维度的定义,而是将候选人的特点自然地融入到整体评价中,保持总结内容精练、简洁\n4. 不要编造信息或者照抄下面的例子\n\n如下是一个总结过程的例子,请参考该例子进行思考:\n\n面试笔记:\n候选人为电子科技大学计算机科学硕士,在校期间成绩优异,2次获得奥林匹克数学竞赛金牌,发表3篇高质量论文;本科为通信工程专业,通过自学和选修计算机相关课程,考取计算机专业研究生;后于微软从事后端服务开发;在面试过程中,候选人表现出了很强的上进心和吃苦耐劳的精神,目标是成为全栈技术专家;候选人有了解过华为文化,理解以客户为中心的导向,认可华为以奋斗者为本的利益分享机制。建议录用。\n\n输出的面试总结:\n经过对面试笔记进行分析,关于候选人的成长基础、成长潜力、成长渴望、价值观与文化适应性和综合评价意见总结如下:\n**成长基础**:\n候选人学习能力表现得非常突出,掌握了扎实的计算机基础知识;候选人对新兴技术保持高度敏感,在阿里巴巴工作期间,引入了Docker容器化技术以简化开发和部署流程;在微软项目中,候选人发现了在高并发情况下部分API接口存在响应迟缓的问题,提出了基于微服务架构的重构方案,有效提高了系统的扩展性和处理能力。\n**成长潜力**:\n候选人在本科阶段从通信工程专业转向计算机领域,证明候选人具备灵活的知识迁移能力;在微软任职后,候选人快速掌握了C#、Azure云平台等多种技术栈,并成功推动了Windows 365 Web终端项目的多个新特性的交付,展示了快速适应新技术和工具的能力。候选人在工作中不仅注重技术深度的积累,还主动承担了跨部门的技术合作工作。\n**成长渴望**:\n在硕士阶段,候选人多次主动参与课外科研项目,展示了自我驱动的学习能力。在阿里巴巴期间,候选人主动识别并解决了系统性能瓶颈,优化了后端服务的响应速度和资源利用率,展现了在面对复杂问题时勇于突破的品质。在微软的项目推进过程中,候选人遇到过多次技术障碍和进度延误,但他凭借顽强的毅力克服了困难,最终高质量地交付了项目。\n**价值观与文化适应性**:\n候选人了解华为的企业文化,特别是以客户为中心和以奋斗者为本的核心价值观。在面试中表达了对华为文化的高度认同,展示出愿意成为华为一分子的积极态度。\n**综合评价意见**:\n候选人在电子科技大学完成了计算机科学硕士学业,期间成绩优异,发表了3篇高质量论文,两次获得奥林匹克数学竞赛金牌,展现了扎实的学术基础和卓越的逻辑思维能力。从通信工程专业跨入计算机领域并成功考取研究生的过程,充分体现了候选人的跨学科背景和灵活的知识迁移能力。在阿里巴巴工作期间,候选人引入Docker容器化技术,简化了开发和部署流程,显著提高了团队效率。在微软担任MSFT Intune后端服务开发时,候选人迅速掌握了C#、JavaScript、Python和Azure等技术,成功推动了多个Windows 365 Web终端新特性的交付,并通过微服务架构重构解决了高并发下的性能问题。候选人在团队协作方面表现出色,在微软的工作中主动承担跨部门合作任务,推动技术与产品的融合。他具备强烈的上进心和吃苦耐劳的精神,面对复杂问题时勇于突破,不断追求卓越。在面试过程中,候选人表达了对华为企业文化的高度认同,特别是以客户为中心和以奋斗者为本的核心价值观,展现出愿意成为华为一分子的积极态度。基于以上综合表现,建议录用。\n\n\n用户输入的面试笔记:\n{{面试意见}}\n\n输出的面试总结:","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"ns2o09"},{"name":"总结成长基础面试意见","description":"总结成长基础面试意见","prompt":"<步骤:总结成长基础面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 洞察力\n问题分析能力:面试者是否能迅速理解复杂问题,识别关键因素,制定解决问题的策略。\n趋势预测能力:面试者能否基于现有信息预测行业或项目可能的发展趋势,为决策提供前瞻性视角。\n细节敏锐度:面试者在处理任务或项目时,是否能注意到关键细节,避免潜在的错误或遗漏。\n2. 影响力\n沟通表达能力:面试者是否能够清晰、有说服力地表达自己的观点,使他人理解并接受。\n领导力:在团队中,面试者能否通过自己的行动和决策带动团队成员,推动项目进展。\n激励他人:面试者是否能够激发团队成员的积极性,提升团队整体的工作热情和效率。\n3. 合作性\n团队协作能力:面试者是否愿意与团队成员分享信息,共同解决问题。\n适应性:在团队中,面试者能否快速适应不同的工作风格和环境,与各种性格的成员有效合作。\n冲突解决能力:面对团队内部的分歧或冲突,面试者是否能采取积极有效的措施进行调解,维持团队和谐。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"hkrwmp"},{"name":"总结成长潜力面试意见","description":"总结成长潜力面试意见","prompt":"<步骤:总结成长潜力面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 学习能力\n主动学习态度:面试者是否展现出对新知识和技能的主动寻求和掌握态度,是否能够意识到自我提升的需求,并采取积极措施进行自我教育。\n快速适应能力:面试者是否能够描述他们如何在有限的时间内掌握新技能、适应新项目或工作流程,以及在适应过程中遇到挑战时的应对策略。\n2. 开放性\n接受新观念:面试者是否愿意接受和考虑不同的观点、方法或文化,以及在面对新事物时的态度。\n适应变化:面试者能否描述他们如何处理工作中的突发变化,如项目方向调整、团队重组等,以及在变化中保持积极态度和工作效率。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"bxb161"},{"name":"总结成长渴望面试意见","description":"总结成长渴望面试意见","prompt":"<步骤:总结成长渴望面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 成就导向性\n目标设定能力:面试者是否能够设定清晰、具体、有挑战性的目标,并为之制定计划。\n结果导向:面试者是否专注于结果,能够有效地解决问题,克服障碍,以达成目标。\n自我激励:面试者是否能自我驱动,即使在缺乏外部激励或监督的情况下仍能保持高效和积极。\n持续改进:面试者是否具有持续学习和改进的意愿,不断自我提升以达到更高标准。\n2. 坚韧性\n逆境适应性:面试者面对逆境时是否能够保持冷静,寻找解决方案,而不是轻易放弃。\n情绪稳定性:面试者在压力下是否能够保持情绪稳定和专业性的能力。\n外部支持利用:面试者是否能够有效地寻求和利用外部资源,如团队成员、导师或工具,以克服挑战。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"siu3av"},{"name":"总结价值观与文化适应性面试意见","description":"总结价值观与文化适应性面试意见","prompt":"<步骤:总结价值观与文化适应性面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 对华为核心价值观的认同\n以客户为中心:面试者是否把客户的需求放在首位,是否愿意为了满足客户需求而努力工作,以及是否具备与客户建立良好关系的能力。\n以奋斗者为本:面试者是否展现出强烈的自我驱动力和对工作的投入度。\n坚持长期艰苦奋斗:面试者是否具备吃苦耐劳的精神,是否能够在困难面前不放弃,以及是否愿意为了目标付出额外努力。\n自我批判:面试者是否能够坦诚地反思自己的不足,并愿意主动寻求改进。\n2. 对华为文化的适应\n适应变化:面试者是否能够快速适应工作环境的变化,是否积极准备华为看重的技术技能。\n个人意愿:面试者是否了解和认可华为的文化,是否有强烈的加入华为公司的意愿。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"vfz3z6"},{"name":"总结专业知识面试意见","description":"总结专业知识面试意见","prompt":"<步骤:总结专业知识面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 基础知识掌握情况:考察候选人对该领域核心概念、理论基础和常见工具的理解。\n2. 深度理解与领域前沿:考察候选人是否对该领域有深入理解,是否掌握了领域内的最新技术、趋势或前沿研究。\n3. 应用与实践能力:考察候选人在该领域中的实际项目经验,以及如何将理论应用于实践。\n4. 领域工具和框架的使用:考察候选人是否熟练掌握该领域常用的工具和框架,以及在不同场景下如何选择合适的工具。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"6ioy0f"},{"name":"总结关键能力面试意见","description":"总结关键能力面试意见","prompt":"<步骤:总结关键能力面试意见>\n<面试意见>:","promptTemplate":"你是一个专业的面试官,非常善于总结面试意见,请帮助其他面试官总结他们的面试意见,将意见提炼和润色成丰富的面试结论总结\n请从下面几个维度进行提炼总结:\n1. 逻辑思维能力:考察候选人在解决问题时的思路是否清晰、条理性如何,是否能够有效地分析和分解复杂问题。\n2. 编码能力:考察候选人编写代码的能力,包括代码的质量、效率、可读性以及解决实际问题的能力。\n如下是用户的面试意见:\n{{面试意见}}","promptVarData":[],"category":"a8bj40:lcp0bt","auto":false,"id":"dtwrcs"}],"showInspiration":true}', 'input', 'chat', '创意灵感', 7, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('ddf6e55b8bf44eb1a10001838906b06c', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'recommend', 'object', '{"showRecommend":false,"list":[]}', 'input', 'chat', '猜你想问', 6, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('1aa4a8c3178c473c8fdabd37af5f0002', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'multimodal', 'object', '{"useMultimodal":true,"maxUploadFilesNum":1,"autoChatOnUpload":true}', 'input', 'chat', '多模态', 8, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('64a7ca8b22414bf28abb49a75e0f1f1c', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'workflow', 'String', 'null', 'none', 'null', '工作流编排', 0, 0, 'fd8166b5005e4d66a77d318f3b1dd5e5') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."flow_definition" ("definition_id", "meta_id", "name", "tenant", "version", "status", "graph", "created_by", "created_at") VALUES ('0e58b12cdafd476a81d4da70dce8175a', '24f72de428124eb19fd12db36ebcfd34', '24f72de428124eb19fd12db36ebcfd34', '31f20efc7e0848deab6a6bc10fc3021e', '1.0.0', 'active', '{"name": "24f72de428124eb19fd12db36ebcfd34", "nodes": [{"name": "开始", "type": "startNodeStart", "metaId": "jade6qm5eg", "runnable": true, "inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": true}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}], "triggerMode": "auto"}, {"name": "结束", "type": "endNodeEnd", "metaId": "jadesoux5i", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "54dab89c-5693-4082-baa7-12c648d812f7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "ffad80c2-3f60-4d57-93b2-c2362a5dab9c", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output", "errorMessage"], "editable": true, "isRequired": true, "description": "", "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "c4469c16-88a7-4575-b339-9a06e3305f3b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"name": "AI简历解析插件", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "fileUrl"}, {"name": "instanceId"}], "return": {"type": "object"}, "uniqueName": "8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "fileUrl_edfec05d-9812-429f-ae55-24df5d2e1216", "from": "Reference", "name": "fileUrl", "type": "String", "value": ["output", "fileUrl"], "isRequired": true, "description": "简历文件URL", "referenceId": "45fafbfd-5966-47ba-91af-baebe09bdab1", "referenceKey": "fileUrl", "referenceNode": "jadeemu770"}, {"id": "instanceId_24e98795-b3aa-4a0f-b78f-d4b059a59768", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "description": "实例ID", "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_5315b59e-1d27-4278-a06e-f89c7627782a", "name": "output", "type": "Object", "value": [{"id": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "name": "isFileHandled", "type": "Boolean", "value": "Boolean"}, {"id": "9e3844fd-864d-4504-8f2b-dba7e919135b", "name": "cvAnalyzerPrompt", "type": "String", "value": "String"}, {"id": "50617d76-27e1-49aa-a653-1947168d8937", "name": "errorMessage", "type": "String", "value": "String"}]}]}}}, "metaId": "jadelk78r1", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "代码", "type": "codeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "98a0c373-6b03-4fd0-bc1d-101c9f1b1fd2", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "a15b25db-133c-41b4-a329-4b9bd257ad3e", "from": "Reference", "name": "fileUrls", "type": "Array", "value": ["fileUrls"], "referenceId": "fileUrls", "referenceKey": "fileUrls", "referenceNode": "_systemEnv"}]}, {"id": "6a95280d-156a-45b2-b235-9ca2b9015d2f", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n ret: Output = {\n \"fileUrl\": args[''fileUrls''][0] if args[''fileUrls''] else \"\"\n }\n return ret", "language": "python"}, {"id": "b770fd0c-d5ae-4f2a-8129-52839becb775", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "1e2ee2b3-0106-44ac-89db-1684220c5ad4", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "object", "properties": {"fileUrl": {"type": "string", "description": ""}}, "description": ""}}}}], "outputParams": [{"id": "ebdfcbcb-4dbd-47dc-8bd6-a5752edb2487", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "45fafbfd-5966-47ba-91af-baebe09bdab1", "from": "Input", "name": "fileUrl", "type": "String", "value": "", "description": ""}]}]}}}, "metaId": "jadeemu770", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadeemu770", "from": "jade6qm5eg", "type": "jadeEvent", "metaId": "jadexi4d2i", "runnable": true, "fromConnector": "E"}, {"to": "jadelk78r1", "from": "jadeemu770", "type": "jadeEvent", "metaId": "jade3ccnk1", "runnable": true, "fromConnector": "E"}, {"name": "条件", "type": "conditionNodeCondition", "metaId": "jade4h5wks", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "70695434-91fb-4493-87a4-e681e99d985e", "type": "if", "runnable": true, "conditions": [{"id": "a5aef4c1-af83-475e-9805-2dbff919de10", "value": [{"id": "d9df8a7c-4cc6-4147-b73e-a9db324b1bde", "from": "Reference", "name": "left", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "4a915ffb-6082-4fca-a622-71d875b34412", "from": "Input", "name": "right", "type": "Boolean", "value": true, "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}, {"id": "8e60cd88-0799-4f12-9fcb-e20ccbc7ecd0", "value": [{"id": "f2552657-58f6-4def-a3c7-cab73575e5b0", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "9c8f2868-6722-4ed6-b388-3215bfbcacca", "from": "Input", "name": "right", "type": "String", "value": "请确认 上传的文件是一份简历", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "contains"}], "conditionRelation": "or"}, {"id": "32f55d87-ebf2-464f-9f78-ecd62e36f2dc", "type": "if", "runnable": true, "conditions": [{"id": "5e19d158-03a3-4315-b45c-ff2a0b384db0", "value": [{"id": "847d2cf6-5201-4628-a183-ca9ba62745f2", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "9bab7914-26b4-487e-b609-d9776b13e608", "from": "Reference", "name": "right", "type": "", "value": [], "referenceNode": ""}], "condition": "is not empty string"}], "conditionRelation": "and"}, {"id": "8650733f-4401-4155-8633-048a5726e6fe", "type": "else", "runnable": true, "conditions": [{"id": "4e7f3b9e-f609-4178-bd27-459d0683cac9", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"name": "条件_1", "type": "conditionNodeCondition", "metaId": "jade2es9ti", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "e2cefcda-b6e2-4713-ac74-f2266a10bb19", "type": "if", "runnable": true, "conditions": [{"id": "2f466536-4151-4013-b008-bf5af8a37edb", "value": [{"id": "c8601563-bbeb-497b-91f4-a1f81e08ad32", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "3543673f-ee62-4e18-82ef-c9e0ecc2e9a2", "from": "Reference", "name": "right", "type": "", "value": "", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "is empty string"}], "conditionRelation": "and"}, {"id": "99f4f4da-c3fd-4296-9b83-b0ff5a3ef595", "type": "else", "runnable": true, "conditions": [{"id": "d9733cad-0646-41f9-be5b-f39c9ac4d220", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"to": "jade2es9ti", "from": "jade4h5wks", "type": "jadeEvent", "metaId": "jade9gqfjk", "runnable": true, "fromConnector": "dynamic-1|70695434-91fb-4493-87a4-e681e99d985e"}, {"name": "智能表单", "task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/e85bd769-0212-4305-b56b-01e77faa14ff/form.png", "taskId": "115b557320ac41e1b449b8107aaa1781", "formName": "面试助手3", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "isCvFile_337878e3-7720-4ece-bf92-40d4a402625c", "from": "Input", "name": "isCvFile", "type": "String", "value": "null", "isRequired": true}, {"id": "instanceId_59f311b0-98b3-4292-8887-aba60c48e3e4", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_e5ae0061-8304-4327-958a-a6cd0f80100d", "name": "output", "type": "Object", "value": [{"id": "ab8bf709-7374-4f33-90d6-72a64254f07e", "name": "isCvFile", "type": "String", "value": "String"}]}]}}}, "type": "manualCheckNodeState", "metaId": "jade758stt", "runnable": true, "triggerMode": "manual"}, {"to": "jade758stt", "from": "jade2es9ti", "type": "jadeEvent", "metaId": "jadepke1tr", "runnable": true, "fromConnector": "dynamic-999"}, {"name": "AI提示词拼接工具", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "appId"}, {"name": "instanceId"}, {"name": "input"}], "return": {"type": "string"}, "uniqueName": "bdc009dc-969e-4839-b5d7-e9599009d50d"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "appId_0293cca7-c1ea-45b0-8ca8-b37a55f99303", "from": "Reference", "name": "appId", "type": "String", "value": ["appId"], "isRequired": true, "description": "应用ID", "referenceId": "appId", "referenceKey": "appId", "referenceNode": "_systemEnv"}, {"id": "instanceId_93f70f4f-e2b6-4518-a53e-af0ccdc99d2c", "from": "Reference", "name": "instanceId", "type": "String", "value": ["instanceId"], "isRequired": true, "description": "实例ID", "referenceId": "instanceId", "referenceKey": "instanceId", "referenceNode": "_systemEnv"}, {"id": "input_4ac2267a-fa7a-462c-ad8d-7b2936e0fac3", "from": "Reference", "name": "input", "type": "String", "value": ["Question"], "isRequired": true, "description": "用户输入", "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}], "outputParams": [{"id": "output_65d89d72-205f-4bf6-bb6e-a11bebaa8497", "name": "output", "type": "String", "value": []}]}}}, "metaId": "jadey32p7b", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "大模型", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "009c2461-9c7e-4f85-bf43-ad887acd6a8a", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f78bde02-c766-4be5-bab6-70d2f6e6de6c", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "af11f474-c1ae-4e07-ad7e-92f378a9d19d", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "5fe88d69-b7bb-4ff3-abb1-a2fbeb4ec240", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "b23e18ed-c36f-4692-9917-4971f9c1659b", "from": "Input", "name": "temperature", "type": "Number", "value": 0.7}, {"id": "cd4194cd-db96-4164-8a45-9ca4ea30cd81", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "184c22f2-c6f5-4bb6-81ca-d214eb266b35", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"}, {"id": "30eacd51-29f6-4a90-8e17-4e4ffe1ddcc6", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "155a2932-e37e-4c18-b14b-6d44b2a43ae4", "from": "Reference", "name": "isFileHandled", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "950c301c-81ba-41a6-bda5-aa37360e92a5", "from": "Reference", "name": "cvAnalyzerprompt", "type": "String", "value": ["output", "cvAnalyzerPrompt"], "referenceId": "9e3844fd-864d-4504-8f2b-dba7e919135b", "referenceKey": "cvAnalyzerPrompt", "referenceNode": "jadelk78r1"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "a7b687f8-e993-4e5a-9a10-bb6df8df25d9", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "6ec07829-b51c-4397-8670-59659e43036f", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "3733d6f2-0ea2-4dab-9f5b-423352d4af0b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "091115ca-4fd9-4265-8280-5d70cfcdcfca", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "b0a3bdd3-7a3c-4657-8288-4cf8a98d33a0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "51b4e7bb-19f5-4477-a2df-b974f8c97ba7", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jade39q3lc", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jade39q3lc", "from": "jade2es9ti", "type": "jadeEvent", "metaId": "jadeqraasb", "runnable": true, "fromConnector": "dynamic-0|e2cefcda-b6e2-4713-ac74-f2266a10bb19"}, {"name": "条件_2", "type": "conditionNodeCondition", "metaId": "jadegqixt5", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "bb4f8032-19cc-445b-a2ff-5cda77ae59e3", "type": "if", "runnable": true, "conditions": [{"id": "8085598f-b128-4749-940c-b4bda2619cba", "value": [{"id": "9a656755-91fe-47b6-a54c-4b2be274978a", "from": "Reference", "name": "left", "type": "String", "value": ["output", "errorMessage"], "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}, {"id": "37c01598-663a-476c-9a99-202b73131d4b", "from": "Input", "name": "right", "type": "String", "value": "", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "is not empty string"}], "conditionRelation": "and"}, {"id": "319689dd-4250-4f0d-9c1b-d071013ac712", "type": "else", "runnable": true, "conditions": [{"id": "737ba1f5-dbb5-401c-9bf5-e18597f02123", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"name": "大模型_1", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "b8519f34-6d5f-42cd-b949-438f44d3210e", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "8c18f95c-1585-4e11-be07-de595ad13a92", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "8fd16d6c-b043-46dd-8b55-f0b8dee2615c", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "bd6f983c-f8e8-4538-ac66-60024d09c0a1", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "0263e85d-6125-4e27-ab7a-d2bc351cf414", "from": "Input", "name": "temperature", "type": "Number", "value": 0.6}, {"id": "1cf0a04f-2ca7-41fd-bec4-f9a1dd016c7f", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "a1335ac5-b02b-47c5-91d4-ecb945833ef3", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"}, {"id": "d6309a8b-d15b-4380-b6c6-ef000a9e5201", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "25e56c39-1668-4428-8174-116b1a906bbd", "from": "Reference", "name": "isFileHandled", "type": "Boolean", "value": ["output", "isFileHandled"], "referenceId": "bf6d9282-2bf5-4826-9be1-10756ed6d728", "referenceKey": "isFileHandled", "referenceNode": "jadelk78r1"}, {"id": "498b5c92-92ef-4ebf-9346-f2847fb88a3a", "from": "Reference", "name": "cvAnalyzerprompt", "type": "String", "value": ["output", "cvAnalyzerPrompt"], "referenceId": "9e3844fd-864d-4504-8f2b-dba7e919135b", "referenceKey": "cvAnalyzerPrompt", "referenceNode": "jadelk78r1"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "e1ad1e33-6a7c-451d-ba5a-158595cb2af8", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "3c3f111b-85ec-42c0-af8e-f3352518a29b", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "1cd6884e-c3e3-483b-baeb-d592c4f58ebf", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "7d01bbd3-df9c-4884-8d02-54b195096857", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "9e2fb06e-0276-48c8-8606-c36a31cf93b0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "3eab6484-fa97-4e06-a0cc-263d3ed5274a", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "d438bc29-a627-44c2-bd57-4bf64eceba5a", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadem1n9u5", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadem1n9u5", "from": "jadegqixt5", "type": "jadeEvent", "metaId": "jadee79arc", "runnable": true, "fromConnector": "dynamic-0|bb4f8032-19cc-445b-a2ff-5cda77ae59e3"}, {"name": "大模型_2", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "1d11a986-7dc9-4ba3-84d8-3f15810ae587", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "4ebabb28-8981-46d6-a458-953082c01f9a", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "5af05d34-4b6e-435f-872c-96fa990a1476", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "ea9b2985-96e6-480c-b065-c7cce122b57a", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "32801848-b75f-4ac0-b050-f3dfbd57e3ad", "from": "Input", "name": "temperature", "type": "Number", "value": 0.7}, {"id": "1891af57-83ff-458d-ac70-d35a7af89705", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "e9e29b94-85e2-47c8-84d2-055a77660b88", "from": "Input", "name": "template", "type": "String", "value": "{{query}}"}, {"id": "29e702cd-b721-442a-9893-81298cfa2953", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "c34fda11-8f17-4c85-96aa-35c5bb311e2a", "from": "Reference", "name": "query", "type": "String", "value": ["output"], "referenceId": "output_65d89d72-205f-4bf6-bb6e-a11bebaa8497", "referenceKey": "output", "referenceNode": "jadey32p7b"}]}]}, {"id": "dded6123-d168-44b8-8779-738d61af4859", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "2b06515f-c9d5-41bd-bfbe-e1b1382d6ec1", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "0b5e2451-ed5f-46b2-8600-8893963296e4", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "acb67126-1a1b-4430-946e-30e768712333", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "f7d670ab-1cad-4028-a9c8-c84a5a170ac1", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "29ed2025-1eaa-462a-a37a-e05677eef8c4", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "451cf45e-6250-4f08-9d73-51d94433d749", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "dab1caf6-f171-4f6d-beb7-97c913fa1637", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadedzi58q", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadedzi58q", "from": "jadey32p7b", "type": "jadeEvent", "metaId": "jadeqdcs4x", "runnable": true, "fromConnector": "E"}, {"name": "结束_2", "type": "endNodeEnd", "metaId": "jadefw1zfk", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "b6d32b12-26db-4a72-974b-7e197326e653", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "5c0e817c-8851-4350-9c5b-0ac5792a4e71", "from": "Reference", "name": "output", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "451cf45e-6250-4f08-9d73-51d94433d749", "referenceKey": "llmOutput", "referenceNode": "jadedzi58q"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "5e7a8388-8193-4f2d-a44a-094ab6fc576b", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadefw1zfk", "from": "jadedzi58q", "type": "jadeEvent", "metaId": "jadecvs6at", "runnable": true, "fromConnector": "E"}, {"name": "结束_4", "type": "endNodeEnd", "metaId": "jadexn70e2", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "5268f248-07c8-4871-8732-e007316214fc", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "1434e18b-bd1b-4286-bc2f-cfb2cb1e9c1e", "from": "Reference", "name": "output", "type": "String", "value": ["output", "errorMessage"], "editable": true, "isRequired": true, "description": "", "referenceId": "50617d76-27e1-49aa-a653-1947168d8937", "referenceKey": "errorMessage", "referenceNode": "jadelk78r1"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "e0c88e2d-1373-4606-8aaf-2d8ff330a27f", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadexn70e2", "from": "jadegqixt5", "type": "jadeEvent", "metaId": "jade2u3je1", "runnable": true, "fromConnector": "dynamic-999"}, {"to": "jadegqixt5", "from": "jade758stt", "type": "jadeEvent", "metaId": "jadet1dvap", "runnable": true, "fromConnector": "E"}, {"name": "文本提取", "type": "textExtractionNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "正在生成推荐面试问题...", "inputParams": [{"id": "extractParam_e7356380-7e19-4def-a1b5-3f7fb62c06af", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_da536c71-b299-40f0-8bca-720364fe20a0", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "3eab6484-fa97-4e06-a0cc-263d3ed5274a", "referenceKey": "llmOutput", "referenceNode": "jadem1n9u5"}, {"id": "desc_ddf67a20-776a-4c29-b741-9b5ed31a5104", "from": "Input", "name": "desc", "type": "String", "value": ""}, {"id": "outputSchema_1d2b703c-237b-40c9-9286-0d427c2b7312", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"}, {"id": "0d0a1d90-49b7-4bc6-af15-4c1c2905dadd", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "928af3d3-8671-4d93-a091-fd1662b74474", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "2ee47e96-94a7-4ee4-b616-36201338a7e4", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_236f8466-2e34-4b41-a199-a8d2843261ac", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_88d92098-2b56-4757-9fb6-08f3628e19ce", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_91b53de6-83c0-44eb-bf92-2585268d7526", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_f77ab974-f9c6-4ad3-9bcd-818fad8d96e2", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_7894b9b6-55e0-4b6e-acf8-d430e396d00c", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_a59975ed-71a1-4647-83b1-20c3aefe44f1", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_643f02b9-d709-4fc9-a04b-f9e590d96786", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "28eac6e6-2877-426a-a22c-5e425bfd2b1e", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "fa30b424-be71-443d-b18d-fbd32144e049", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "c19b4740-ce95-4483-b789-47286a19096b", "from": "Input", "name": "questions", "type": "Array", "value": "", "description": "针对简历的问题列表"}]}, {"id": "success_67bf7577-82ac-47b8-b8fa-7d7ebc53513a", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "metaId": "jade9puqh3", "runnable": true, "stageDesc": "正在生成推荐面试问题...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, {"to": "jade9puqh3", "from": "jadem1n9u5", "type": "jadeEvent", "metaId": "jade79wnvk", "runnable": true, "fromConnector": "E"}, {"to": "jadesoux5i", "from": "jade4h5wks", "type": "jadeEvent", "metaId": "jadekuuju4", "runnable": true, "fromConnector": "dynamic-1|32f55d87-ebf2-464f-9f78-ecd62e36f2dc"}, {"to": "jadey32p7b", "from": "jade4h5wks", "type": "jadeEvent", "metaId": "jade1o7wna", "runnable": true, "fromConnector": "dynamic-999"}, {"name": "智能表单_1", "task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png", "taskId": "d496c444a3174beabbcec5441aed40e2", "formName": "面试评价表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "questions_7e08f8cb-47ae-47df-b1a7-aaf62d7be03f", "from": "Reference", "name": "questions", "type": "Array", "value": ["output", "extractedParams", "questions"], "isRequired": true, "referenceId": "c19b4740-ce95-4483-b789-47286a19096b", "referenceKey": "questions", "referenceNode": "jade9puqh3"}], "outputParams": [{"id": "output_f00ac42b-c893-479b-b0ee-5cbe996c885a", "name": "output", "type": "Object", "value": [{"id": "27aa7c57-e31e-482a-98f0-bdbcc21a6138", "name": "qeMap", "type": "Array", "value": "Array"}]}]}}}, "type": "manualCheckNodeState", "metaId": "jademrtwtm", "runnable": true, "triggerMode": "manual"}, {"to": "jademrtwtm", "from": "jade9puqh3", "type": "jadeEvent", "metaId": "jadem1n5oj", "runnable": true, "fromConnector": "E"}, {"name": "大模型_3", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "fcd5702a-23f8-482e-a193-945676ca99ef", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "3a0ce6b3-8e98-4045-9323-957415084fba", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "35594014-3ca3-46b7-ac71-acd76b443952", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f3d8a723-81d0-48e2-b671-224caf7f0f17", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "1bf48ee9-ac82-4df5-be74-df96958f3f05", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "eb7a528c-c012-4374-bbf0-4a88e4727334", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "aeb114f8-9032-4be6-b9f2-a0301fd5cf05", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"}, {"id": "c4b818ba-1f8a-4f18-b196-c31bba136185", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "c709baca-8d63-4dd4-bced-e2762386a86a", "from": "Reference", "name": "qeMap", "type": "Array", "value": ["output", "qeMap"], "referenceId": "27aa7c57-e31e-482a-98f0-bdbcc21a6138", "referenceKey": "qeMap", "referenceNode": "jademrtwtm"}]}]}, {"id": "59d76a4e-1fd8-4950-9f84-6ebc17b7553c", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "a5b51a1c-68d7-4d8b-8791-9f788cd52f22", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "e1322719-1397-4dc8-a21e-674e1fb0eec0", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "892cf7e4-85c2-428d-b45b-0112a5978bd3", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "417ec551-d27e-49bc-b8ee-002a4bbd5b48", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "58e76471-8fb4-456d-9415-88192b008bd0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "23891b11-11a1-4915-80d1-53c515335dd5", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "c6b85478-009a-4ade-8bc2-a288e226e6db", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadezaa7cy", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadezaa7cy", "from": "jademrtwtm", "type": "jadeEvent", "metaId": "jadeeeqmda", "runnable": true, "fromConnector": "E"}, {"name": "结束_3", "type": "endNodeEnd", "metaId": "jadeohika6", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "73bad02b-26cb-417a-b1e0-85de07f398fa", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "aa9bc2e9-6dbf-4a6e-8d05-772f148da36e", "from": "Input", "name": "output", "type": "String", "value": "面试完成", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "519710b6-f147-4cdb-98e7-b60aa0600be5", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadeohika6", "from": "jadezaa7cy", "type": "jadeEvent", "metaId": "jadey8ppgc", "runnable": true, "fromConnector": "E"}, {"name": "文本提取_1", "type": "textExtractionNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "正在生成推荐面试问题...", "inputParams": [{"id": "c5fcdff0-2822-49c1-805d-242d38acf952", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "0fc3e0ac-307a-4c9c-8446-da801ca2a86e", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2", "referenceKey": "llmOutput", "referenceNode": "jade39q3lc"}, {"id": "bb3f946b-a504-44c9-a9e6-7271052ca40d", "from": "Input", "name": "desc", "type": "String", "value": ""}, {"id": "e99d0bd6-62f8-4f1b-80e9-72f7a2a19687", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"}, {"id": "c89f3da6-f54f-46d5-bb2d-63c4126aa682", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "de26c850-03f2-4e24-80c2-aedd0482a463", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a8bde3eb-1f41-4e9c-b591-1ea8637da7c7", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,admin"}]}, {"id": "52e82f8a-d0ec-4448-a8fa-a8ecc021a80d", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "4fb27b26-d072-41d3-a6ab-7ed461198348", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "73c5d5a6-ee64-4eae-b8e0-6afef02ce1f1", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "301ee3a6-6e1d-4046-bb13-2ab2b29e5892", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "8d0d7545-9b23-41f1-9d58-3cdf5203ea71", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "bc1fbeed-2b96-4bfd-9810-45fd2f0e01df", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "64c7d8a7-daae-44d9-9d42-f7d7ead0f5f8", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "225af02f-2c58-4e8d-bcb9-d56c6eb4b426", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "13ee6c77-3f84-4758-9ea1-6d1a17923ae4", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "b56c96ec-0b9f-44d3-8e8b-b952d171c580", "from": "Input", "name": "questions", "type": "Array", "value": "", "description": "针对简历的问题列表"}]}, {"id": "e545a304-53b9-40de-a559-f7346aa69a01", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "metaId": "jadelwn1gx", "runnable": true, "stageDesc": "正在生成推荐面试问题...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, {"to": "jadelwn1gx", "from": "jade39q3lc", "type": "jadeEvent", "metaId": "jade9m2sga", "runnable": true, "fromConnector": "E"}, {"name": "智能表单_2", "task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png", "taskId": "d496c444a3174beabbcec5441aed40e2", "formName": "面试评价表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "questions_f1544f8f-d6ec-4dc2-97d8-dbc05a5fd33b", "from": "Reference", "name": "questions", "type": "Array", "value": ["output", "extractedParams", "questions"], "isRequired": true, "referenceId": "b56c96ec-0b9f-44d3-8e8b-b952d171c580", "referenceKey": "questions", "referenceNode": "jadelwn1gx"}], "outputParams": [{"id": "output_4daf0f63-1cc3-46f9-b42a-e36e953141f7", "name": "output", "type": "Object", "value": [{"id": "6c87c01e-4cd3-483e-a646-6b1f63597e91", "name": "qeMap", "type": "Array", "value": "Array"}]}]}}}, "type": "manualCheckNodeState", "metaId": "jade22p95w", "runnable": true, "triggerMode": "manual"}, {"to": "jade22p95w", "from": "jadelwn1gx", "type": "jadeEvent", "metaId": "jade5q1tkm", "runnable": true, "fromConnector": "E"}, {"name": "大模型_4", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "a3bd214d-556f-4f1e-beac-726aec372264", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a2a846ac-8371-42f5-9617-aab697031aad", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "f313cfeb-6029-4d0d-b938-026ffa7cca1e", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "48dd4324-a11f-4d7e-bae8-f4874f526ab7", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "e05bdaeb-3cda-40a5-9144-e564f494b002", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "ee5b87db-2bb7-40a9-8836-658a2047ad98", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "41de7c20-0de4-4a75-b1a1-898c2ac9c2fd", "from": "Input", "name": "template", "type": "String", "value": "你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"}, {"id": "af7e6d67-5162-4c90-884f-9299e76ffd5a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "2e069130-be89-4292-8a00-9827309710dc", "from": "Reference", "name": "qeMap", "type": "Array", "value": ["output", "qeMap"], "referenceId": "6c87c01e-4cd3-483e-a646-6b1f63597e91", "referenceKey": "qeMap", "referenceNode": "jade22p95w"}]}]}, {"id": "52e1fdbe-6598-4a0e-8a5c-1bb8de01534e", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "9098e23e-62e6-4272-a746-7063e45710aa", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "f3dd4d91-9e84-45c4-bca2-5c27c4ae479a", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "d443924b-69a7-4519-a186-85b6bcd14edd", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "6ab5352d-1753-4f90-9c6b-2991d69dddc4", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "bf5d2b76-30ca-4cd6-bdfd-a61d70b1cf85", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "8bb45d25-fc74-45d5-8777-21670075be2e", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "2f382582-cd0c-4a4d-a994-58ba4b07dd97", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadeuhtp3b", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadeuhtp3b", "from": "jade22p95w", "type": "jadeEvent", "metaId": "jade5xr7fy", "runnable": true, "fromConnector": "E"}, {"name": "结束_1", "type": "endNodeEnd", "metaId": "jadehvxxbs", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "6d59e150-b944-42bc-a131-9e3d240f3d06", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "a1c67227-e131-4f88-93aa-e1840f3621ea", "from": "Reference", "name": "output", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "8bb45d25-fc74-45d5-8777-21670075be2e", "referenceKey": "llmOutput", "referenceNode": "jadeuhtp3b"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "b8e2ec79-b8a6-46b0-9e20-14662fabd988", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadehvxxbs", "from": "jadeuhtp3b", "type": "jadeEvent", "metaId": "jade3xtwme", "runnable": true, "fromConnector": "E"}, {"to": "jade4h5wks", "from": "jadelk78r1", "type": "jadeEvent", "metaId": "jade7dr3c3", "runnable": true, "fromConnector": "E"}], "metaId": "24f72de428124eb19fd12db36ebcfd34", "status": "active", "version": "1.0.0", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}', 'Jade', '2025-04-28 08:39:01.900125') ON CONFLICT (definition_id) DO NOTHING; + +INSERT INTO "public"."flow_graph" ("id", "version", "tenant", "status", "name", "data", "created_by", "created_at", "updated_by", "updated_at", "previous", "is_deleted") VALUES ('24f72de428124eb19fd12db36ebcfd34', '1.0.0', '31f20efc7e0848deab6a6bc10fc3021e', 'active', '24f72de428124eb19fd12db36ebcfd34', '{"id":"24f72de428124eb19fd12db36ebcfd34","title":"24f72de428124eb19fd12db36ebcfd34","source":"elsa","type":"jadeFlowGraph","tenant":"31f20efc7e0848deab6a6bc10fc3021e","setting":{"borderColor":"#047bfc","backColor":"whitesmoke","headColor":"steelblue","fontColor":"steelblue","captionfontColor":"whitesmoke","fontFace":"arial","captionfontFace":"arial black","fontSize":12,"captionfontSize":14,"fontStyle":"normal","captionfontStyle":"normal","fontWeight":"lighter","captionfontWeight":"lighter","hAlign":"center","vAlign":"top","captionhAlign":"center","lineHeight":1.5,"lineWidth":2,"captionlineHeight":1,"focusMargin":0,"focusBorderColor":"#047bfc","focusFontColor":"darkorange","focusBackColor":"whitesmoke","mouseInColor":"orange","mouseInBorderColor":"#047bfc","mouseInFontColor":"orange","mouseInBackColor":"whitesmoke","borderWidth":1,"focusBorderWidth":1,"globalAlpha":1,"backAlpha":0.15,"cornerRadius":4,"dashWidth":0,"autoText":false,"autoHeight":false,"autoWidth":false,"margin":25,"pad":10,"code":"","rotateDegree":0,"shadow":"","focusShadow":"","shadowData":"2px 2px 4px","outstanding":false,"pDock":"none","dockMode":"none","priority":0,"infoType":{"name":"none","next":"INFORMATION"},"progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"progressPercent":0.65,"showedProgress":false,"itemPad":[5,5,5,5],"itemScroll":{"x":0,"y":0},"scrollLock":{"x":false,"y":false},"resizeable":true,"selectable":true,"rotateAble":true,"editable":true,"moveable":true,"dragable":true,"visible":true,"deletable":true,"allowLink":true,"shared":false,"strikethrough":false,"underline":false,"numberedList":false,"bulletedList":false,"enableAnimation":false,"enableSocial":true,"emphasized":false,"bulletSpeed":1,"tag":{},"allNodeNumLimit":99,"sameTypeNodeNumLimit":19,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10},"pages":[{"x":-3265.2857142857183,"y":2282.8690476190486,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":true,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.40000000000000013,"scaleY":0.40000000000000013,"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc","shapes":[{"x":-170.8928571428571,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":false,"index":0,"width":360,"height":225,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","isVisible":true,"isRequired":true,"description":"这是用户输入的问题。","displayName":"用户问题","disableModifiable":true}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":2726.964285714285,"y":97.26190476190459,"id":"jadesoux5i","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":false,"index":1,"width":360,"height":181,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"54dab89c-5693-4082-baa7-12c648d812f7","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output","errorMessage"],"editable":true,"isRequired":true,"description":"","referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"c4469c16-88a7-4575-b339-9a06e3305f3b","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","mouseInBorderColor":"rgb(4, 123, 252)"},{"x":886.0342890059239,"y":-29.654761904761983,"id":"jadelk78r1","pad":6,"bold":false,"text":"AI简历解析插件","type":"toolInvokeNodeState","dirty":false,"index":2,"width":360,"height":185,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"fileUrl"},{"name":"instanceId"}],"return":{"type":"object"},"uniqueName":"8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"fileUrl_edfec05d-9812-429f-ae55-24df5d2e1216","from":"Reference","name":"fileUrl","type":"String","value":["output","fileUrl"],"isRequired":true,"description":"简历文件URL","referenceId":"45fafbfd-5966-47ba-91af-baebe09bdab1","referenceKey":"fileUrl","referenceNode":"jadeemu770"},{"id":"instanceId_24e98795-b3aa-4a0f-b78f-d4b059a59768","from":"Reference","name":"instanceId","type":"String","value":["instanceId"],"isRequired":true,"description":"实例ID","referenceId":"instanceId","referenceKey":"instanceId","referenceNode":"_systemEnv"}],"outputParams":[{"id":"output_5315b59e-1d27-4278-a06e-f89c7627782a","name":"output","type":"Object","value":[{"id":"bf6d9282-2bf5-4826-9be1-10756ed6d728","name":"isFileHandled","type":"Boolean","value":"Boolean"},{"id":"9e3844fd-864d-4504-8f2b-dba7e919135b","name":"cvAnalyzerPrompt","type":"String","value":"String"},{"id":"50617d76-27e1-49aa-a653-1947168d8937","name":"errorMessage","type":"String","value":"String"}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":337.57000329163884,"y":-111.01190476190482,"id":"jadeemu770","pad":6,"bold":false,"text":"代码","type":"codeNodeState","dirty":false,"index":3,"width":368,"height":251,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"args"},{"name":"code"},{"name":"language"},{"name":"output"}],"return":{"type":"object"},"uniqueName":"e147f301-957a-4335-a155-1e86d1a45ae5"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"98a0c373-6b03-4fd0-bc1d-101c9f1b1fd2","from":"Expand","name":"args","type":"Object","value":[{"id":"a15b25db-133c-41b4-a329-4b9bd257ad3e","from":"Reference","name":"fileUrls","type":"Array","value":["fileUrls"],"referenceId":"fileUrls","referenceKey":"fileUrls","referenceNode":"_systemEnv"}]},{"id":"6a95280d-156a-45b2-b235-9ca2b9015d2f","from":"Input","name":"code","type":"String","value":"async def main(args: Args) -> Output:\n ret: Output = {\n \"fileUrl\": args[''fileUrls''][0] if args[''fileUrls''] else \"\"\n }\n return ret","language":"python"},{"id":"b770fd0c-d5ae-4f2a-8129-52839becb775","from":"Input","name":"language","type":"String","value":"python"},{"id":"1e2ee2b3-0106-44ac-89db-1684220c5ad4","from":"Input","name":"output","type":"Object","value":{"properties":{"output":{"type":"object","description":"","properties":{"fileUrl":{"type":"string","description":""}}}}}}],"outputParams":[{"id":"ebdfcbcb-4dbd-47dc-8bd6-a5752edb2487","from":"Expand","name":"output","type":"Object","value":[{"id":"45fafbfd-5966-47ba-91af-baebe09bdab1","from":"Input","name":"fileUrl","type":"String","value":"","description":""}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"codeComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":189.1071428571429,"y":145,"id":"jadexi4d2i","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":4,"textX":0,"textY":0,"width":148.46286043449595,"hAlign":"center","height":-130.51190476190482,"italic":false,"margin":20,"toShape":"jadeemu770","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":705.5700032916388,"y":14.488095238095184,"id":"jade3ccnk1","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":5,"textX":0,"textY":0,"width":180.4642857142851,"hAlign":"center","height":48.35714285714283,"italic":false,"margin":20,"toShape":"jadelk78r1","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeemu770","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1502.2128604344966,"y":-113.58333333333326,"id":"jade4h5wks","pad":6,"bold":false,"text":"条件","type":"conditionNodeCondition","dirty":false,"index":6,"width":600,"height":283,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"70695434-91fb-4493-87a4-e681e99d985e","type":"if","runnable":true,"conditions":[{"id":"a5aef4c1-af83-475e-9805-2dbff919de10","value":[{"id":"d9df8a7c-4cc6-4147-b73e-a9db324b1bde","from":"Reference","name":"left","type":"Boolean","value":["output","isFileHandled"],"referenceId":"bf6d9282-2bf5-4826-9be1-10756ed6d728","referenceKey":"isFileHandled","referenceNode":"jadelk78r1"},{"id":"4a915ffb-6082-4fca-a622-71d875b34412","from":"Input","name":"right","type":"Boolean","value":true,"referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"},{"id":"8e60cd88-0799-4f12-9fcb-e20ccbc7ecd0","value":[{"id":"f2552657-58f6-4def-a3c7-cab73575e5b0","from":"Reference","name":"left","type":"String","value":["output","errorMessage"],"referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"},{"id":"9c8f2868-6722-4ed6-b388-3215bfbcacca","from":"Input","name":"right","type":"String","value":"请确认 上传的文件是一份简历","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"contains"}],"conditionRelation":"or"},{"id":"32f55d87-ebf2-464f-9f78-ecd62e36f2dc","type":"if","runnable":true,"conditions":[{"id":"5e19d158-03a3-4315-b45c-ff2a0b384db0","value":[{"id":"847d2cf6-5201-4628-a183-ca9ba62745f2","from":"Reference","name":"left","type":"String","value":["output","errorMessage"],"referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"},{"id":"9bab7914-26b4-487e-b609-d9776b13e608","from":"Reference","name":"right","type":"","value":[],"referenceNode":""}],"condition":"is not empty string"}],"conditionRelation":"and"},{"id":"8650733f-4401-4155-8633-048a5726e6fe","type":"else","runnable":true,"conditions":[{"id":"4e7f3b9e-f609-4178-bd27-459d0683cac9","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2593.820003291639,"y":-446.0119047619048,"id":"jade2es9ti","pad":6,"bold":false,"text":"条件_1","type":"conditionNodeCondition","dirty":false,"index":7,"width":600,"height":227,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"e2cefcda-b6e2-4713-ac74-f2266a10bb19","type":"if","runnable":true,"conditions":[{"id":"2f466536-4151-4013-b008-bf5af8a37edb","value":[{"id":"c8601563-bbeb-497b-91f4-a1f81e08ad32","from":"Reference","name":"left","type":"String","value":["output","errorMessage"],"referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"},{"id":"3543673f-ee62-4e18-82ef-c9e0ecc2e9a2","from":"Reference","name":"right","type":"","value":"","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"is empty string"}],"conditionRelation":"and"},{"id":"99f4f4da-c3fd-4296-9b83-b0ff5a3ef595","type":"else","runnable":true,"conditions":[{"id":"d9733cad-0646-41f9-be5b-f39c9ac4d220","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2095.5463717870352,"y":7.0834256807963385,"id":"jade9gqfjk","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":8,"textX":0,"textY":0,"width":498.2736315046036,"hAlign":"center","height":-339.59533044270114,"italic":false,"margin":20,"toShape":"jade2es9ti","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade4h5wks","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-1|70695434-91fb-4493-87a4-e681e99d985e"},{"x":3641.320003291639,"y":-385.0119047619048,"id":"jade758stt","pad":6,"bold":false,"text":"智能表单","type":"manualCheckNodeState","dirty":true,"index":9,"width":360,"height":352,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","imgUrl":"http://localhost:8001/api/jober/static/smart_form/e85bd769-0212-4305-b56b-01e77faa14ff/form.png","taskId":"115b557320ac41e1b449b8107aaa1781","formName":"面试助手3","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"isCvFile_337878e3-7720-4ece-bf92-40d4a402625c","name":"isCvFile","type":"String","from":"Input","isRequired":true,"referenceNode":null,"referenceId":null,"referenceKey":null,"value":"null"},{"id":"instanceId_59f311b0-98b3-4292-8887-aba60c48e3e4","name":"instanceId","type":"String","from":"Reference","isRequired":true,"referenceNode":"_systemEnv","referenceId":"instanceId","referenceKey":"instanceId","value":["instanceId"]}],"outputParams":[{"id":"output_e5ae0061-8304-4327-958a-a6cd0f80100d","name":"output","type":"Object","value":[{"id":"ab8bf709-7374-4f33-90d6-72a64254f07e","name":"isCvFile","type":"String","value":"String"}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"manualCheckComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3187.1533525195437,"y":-278.34520249139706,"id":"jadepke1tr","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":10,"textX":0,"textY":0,"width":454.1666507720952,"hAlign":"center","height":69.33329772949224,"italic":false,"margin":20,"toShape":"jade758stt","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade2es9ti","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":2777.570003291639,"y":571.4880952380952,"id":"jadey32p7b","pad":6,"bold":false,"text":"AI提示词拼接工具","type":"toolInvokeNodeState","dirty":false,"index":11,"width":360,"height":185,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"appId"},{"name":"instanceId"},{"name":"input"}],"return":{"type":"string"},"uniqueName":"bdc009dc-969e-4839-b5d7-e9599009d50d"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"appId_0293cca7-c1ea-45b0-8ca8-b37a55f99303","from":"Reference","name":"appId","type":"String","value":["appId"],"isRequired":true,"description":"应用ID","referenceId":"appId","referenceKey":"appId","referenceNode":"_systemEnv"},{"id":"instanceId_93f70f4f-e2b6-4518-a53e-af0ccdc99d2c","from":"Reference","name":"instanceId","type":"String","value":["instanceId"],"isRequired":true,"description":"实例ID","referenceId":"instanceId","referenceKey":"instanceId","referenceNode":"_systemEnv"},{"id":"input_4ac2267a-fa7a-462c-ad8d-7b2936e0fac3","from":"Reference","name":"input","type":"String","value":["Question"],"isRequired":true,"description":"用户输入","referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}],"outputParams":[{"id":"output_65d89d72-205f-4bf6-bb6e-a11bebaa8497","name":"output","type":"String","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3412.570003291639,"y":-1506.0119047619048,"id":"jade39q3lc","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":false,"index":12,"width":360,"height":343,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"009c2461-9c7e-4f85-bf43-ad887acd6a8a","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"f78bde02-c766-4be5-bab6-70d2f6e6de6c","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"af11f474-c1ae-4e07-ad7e-92f378a9d19d","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"5fe88d69-b7bb-4ff3-abb1-a2fbeb4ec240","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"b23e18ed-c36f-4692-9917-4971f9c1659b","from":"Input","name":"temperature","type":"Number","value":0.7},{"id":"cd4194cd-db96-4164-8a45-9ca4ea30cd81","from":"Expand","name":"prompt","type":"Object","value":[{"id":"184c22f2-c6f5-4bb6-81ca-d214eb266b35","from":"Input","name":"template","type":"String","value":"你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"},{"id":"30eacd51-29f6-4a90-8e17-4e4ffe1ddcc6","from":"Expand","name":"variables","type":"Object","value":[{"id":"155a2932-e37e-4c18-b14b-6d44b2a43ae4","from":"Reference","name":"isFileHandled","type":"Boolean","value":["output","isFileHandled"],"referenceId":"bf6d9282-2bf5-4826-9be1-10756ed6d728","referenceKey":"isFileHandled","referenceNode":"jadelk78r1"},{"id":"950c301c-81ba-41a6-bda5-aa37360e92a5","from":"Reference","name":"cvAnalyzerprompt","type":"String","value":["output","cvAnalyzerPrompt"],"referenceId":"9e3844fd-864d-4504-8f2b-dba7e919135b","referenceKey":"cvAnalyzerPrompt","referenceNode":"jadelk78r1"}]}]},{"id":"dded6123-d168-44b8-8779-738d61af4859","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"a7b687f8-e993-4e5a-9a10-bb6df8df25d9","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"6ec07829-b51c-4397-8670-59659e43036f","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"3733d6f2-0ea2-4dab-9f5b-423352d4af0b","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"091115ca-4fd9-4265-8280-5d70cfcdcfca","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"b0a3bdd3-7a3c-4657-8288-4cf8a98d33a0","from":"Expand","name":"output","type":"Object","value":[{"id":"ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"51b4e7bb-19f5-4477-a2df-b974f8c97ba7","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3187.1533525195437,"y":-325.3452101207916,"id":"jadeqraasb","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":13,"textX":0,"textY":0,"width":225.41665077209518,"hAlign":"center","height":-1009.1666946411133,"italic":false,"margin":20,"toShape":"jade39q3lc","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade2es9ti","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0|e2cefcda-b6e2-4713-ac74-f2266a10bb19"},{"x":4292.748574720212,"y":-233.29761904761904,"id":"jadegqixt5","pad":6,"bold":false,"text":"条件_2","type":"conditionNodeCondition","dirty":false,"index":14,"width":600,"height":227,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"bb4f8032-19cc-445b-a2ff-5cda77ae59e3","type":"if","runnable":true,"conditions":[{"id":"8085598f-b128-4749-940c-b4bda2619cba","value":[{"id":"9a656755-91fe-47b6-a54c-4b2be274978a","from":"Reference","name":"left","type":"String","value":["output","errorMessage"],"referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"},{"id":"37c01598-663a-476c-9a99-202b73131d4b","from":"Input","name":"right","type":"String","value":"","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"is not empty string"}],"conditionRelation":"and"},{"id":"319689dd-4250-4f0d-9c1b-d071013ac712","type":"else","runnable":true,"conditions":[{"id":"737ba1f5-dbb5-401c-9bf5-e18597f02123","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":5110.605717577355,"y":-790.4761904761904,"id":"jadem1n9u5","pad":6,"bold":false,"text":"大模型_1","type":"llmNodeState","dirty":false,"index":15,"width":360,"height":343,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"b8519f34-6d5f-42cd-b949-438f44d3210e","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"8c18f95c-1585-4e11-be07-de595ad13a92","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"8fd16d6c-b043-46dd-8b55-f0b8dee2615c","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"bd6f983c-f8e8-4538-ac66-60024d09c0a1","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"0263e85d-6125-4e27-ab7a-d2bc351cf414","from":"Input","name":"temperature","type":"Number","value":0.6},{"id":"1cf0a04f-2ca7-41fd-bec4-f9a1dd016c7f","from":"Expand","name":"prompt","type":"Object","value":[{"id":"a1335ac5-b02b-47c5-91d4-ecb945833ef3","from":"Input","name":"template","type":"String","value":"你是一名经验丰富的专业面试官,请根据下文提供的简历信息,帮我梳理和回答以下问题:\n一、以“项目经历与专业技能总结”为题,总结候选人的项目经历、专业技能,格式如下:\n项目经历:(选择前3项经历,如果候选人项目经历不足3个,选择既有项目即可,不要做额外补充,从技术亮点、核心贡献两个维度进行总结)\n专业技能:(总结3类候选人掌握的专业技能,不总结人际沟通、团队协作、项目管理等非技术类技能)\n二、以“项目技术考察重点方向”为标题,请根据项目经历项,为每个项目建议2个技术方面的重点考察方向\n三、以“项目综合能力问题”为标题,请根据项目经历项,为每个项目准备2个考察综合能力(系统思维、影响他人的能力、学些能力、团队合作能力、抗压能力)的问题,不考察项目的具体技术实现\n四、以“专业技能重点考察方向”为标题,请根据专业技能项,为每个技能建议2个重点考察方向,考察候选人对编程语言和软件理论的掌握程度\n请用中文输出所有信息。\n以下为简历信息:\n{{cvAnalyzerprompt}}"},{"id":"d6309a8b-d15b-4380-b6c6-ef000a9e5201","from":"Expand","name":"variables","type":"Object","value":[{"id":"25e56c39-1668-4428-8174-116b1a906bbd","from":"Reference","name":"isFileHandled","type":"Boolean","value":["output","isFileHandled"],"referenceId":"bf6d9282-2bf5-4826-9be1-10756ed6d728","referenceKey":"isFileHandled","referenceNode":"jadelk78r1"},{"id":"498b5c92-92ef-4ebf-9346-f2847fb88a3a","from":"Reference","name":"cvAnalyzerprompt","type":"String","value":["output","cvAnalyzerPrompt"],"referenceId":"9e3844fd-864d-4504-8f2b-dba7e919135b","referenceKey":"cvAnalyzerPrompt","referenceNode":"jadelk78r1"}]}]},{"id":"dded6123-d168-44b8-8779-738d61af4859","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"e1ad1e33-6a7c-451d-ba5a-158595cb2af8","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"3c3f111b-85ec-42c0-af8e-f3352518a29b","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"1cd6884e-c3e3-483b-baeb-d592c4f58ebf","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"7d01bbd3-df9c-4884-8d02-54b195096857","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"9e2fb06e-0276-48c8-8606-c36a31cf93b0","from":"Expand","name":"output","type":"Object","value":[{"id":"3eab6484-fa97-4e06-a0cc-263d3ed5274a","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"d438bc29-a627-44c2-bd57-4bf64eceba5a","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":4886.081819043942,"y":-112.63091010139105,"id":"jadee79arc","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":16,"textX":0,"textY":0,"width":224.52389853341265,"hAlign":"center","height":-506.3452803747993,"italic":false,"margin":20,"toShape":"jadem1n9u5","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadegqixt5","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0|bb4f8032-19cc-445b-a2ff-5cda77ae59e3"},{"x":3451.3200032916393,"y":321.4880952380952,"id":"jadedzi58q","pad":6,"bold":false,"text":"大模型_2","type":"llmNodeState","dirty":false,"index":17,"width":360,"height":343,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"1d11a986-7dc9-4ba3-84d8-3f15810ae587","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"4ebabb28-8981-46d6-a458-953082c01f9a","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"5af05d34-4b6e-435f-872c-96fa990a1476","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"ea9b2985-96e6-480c-b065-c7cce122b57a","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"32801848-b75f-4ac0-b050-f3dfbd57e3ad","from":"Input","name":"temperature","type":"Number","value":0.7},{"id":"1891af57-83ff-458d-ac70-d35a7af89705","from":"Expand","name":"prompt","type":"Object","value":[{"id":"e9e29b94-85e2-47c8-84d2-055a77660b88","from":"Input","name":"template","type":"String","value":"{{query}}"},{"id":"29e702cd-b721-442a-9893-81298cfa2953","from":"Expand","name":"variables","type":"Object","value":[{"id":"c34fda11-8f17-4c85-96aa-35c5bb311e2a","from":"Reference","name":"query","type":"String","value":["output"],"referenceId":"output_65d89d72-205f-4bf6-bb6e-a11bebaa8497","referenceKey":"output","referenceNode":"jadey32p7b"}]}]},{"id":"dded6123-d168-44b8-8779-738d61af4859","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"2b06515f-c9d5-41bd-bfbe-e1b1382d6ec1","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"0b5e2451-ed5f-46b2-8600-8893963296e4","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"acb67126-1a1b-4430-946e-30e768712333","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"f7d670ab-1cad-4028-a9c8-c84a5a170ac1","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"29ed2025-1eaa-462a-a37a-e05677eef8c4","from":"Expand","name":"output","type":"Object","value":[{"id":"451cf45e-6250-4f08-9d73-51d94433d749","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"dab1caf6-f171-4f6d-beb7-97c913fa1637","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3137.570003291639,"y":663.9880952380952,"id":"jadeqdcs4x","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":18,"textX":0,"textY":0,"width":313.75000000000045,"hAlign":"center","height":-171,"italic":false,"margin":20,"toShape":"jadedzi58q","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadey32p7b","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4101.320003291639,"y":470.2380952380952,"id":"jadefw1zfk","pad":6,"bold":false,"text":"结束_2","type":"endNodeEnd","dirty":false,"index":19,"width":360,"height":181,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"b6d32b12-26db-4a72-974b-7e197326e653","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"5c0e817c-8851-4350-9c5b-0ac5792a4e71","from":"Reference","name":"output","type":"String","value":["output","llmOutput"],"editable":true,"isRequired":true,"description":"","referenceId":"451cf45e-6250-4f08-9d73-51d94433d749","referenceKey":"llmOutput","referenceNode":"jadedzi58q"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"5e7a8388-8193-4f2d-a44a-094ab6fc576b","from":"Input","name":"enableLog","type":"Boolean","value":false}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":3811.3200032916393,"y":492.9880952380952,"id":"jadecvs6at","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":20,"textX":0,"textY":0,"width":290,"hAlign":"center","height":67.75,"italic":false,"margin":20,"toShape":"jadefw1zfk","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadedzi58q","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":5366.320003291639,"y":447.7380952380952,"id":"jadexn70e2","pad":6,"bold":false,"text":"结束_4","type":"endNodeEnd","dirty":false,"index":21,"width":360,"height":181,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"5268f248-07c8-4871-8732-e007316214fc","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"1434e18b-bd1b-4286-bc2f-cfb2cb1e9c1e","from":"Reference","name":"output","type":"String","value":["output","errorMessage"],"editable":true,"isRequired":true,"description":"","referenceId":"50617d76-27e1-49aa-a653-1947168d8937","referenceKey":"errorMessage","referenceNode":"jadelk78r1"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"e0c88e2d-1373-4606-8aaf-2d8ff330a27f","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":4886.081819043942,"y":-65.6309930710566,"id":"jade2u3je1","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":22,"textX":0,"textY":0,"width":480.23818424769706,"hAlign":"center","height":603.8690883091517,"italic":false,"margin":20,"toShape":"jadexn70e2","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadegqixt5","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":4001.320003291639,"y":-209.01190476190482,"id":"jadet1dvap","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":23,"textX":0,"textY":0,"width":291.4285714285729,"hAlign":"center","height":89.21428571428578,"italic":false,"margin":20,"toShape":"jadegqixt5","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade758stt","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":5660.4628604345,"y":-664.6309523809527,"id":"jade9puqh3","pad":6,"bold":false,"text":"文本提取","type":"textExtractionNodeState","dirty":false,"index":24,"width":360,"height":333,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"extractParam"},{"name":"memoryConfig"},{"name":"memorySwitch"},{"name":"histories"}],"return":{"type":"object"},"uniqueName":"3bca6a3f-9623-4228-b120-1a5e0d41dc14"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"stageDesc":"正在生成推荐面试问题...","inputParams":[{"id":"extractParam_e7356380-7e19-4def-a1b5-3f7fb62c06af","from":"Expand","name":"extractParam","type":"Object","value":[{"id":"text_da536c71-b299-40f0-8bca-720364fe20a0","from":"Reference","name":"text","type":"String","value":["output","llmOutput"],"referenceId":"3eab6484-fa97-4e06-a0cc-263d3ed5274a","referenceKey":"llmOutput","referenceNode":"jadem1n9u5"},{"id":"desc_ddf67a20-776a-4c29-b741-9b5ed31a5104","from":"Input","name":"desc","type":"String","value":""},{"id":"outputSchema_1d2b703c-237b-40c9-9286-0d427c2b7312","from":"Input","name":"outputSchema","type":"String","value":"{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"},{"id":"0d0a1d90-49b7-4bc6-af15-4c1c2905dadd","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"928af3d3-8671-4d93-a091-fd1662b74474","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"2ee47e96-94a7-4ee4-b616-36201338a7e4","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"temperature_236f8466-2e34-4b41-a199-a8d2843261ac","from":"Input","name":"temperature","type":"Number","value":"0.3"}]},{"id":"memoryConfig_88d92098-2b56-4757-9fb6-08f3628e19ce","from":"Expand","name":"memoryConfig","type":"Object","value":[{"id":"windowAlg_91b53de6-83c0-44eb-bf92-2585268d7526","from":"Input","name":"windowAlg","type":"String","value":"buffer_window"},{"id":"serializeAlg_f77ab974-f9c6-4ad3-9bcd-818fad8d96e2","from":"Input","name":"serializeAlg","type":"String","value":"full"},{"id":"property_7894b9b6-55e0-4b6e-acf8-d430e396d00c","from":"Input","name":"property","type":"Integer","value":"0"}]},{"id":"memorySwitch_a59975ed-71a1-4647-83b1-20c3aefe44f1","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"histories_643f02b9-d709-4fc9-a04b-f9e590d96786","from":"Reference","name":"histories","type":"Array","value":["memories"],"referenceId":"memories","referenceKey":"memories","referenceNode":"_systemEnv"}],"outputParams":[{"id":"28eac6e6-2877-426a-a22c-5e425bfd2b1e","from":"Expand","name":"output","type":"Object","value":[{"id":"fa30b424-be71-443d-b18d-fbd32144e049","from":"Expand","name":"extractedParams","type":"Object","value":[{"id":"c19b4740-ce95-4483-b789-47286a19096b","from":"Input","name":"questions","type":"Array","value":"","description":"针对简历的问题列表"}]},{"id":"success_67bf7577-82ac-47b8-b8fa-7d7ebc53513a","from":"Input","name":"success","type":"Boolean","value":"Boolean"}]}],"enableStageDesc":true,"jadeNodeConfigChangeIgnored":false}}},"stageDesc":"正在生成推荐面试问题...","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","enableStageDesc":true},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"textExtractionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":5470.605717577355,"y":-618.9761904761904,"id":"jade79wnvk","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":25,"textX":0,"textY":0,"width":189.85714285714494,"hAlign":"center","height":120.84523809523762,"italic":false,"margin":20,"toShape":"jade9puqh3","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadem1n9u5","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2095.5463717870352,"y":63.08343664805096,"id":"jadekuuju4","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":26,"textX":0,"textY":0,"width":631.4179139272496,"hAlign":"center","height":124.67846811385363,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade4h5wks","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-1|32f55d87-ebf2-464f-9f78-ecd62e36f2dc"},{"x":2095.5463717870352,"y":110.08335367838541,"id":"jade1o7wna","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":27,"textX":0,"textY":0,"width":682.0236315046036,"hAlign":"center","height":553.9047415597098,"italic":false,"margin":20,"toShape":"jadey32p7b","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade4h5wks","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":6189.070031819352,"y":-567.4536159624079,"id":"jademrtwtm","pad":6,"bold":false,"text":"智能表单_1","type":"manualCheckNodeState","dirty":true,"index":28,"width":360,"height":473,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","imgUrl":"http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png","taskId":"d496c444a3174beabbcec5441aed40e2","formName":"面试评价表单","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"questions_7e08f8cb-47ae-47df-b1a7-aaf62d7be03f","name":"questions","type":"Array","from":"Reference","isRequired":true,"referenceNode":"jade9puqh3","referenceId":"c19b4740-ce95-4483-b789-47286a19096b","referenceKey":"questions","value":["output","extractedParams","questions"]}],"outputParams":[{"id":"output_f00ac42b-c893-479b-b0ee-5cbe996c885a","name":"output","type":"Object","value":[{"id":"27aa7c57-e31e-482a-98f0-bdbcc21a6138","name":"qeMap","type":"Array","value":"Array"}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"manualCheckComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":6020.4628604345,"y":-498.13095238095275,"id":"jadem1n5oj","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":29,"textX":0,"textY":0,"width":168.60717138485234,"hAlign":"center","height":167.17733641854488,"italic":false,"margin":20,"toShape":"jademrtwtm","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade9puqh3","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":6915.736698486009,"y":-872.4536159624076,"id":"jadezaa7cy","pad":6,"bold":false,"text":"大模型_3","type":"llmNodeState","dirty":true,"index":30,"width":360,"height":411,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"fcd5702a-23f8-482e-a193-945676ca99ef","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"3a0ce6b3-8e98-4045-9323-957415084fba","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"35594014-3ca3-46b7-ac71-acd76b443952","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"f3d8a723-81d0-48e2-b671-224caf7f0f17","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"1bf48ee9-ac82-4df5-be74-df96958f3f05","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"eb7a528c-c012-4374-bbf0-4a88e4727334","from":"Expand","name":"prompt","type":"Object","value":[{"id":"aeb114f8-9032-4be6-b9f2-a0301fd5cf05","from":"Input","name":"template","type":"String","value":"你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"},{"id":"c4b818ba-1f8a-4f18-b196-c31bba136185","from":"Expand","name":"variables","type":"Object","value":[{"id":"c709baca-8d63-4dd4-bced-e2762386a86a","from":"Reference","name":"qeMap","type":"Array","value":["output","qeMap"],"referenceId":"27aa7c57-e31e-482a-98f0-bdbcc21a6138","referenceKey":"qeMap","referenceNode":"jademrtwtm"}]}]},{"id":"59d76a4e-1fd8-4950-9f84-6ebc17b7553c","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"a5b51a1c-68d7-4d8b-8791-9f788cd52f22","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"e1322719-1397-4dc8-a21e-674e1fb0eec0","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"892cf7e4-85c2-428d-b45b-0112a5978bd3","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"417ec551-d27e-49bc-b8ee-002a4bbd5b48","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"58e76471-8fb4-456d-9415-88192b008bd0","from":"Expand","name":"output","type":"Object","value":[{"id":"23891b11-11a1-4915-80d1-53c515335dd5","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"c6b85478-009a-4ade-8bc2-a288e226e6db","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":6549.070031819352,"y":-330.95361596240787,"id":"jadeeeqmda","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":31,"textX":0,"textY":0,"width":366.66666666665697,"hAlign":"center","height":-335.9999999999998,"italic":false,"margin":20,"toShape":"jadezaa7cy","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jademrtwtm","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":7634.070031819344,"y":-579.1202826290745,"id":"jadeohika6","pad":6,"bold":false,"text":"结束_3","type":"endNodeEnd","dirty":false,"index":32,"width":360,"height":181,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"73bad02b-26cb-417a-b1e0-85de07f398fa","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"aa9bc2e9-6dbf-4a6e-8d05-772f148da36e","from":"Input","name":"output","type":"String","value":"面试完成","editable":true,"isRequired":true,"description":""}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"519710b6-f147-4cdb-98e7-b60aa0600be5","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":7275.736698486009,"y":-666.9536159624076,"id":"jadey8ppgc","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":33,"textX":0,"textY":0,"width":358.33333333333485,"hAlign":"center","height":178.33333333333314,"italic":false,"margin":20,"toShape":"jadeohika6","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadezaa7cy","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4079.0700318193494,"y":-1339.1202826290773,"id":"jadelwn1gx","pad":6,"bold":false,"text":"文本提取_1","type":"textExtractionNodeState","dirty":false,"index":34,"width":360,"height":333,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"extractParam"},{"name":"memoryConfig"},{"name":"memorySwitch"},{"name":"histories"}],"return":{"type":"object"},"uniqueName":"3bca6a3f-9623-4228-b120-1a5e0d41dc14"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"stageDesc":"正在生成推荐面试问题...","inputParams":[{"id":"c5fcdff0-2822-49c1-805d-242d38acf952","from":"Expand","name":"extractParam","type":"Object","value":[{"id":"0fc3e0ac-307a-4c9c-8446-da801ca2a86e","from":"Reference","name":"text","type":"String","value":["output","llmOutput"],"referenceId":"ec24f7c3-3c2e-4f9c-b5e1-3ef129e9ddf2","referenceKey":"llmOutput","referenceNode":"jade39q3lc"},{"id":"bb3f946b-a504-44c9-a9e6-7271052ca40d","from":"Input","name":"desc","type":"String","value":""},{"id":"e99d0bd6-62f8-4f1b-80e9-72f7a2a19687","from":"Input","name":"outputSchema","type":"String","value":"{\"type\":\"object\",\"properties\":{\"questions\":{\"type\":\"array\",\"description\":\"针对简历的问题列表\"}}}"},{"id":"c89f3da6-f54f-46d5-bb2d-63c4126aa682","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"de26c850-03f2-4e24-80c2-aedd0482a463","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"a8bde3eb-1f41-4e9c-b591-1ea8637da7c7","from":"Input","name":"tag","type":"String","value":"SiliconFlow,admin"}]},{"id":"52e82f8a-d0ec-4448-a8fa-a8ecc021a80d","from":"Input","name":"temperature","type":"Number","value":"0.3"}]},{"id":"4fb27b26-d072-41d3-a6ab-7ed461198348","from":"Expand","name":"memoryConfig","type":"Object","value":[{"id":"73c5d5a6-ee64-4eae-b8e0-6afef02ce1f1","from":"Input","name":"windowAlg","type":"String","value":"buffer_window"},{"id":"301ee3a6-6e1d-4046-bb13-2ab2b29e5892","from":"Input","name":"serializeAlg","type":"String","value":"full"},{"id":"8d0d7545-9b23-41f1-9d58-3cdf5203ea71","from":"Input","name":"property","type":"Integer","value":"0"}]},{"id":"bc1fbeed-2b96-4bfd-9810-45fd2f0e01df","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"64c7d8a7-daae-44d9-9d42-f7d7ead0f5f8","from":"Reference","name":"histories","type":"Array","value":["memories"],"referenceId":"memories","referenceKey":"memories","referenceNode":"_systemEnv"}],"outputParams":[{"id":"225af02f-2c58-4e8d-bcb9-d56c6eb4b426","from":"Expand","name":"output","type":"Object","value":[{"id":"13ee6c77-3f84-4758-9ea1-6d1a17923ae4","from":"Expand","name":"extractedParams","type":"Object","value":[{"id":"b56c96ec-0b9f-44d3-8e8b-b952d171c580","from":"Input","name":"questions","type":"Array","value":"","description":"针对简历的问题列表"}]},{"id":"e545a304-53b9-40de-a559-f7346aa69a01","from":"Input","name":"success","type":"Boolean","value":"Boolean"}]}],"enableStageDesc":true,"jadeNodeConfigChangeIgnored":false}}},"stageDesc":"正在生成推荐面试问题...","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","enableStageDesc":true},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"textExtractionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3772.570003291639,"y":-1334.5119047619048,"id":"jade9m2sga","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":35,"textX":0,"textY":0,"width":306.5000285277106,"hAlign":"center","height":161.89162213282748,"italic":false,"margin":20,"toShape":"jadelwn1gx","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade39q3lc","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4615.403365152686,"y":-1370.786949295743,"id":"jade22p95w","pad":6,"bold":false,"text":"智能表单_2","type":"manualCheckNodeState","dirty":true,"index":36,"width":360,"height":314,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","imgUrl":"http://localhost:8001/api/jober/static/smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png","taskId":"d496c444a3174beabbcec5441aed40e2","formName":"面试评价表单","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"questions_f1544f8f-d6ec-4dc2-97d8-dbc05a5fd33b","name":"questions","type":"Array","from":"Reference","isRequired":true,"referenceNode":"jadelwn1gx","referenceId":"b56c96ec-0b9f-44d3-8e8b-b952d171c580","referenceKey":"questions","value":["output","extractedParams","questions"]}],"outputParams":[{"id":"output_4daf0f63-1cc3-46f9-b42a-e36e953141f7","name":"output","type":"Object","value":[{"id":"6c87c01e-4cd3-483e-a646-6b1f63597e91","name":"qeMap","type":"Array","value":"Array"}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"manualCheckComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":4439.070031819349,"y":-1172.6202826290773,"id":"jade5q1tkm","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":37,"textX":0,"textY":0,"width":176.33333333333667,"hAlign":"center","height":-41.166666666665606,"italic":false,"margin":20,"toShape":"jade22p95w","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelwn1gx","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":5333.0700318193485,"y":-1757.7869492957443,"id":"jadeuhtp3b","pad":6,"bold":false,"text":"大模型_4","type":"llmNodeState","dirty":false,"index":38,"width":360,"height":411,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"a3bd214d-556f-4f1e-beac-726aec372264","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"a2a846ac-8371-42f5-9617-aab697031aad","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"f313cfeb-6029-4d0d-b938-026ffa7cca1e","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"48dd4324-a11f-4d7e-bae8-f4874f526ab7","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"e05bdaeb-3cda-40a5-9144-e564f494b002","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"ee5b87db-2bb7-40a9-8836-658a2047ad98","from":"Expand","name":"prompt","type":"Object","value":[{"id":"41de7c20-0de4-4a75-b1a1-898c2ac9c2fd","from":"Input","name":"template","type":"String","value":"你是一名经验丰富的专业面试官,当前你已经完成了面试提问,并对面试者的回答进行的评价。请根据以下信息,帮我总结面试者的整体表现情况:\n\n请用中文输出所有信息,严格按照我给你的信息进行总结,不要杜撰不存在的事情。\n\n以下为面试者回答的问题以及你的评价, 其中问题内容在“question:”后,评价内容在“evaluate”后。\n{{qeMap}}"},{"id":"af7e6d67-5162-4c90-884f-9299e76ffd5a","from":"Expand","name":"variables","type":"Object","value":[{"id":"2e069130-be89-4292-8a00-9827309710dc","from":"Reference","name":"qeMap","type":"Array","value":["output","qeMap"],"referenceId":"6c87c01e-4cd3-483e-a646-6b1f63597e91","referenceKey":"qeMap","referenceNode":"jade22p95w"}]}]},{"id":"52e1fdbe-6598-4a0e-8a5c-1bb8de01534e","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"9098e23e-62e6-4272-a746-7063e45710aa","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"f3dd4d91-9e84-45c4-bca2-5c27c4ae479a","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"d443924b-69a7-4519-a186-85b6bcd14edd","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"6ab5352d-1753-4f90-9c6b-2991d69dddc4","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"bf5d2b76-30ca-4cd6-bdfd-a61d70b1cf85","from":"Expand","name":"output","type":"Object","value":[{"id":"8bb45d25-fc74-45d5-8777-21670075be2e","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"2f382582-cd0c-4a4d-a994-58ba4b07dd97","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":4975.403365152686,"y":-1213.786949295743,"id":"jade5xr7fy","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":39,"textX":0,"textY":0,"width":357.6666666666624,"hAlign":"center","height":-338.50000000000136,"italic":false,"margin":20,"toShape":"jadeuhtp3b","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade22p95w","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":6125.736698486013,"y":-1567.4536159624104,"id":"jadehvxxbs","pad":6,"bold":false,"text":"结束_1","type":"endNodeEnd","dirty":false,"index":40,"width":360,"height":181,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"6d59e150-b944-42bc-a131-9e3d240f3d06","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"a1c67227-e131-4f88-93aa-e1840f3621ea","from":"Reference","name":"output","type":"String","value":["output","llmOutput"],"editable":true,"isRequired":true,"description":"","referenceId":"8bb45d25-fc74-45d5-8777-21670075be2e","referenceKey":"llmOutput","referenceNode":"jadeuhtp3b"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"b8e2ec79-b8a6-46b0-9e20-14662fabd988","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":5693.0700318193485,"y":-1552.2869492957443,"id":"jade3xtwme","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":41,"textX":0,"textY":0,"width":432.66666666666424,"hAlign":"center","height":75.33333333333394,"italic":false,"margin":20,"toShape":"jadehvxxbs","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeuhtp3b","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1246.034289005924,"y":62.84523809523802,"id":"jade7dr3c3","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":42,"textX":0,"textY":0,"width":256.1785714285727,"hAlign":"center","height":-34.928571428571274,"italic":false,"margin":20,"toShape":"jade4h5wks","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadelk78r1","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"}]}],"enableText":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"]},"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"]},"version":"1.0.0"}', 'Jade', '2025-04-28 08:39:01.380192', 'Jade', '2025-04-28 08:39:01.90698', NULL, 'f') ON CONFLICT (id, version) DO NOTHING; + +INSERT INTO "public"."task_new" ("id", "name", "version", "template_id", "tenant_id", "attributes", "created_by", "created_at", "updated_by", "updated_at", "is_deleted") VALUES ('ea29812bde61441b8f530d7a08dcd7a6', '面试助手-v3', '1.0.0', 'ec6f8e93a80541bb930fc22678ef7043', '31f20efc7e0848deab6a6bc10fc3021e', '{"app_id": "fd8166b5005e4d66a77d318f3b1dd5e5", "version": "1.0.0", "aipp_type": "NORMAL", "meta_icon": "", "publish_at": "2025-04-28T08:39:03.015862656", "description": "", "meta_status": "active", "unique_name": "e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf", "flow_config_id": "24f72de428124eb19fd12db36ebcfd34", "flow_definition_id": "0e58b12cdafd476a81d4da70dce8175a", "publish_update_log": "", "publish_description": ""}', 'Jade', '2025-04-28 08:39:01.860385', 'Jade', '2025-04-28 08:39:03.032764', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('AI简历解析插件', '{"name":"AI简历解析插件","description":"解析简历内容,填充提示词模板,输出大模型提示词","parameters":{"type":"object","properties":{"fileUrl":{"type":"string","description":"简历文件URL"},"instanceId":{"type":"string","description":"实例ID"}},"required":["fileUrl","instanceId"]},"order":["fileUrl","instanceId"],"return":{"description":"Map结构包含提示词,插件处理过文件的标志位以及错误信息","type":"object","properties":{"isFileHandled":{"type":"boolean"},"cvAnalyzerPrompt":{"type":"string"},"errorMessage":{"type":"string"}},"converter":""}}', '{"FIT":{"fitableId":"cv.analyzer","genericableId":"modelengine.fit.jober.aipp.tool.cv.analyzer"}}', '{}', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', '1.0.0', 't', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8') ON CONFLICT("unique_name", "version") DO NOTHING; +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('AI提示词拼接工具', '{"name":"AI提示词拼接工具","description":"这是一个对用户输入提示词进行拼接的工具。","parameters":{"type":"object","properties":{"appId":{"type":"string","description":"应用ID"},"instanceId":{"type":"string","description":"实例ID"},"input":{"type":"string","description":"用户输入"}},"required":["appId","instanceId","input"],"order":["appId","instanceId","input"]},"return":{"type":"string"}}', '{"FIT":{"genericableId":"modelengine.fit.jober.aipp.tool.prompt.word.splice","fitableId":"prompt.word.splice","alias":"prompt.word.splice"}}', '{}', 'bdc009dc-969e-4839-b5d7-e9599009d50d', '1.0.0', 't', 'bdc009dc-969e-4839-b5d7-e9599009d50d', 'bdc009dc-969e-4839-b5d7-e9599009d50d', 'bdc009dc-969e-4839-b5d7-e9599009d50d') ON CONFLICT("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('面试助手-v3', '{"name":"面试助手-v3","description":"","manualIntervention":false,"parameters":{"type":"object","properties":{"aippId":{"description":"the aipp id of the waterFlow tool","default":"ec6f8e93a80541bb930fc22678ef7043","type":"string"},"tenantId":{"description":"the tenant id of the waterFlow tool","default":"31f20efc7e0848deab6a6bc10fc3021e","type":"string"},"inputParams":{"type":"object","properties":{"Question":{"type":"String","description":"这是用户输入的问题。"}},"required":["Question"],"order":["Question"]},"version":{"description":"the aipp version of the waterFlow tool","default":"1.0.0","type":"string"}},"required":["tenantId","aippId","version","inputParams"]},"return":{"type":"object","properties":{}},"order":["tenantId","aippId","version","inputParams"]}', '{"FIT":{"fitableId":"water.flow.invoke","genericableId":"07b51bd246594c159d403164369ce1db"},"APP":{"aippId":"ec6f8e93a80541bb930fc22678ef7043","appCategory":"chatbot","version":"1.0.0","appId":"fd8166b5005e4d66a77d318f3b1dd5e5"}}', 'null', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', '1.0.0', 't', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf') ON CONFLICT("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_app" ("source", "icon", "app_category", "tool_name", "tool_unique_name") VALUES ('system', '', 'chatbot', '面试助手-v3', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf') ON CONFLICT("tool_unique_name") DO NOTHING;; + +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', '{"name":"AI简历解析插件","description":"解析简历内容,填充提示词模板,输出大模型提示词","parameters":{"type":"object","properties":{"fileUrl":{"type":"string","description":"简历文件URL"},"instanceId":{"type":"string","description":"实例ID"}},"required":["fileUrl","instanceId"]},"order":["fileUrl","instanceId"],"return":{"description":"Map结构包含提示词,插件处理过文件的标志位以及错误信息","type":"object","properties":{"isFileHandled":{"type":"boolean"},"cvAnalyzerPrompt":{"type":"string"},"errorMessage":{"type":"string"}},"converter":""}}', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8') ON CONFLICT("name", "definition_group_name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('bdc009dc-969e-4839-b5d7-e9599009d50d', '{"name":"AI提示词拼接工具","description":"这是一个对用户输入提示词进行拼接的工具。","parameters":{"type":"object","properties":{"appId":{"type":"string","description":"应用ID"},"instanceId":{"type":"string","description":"实例ID"},"input":{"type":"string","description":"用户输入"}},"required":["appId","instanceId","input"],"order":["appId","instanceId","input"]},"return":{"type":"string"}}', 'bdc009dc-969e-4839-b5d7-e9599009d50d') ON CONFLICT("name", "definition_group_name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', '{"name":"e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf","description":"","manualIntervention":false,"parameters":{"type":"object","properties":{"aippId":{"description":"the aipp id of the waterFlow tool","default":"ec6f8e93a80541bb930fc22678ef7043","type":"string"},"tenantId":{"description":"the tenant id of the waterFlow tool","default":"31f20efc7e0848deab6a6bc10fc3021e","type":"string"},"inputParams":{"type":"object","properties":{"Question":{"type":"String","description":"这是用户输入的问题。"}},"required":["Question"],"order":["Question"]},"version":{"description":"the aipp version of the waterFlow tool","default":"1.0.0","type":"string"}},"required":["tenantId","aippId","version","inputParams"]},"return":{"type":"object","properties":{}},"order":["tenantId","aippId","version","inputParams"]}', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf') ON CONFLICT("name", "definition_group_name") DO NOTHING; + +INSERT INTO "public"."store_definition_group" ("name", "summary", "description", "extensions") VALUES ('e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'no summary', 'no desc', 'null') ON CONFLICT("name") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('bdc009dc-969e-4839-b5d7-e9599009d50d', 'FIT') ON CONFLICT("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('bdc009dc-969e-4839-b5d7-e9599009d50d', 'BUILTIN') ON CONFLICT("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', 'FIT') ON CONFLICT("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', 'BUILTIN') ON CONFLICT("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'APP') ON CONFLICT("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'APP_TYPE_4DB152B24F94473AB683B1ACBFE3C865') ON CONFLICT("tool_unique_name", "name") DO NOTHING; + +INSERT INTO "public"."store_tool_group" ("name", "definition_group_name", "summary", "description", "extensions") VALUES ('e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'e8bbd29c-e529-4c8e-abdb-b355b2d8dcdf', 'no summary', 'no desc', 'null') ON CONFLICT("name") DO NOTHING; + +INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "deploy_status", "is_builtin", "source", "icon") VALUES ('eb5adfcef2355b7b4e76dc53626a98a4b863c3e33c12c43221d03907c025bce8', 'AI提示词拼接工具', '{}', 'DEPLOYED', 't', '', NULL) ON CONFLICT ("plugin_id") DO NOTHING; +INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "deploy_status", "is_builtin", "source", "icon") VALUES ('f13a2bd6bdb5afdb2ce166fb2da6c445057b7e092791c743f9b8238dd78a62dd', 'AI简历解析插件', '{"artifactId":"aipp-plugin","groupId":"modelengine.fit.jober","checksum":"aba5bd4a9ad359ede54794a189d36a2e77c6d611af6a4aef6c63bbd588c61b24","type":"java","description":"AI简历解析插件","pluginFullName":"aipp-plugin-0.1.0-SNAPSHOT_1726059364182.jar","pluginName":"AI简历解析插件"}', 'DEPLOYED', 't', '', NULL) ON CONFLICT ("plugin_id") DO NOTHING; + +INSERT INTO "public"."store_plugin_tool" ("tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES ('AI简历解析插件', 'f13a2bd6bdb5afdb2ce166fb2da6c445057b7e092791c743f9b8238dd78a62dd', '8b7e54b7-ce07-40ed-ad93-5d608aa8f6d8', '', NULL) ON CONFLICT("plugin_id", "tool_unique_name") DO NOTHING; +INSERT INTO "public"."store_plugin_tool" ("tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES ('AI提示词拼接工具', 'eb5adfcef2355b7b4e76dc53626a98a4b863c3e33c12c43221d03907c025bce8', 'bdc009dc-969e-4839-b5d7-e9599009d50d', '', NULL) ON CONFLICT("plugin_id", "tool_unique_name") DO NOTHING; + +INSERT INTO "public"."app_builder_form" ("id", "name", "tenant_id", "appearance", "type", "create_by", "create_at", "update_by", "update_at", "is_deleted", "form_suite_id", "version") VALUES ('d496c444a3174beabbcec5441aed40e2', '面试评价表单', '31f20efc7e0848deab6a6bc10fc3021e', '{"imgUrl": "smart_form/7958d851-8062-49bd-b21e-d7372991c905/form.png", "schema": {"return": {"type": "object", "properties": {"qeMap": {"type": "array", "items": {"type": "object", "properties": {"evaluate": {"type": "string"}, "question": {"type": "string"}}}}}}, "parameters": {"type": "object", "required": ["questions"], "properties": {"questions": {"type": "array", "items": {"type": "string"}}}}}, "fileName": "面试评价表单.zip", "fileSize": 262789, "fileUuid": "3efef5019030460f8865c6f4e6ba419f", "iframeUrl": "smart_form/7958d851-8062-49bd-b21e-d7372991c905/build/index.html", "description": ""}', 'runtime', 'Jade', '2025-04-19 09:49:29.027086', 'Jade', '2025-04-19 09:49:29.027109', 0, '2a9a4ec42ea348a2882461139dd6639d', '1.0.0') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form" ("id", "name", "tenant_id", "appearance", "type", "create_by", "create_at", "update_by", "update_at", "is_deleted", "form_suite_id", "version") VALUES ('115b557320ac41e1b449b8107aaa1781', '面试助手3', '31f20efc7e0848deab6a6bc10fc3021e', '{"imgUrl": "smart_form/e85bd769-0212-4305-b56b-01e77faa14ff/form.png", "schema": {"return": {"type": "object", "properties": {"isCvFile": {"type": "string"}}}, "parameters": {"type": "object", "required": ["instanceId", "isCvFile"], "properties": {"isCvFile": {"type": "string"}, "instanceId": {"type": "string"}}}}, "fileName": "面试助手3.zip", "fileSize": 163980, "fileUuid": "4f77cacd52d2466abf7b983ff1d8628e", "iframeUrl": "smart_form/e85bd769-0212-4305-b56b-01e77faa14ff/build/index.html", "description": ""}', 'runtime', 'Jade', '2025-04-19 09:49:42.73549', 'Jade', '2025-04-19 09:49:42.735504', 0, 'f4e58718ca3a409abea5d34b11776bfc', '1.0.0') ON CONFLICT (id) DO NOTHING; + +SELECT MAX(id) FROM store_tag; +SELECT setval('store_tag_id_seq', (SELECT MAX(id) FROM store_tag)); +SELECT MAX(id) FROM store_tool; +SELECT setval('store_tool_id_seq', (SELECT MAX(id) FROM store_tool)); + + + diff --git a/app-builder/jane/jober/sql/jade/02_appbuilder_create.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/appbuilder_create.sql similarity index 98% rename from app-builder/jane/jober/sql/jade/02_appbuilder_create.sql rename to app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/appbuilder_create.sql index bb5e69ec39..d34da6e817 100644 --- a/app-builder/jane/jober/sql/jade/02_appbuilder_create.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/appbuilder_create.sql @@ -69,7 +69,13 @@ create table if not exists app_builder_app collection_usr_cnt bigint NOT NULL DEFAULT 0, is_deleted int2 DEFAULT 0, path varchar(255), - app_type varchar(255) DEFAULT '' + app_type varchar(255) DEFAULT '', + app_suite_id varchar(32) NULL, + is_active bool NULL DEFAULT false, + status varchar(16) NULL, + unique_name varchar(64) NULL, + publish_at timestamp(6) NULL, + app_id varchar(64) NULL ); create table if not exists app_builder_component diff --git a/app-builder/jane/jober/sql/create/flowsengine/flowsengine.postgres.create.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/general_create.sql similarity index 66% rename from app-builder/jane/jober/sql/create/flowsengine/flowsengine.postgres.create.sql rename to app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/general_create.sql index 1e1ab274f9..c17ee920dd 100644 --- a/app-builder/jane/jober/sql/create/flowsengine/flowsengine.postgres.create.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/general_create.sql @@ -1,3 +1,4 @@ +-- 流程相关 CREATE TABLE IF NOT EXISTS flow_definition ( definition_id VARCHAR(32) PRIMARY KEY, @@ -7,7 +8,7 @@ CREATE TABLE IF NOT EXISTS flow_definition version VARCHAR(16) NOT NULL, status VARCHAR(32) NOT NULL, graph JSONB NOT NULL, - created_by VARCHAR(127) NOT NULL, + created_by VARCHAR(32) NOT NULL, created_at timestamp without time zone NOT NULL ); @@ -24,12 +25,11 @@ comment on column flow_definition.created_at is '表示创建时间。'; CREATE UNIQUE INDEX IF NOT EXISTS UK_FLOW_NAME_VERSION ON flow_definition (name, version); CREATE UNIQUE INDEX IF NOT EXISTS UK_FLOW_META_ID_VERSION ON flow_definition (meta_id, version); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_DEFINITION_ID ON flow_definition (definition_id); CREATE TABLE IF NOT EXISTS flow_context ( context_id VARCHAR(32) NOT NULL PRIMARY KEY, - trace_id TEXT NOT NULL, + trace_id VARCHAR(32) NOT NULL, trans_id VARCHAR(32) NOT NULL, root_id VARCHAR(32) NOT NULL, stream_id VARCHAR(64) NOT NULL, @@ -41,6 +41,7 @@ CREATE TABLE IF NOT EXISTS flow_context parallel_mode VARCHAR(10), previous VARCHAR(32), batch_id VARCHAR(32), + from_batch VARCHAR(32), to_batch VARCHAR(32), sent BOOLEAN DEFAULT FALSE, create_at timestamp without time zone NOT NULL, @@ -62,29 +63,29 @@ comment on column flow_context.parallel is '表示流程实例上下文处于哪 comment on column flow_context.parallel_mode is '表示流程实例上下文所处平行节点状态。'; comment on column flow_context.previous is '表示流程实例上下文来源哪个上下文。'; comment on column flow_context.batch_id is '表示流程实例上下文所处批次标识。'; +comment on column flow_context.from_batch is '表示流程实例上下文来源批次标识。'; comment on column flow_context.to_batch is '表示流程实例上下文指向批次标识。'; comment on column flow_context.sent is '表示流程实例上下文是否已被事件发送。'; comment on column flow_context.create_at is '表示流程实例上下文创建时间。'; comment on column flow_context.update_at is '表示流程实例上下文更新时间。'; comment on column flow_context.archived_at is '表示流程实例上下文完成时间。'; -CREATE INDEX IF NOT EXISTS INDEX_FLOW_BATCH_ID ON flow_context (batch_id, to_batch); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_STREAM_ID ON flow_context (stream_id, position_id, batch_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_CONTEXT_ID ON flow_context (context_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TO_BATCH ON flow_context (to_batch); +CREATE INDEX IF NOT EXISTS INDEX_FLOW_STREAM_ID ON flow_context (stream_id); +CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_ID ON flow_context (trace_id); +CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRANS_ID_POSITION_ID ON flow_context (trans_id, position_id); +CREATE INDEX IF NOT EXISTS INDEX_FLOW_BATCH_ID ON flow_context (batch_id, from_batch, to_batch); CREATE TABLE IF NOT EXISTS flow_trace ( trace_id VARCHAR(32) NOT NULL PRIMARY KEY, stream_id VARCHAR(64) NOT NULL, - operator VARCHAR(127) NOT NULL, + operator VARCHAR(32) NOT NULL, application_name VARCHAR(32) NOT NULL, start_node VARCHAR(32) NOT NULL, - cur_nodes TEXT NOT NULL DEFAULT 'default_node', + cur_nodes TEXT NOT NULL, start_time timestamp without time zone NOT NULL, end_time timestamp without time zone, - status VARCHAR(32) NOT NULL DEFAULT 'ARCHIVED', - context_pool TEXT + merged_to VARCHAR(32) ); comment on table flow_trace is '流程轨迹'; @@ -96,34 +97,88 @@ comment on column flow_trace.start_node is '表示流程启动的开始节点'; comment on column flow_trace.cur_nodes is '表示流程实例当前所处节点'; comment on column flow_trace.start_time is '表示流程实例启动时间'; comment on column flow_trace.end_time is '表示流程实例结束时间'; +comment on column flow_trace.merged_to is '表示流程实例合并到哪个流程实例'; CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_STREAM_ID ON flow_trace (stream_id); -CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_ID ON flow_trace (trace_id); +CREATE INDEX IF NOT EXISTS INDEX_FLOW_TRACE_MERGE_ID ON flow_trace (trace_id, merged_to); -CREATE TABLE IF NOT EXISTS flow_graph -( - id VARCHAR(32) NOT NULL, - version VARCHAR(16) NOT NULL, - tenant VARCHAR(32) NOT NULL, - status VARCHAR(32) NOT NULL, - name VARCHAR(256), - data TEXT, - created_by VARCHAR(127) NOT NULL, - created_at timestamp without time zone NOT NULL, - updated_by VARCHAR(127) NOT NULL, - updated_at timestamp without time zone NOT NULL, - previous VARCHAR(50), - is_deleted BOOLEAN DEFAULT FALSE, - PRIMARY KEY (id, version) -); +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name = 'flow_trace' AND column_name = 'status' + ) THEN + -- 如果不存在,则执行 ALTER TABLE 语句 +ALTER TABLE flow_trace ADD COLUMN status VARCHAR(32) NOT NULL DEFAULT 'ARCHIVED'; +END IF; +END $$; + +ALTER TABLE flow_context ALTER COLUMN trace_id TYPE TEXT; +ALTER TABLE flow_trace ALTER COLUMN cur_nodes SET DEFAULT 'default_node'; +ALTER TABLE flow_trace ADD COLUMN IF NOT EXISTS context_pool TEXT; -CREATE TABLE fitable_usage +-- 任务中心相关 +CREATE TABLE IF NOT EXISTS fitable_usage ( fitable_id VARCHAR(128) NOT NULL, definition_id VARCHAR(32) NOT NULL, PRIMARY KEY (fitable_id, definition_id) ); +CREATE TABLE IF NOT EXISTS tag +( + id CHAR(32) PRIMARY KEY, + name VARCHAR(64) NOT NULL, + description VARCHAR(512) NOT NULL, + created_by VARCHAR(127) NOT NULL, + created_at TIMESTAMP NOT NULL, + updated_by VARCHAR(127) NOT NULL, + updated_at TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG ON tag (name); + +CREATE TABLE IF NOT EXISTS tag_usage +( + id CHAR(32) PRIMARY KEY, + tag_id CHAR(32) NOT NULL, + object_type VARCHAR(16) NOT NULL, + object_id CHAR(32) NOT NULL, + created_by VARCHAR(127) NOT NULL, + created_at TIMESTAMP NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS UK_TAG_USAGE ON tag_usage (tag_id, object_id, object_type); +CREATE INDEX IF NOT EXISTS IDX_TAG_OBJECT_ID ON tag_usage (object_id); + +CREATE TABLE IF NOT EXISTS task_category_trigger +( + "id" CHAR(32) PRIMARY KEY, + "task_id" CHAR(32) NOT NULL, + "category_id" CHAR(32) NOT NULL, + "fitable_id" CHAR(32) NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS "UK_TASK_CATEGORY_TRIGGER" ON task_category_trigger ("task_id", "category_id", "fitable_id"); +CREATE INDEX IF NOT EXISTS "IDX_TASK_CATEGORY_TRIGGER_TASK" ON task_category_trigger ("task_id"); + + +CREATE +OR REPLACE FUNCTION generate_uuid_text() +RETURNS CHAR(32) AS $$ +BEGIN +RETURN md5(random()::text || clock_timestamp()::text); +END; +$$ +LANGUAGE plpgsql; + +CREATE +OR REPLACE FUNCTION now_utc() +RETURNS TIMESTAMP AS $$ +BEGIN +RETURN CURRENT_TIMESTAMP AT TIME ZONE 'UTC'; +END; +$$ +LANGUAGE plpgsql; + CREATE TABLE IF NOT EXISTS flow_retry ( entity_id VARCHAR(32) NOT NULL, @@ -141,4 +196,33 @@ comment on column flow_retry.entity_type is '表示任务重试的上下文实 comment on column flow_retry.next_retry_time is '表示任务重试的下次时间'; comment on column flow_retry.last_retry_time is '表示上次执行的任务重试时间'; comment on column flow_retry.retry_count is '表示到目前为止的任务重试次数'; -comment on column flow_retry.version is '表示当前重试的版本号'; \ No newline at end of file +comment on column flow_retry.version is '表示当前重试的版本号'; + +CREATE TABLE IF NOT EXISTS flow_lock +( + lock_key VARCHAR(100) NOT NULL PRIMARY KEY, + expired_at timestamp without time zone, + locked_client VARCHAR(50) + ); + +comment on table flow_lock is '流程锁'; +comment on column flow_lock.lock_key is '锁名称'; +comment on column flow_lock.expired_at is '锁过期时间'; +comment on column flow_lock.locked_client is '上锁的客户端IP'; + +CREATE TABLE IF NOT EXISTS flow_graph +( + id VARCHAR(32) NOT NULL, + version VARCHAR(16) NOT NULL, + tenant VARCHAR(32) NOT NULL, + status VARCHAR(32) NOT NULL, + name VARCHAR(256), + data TEXT, + created_by VARCHAR(32) NOT NULL, + created_at timestamp without time zone NOT NULL, + updated_by VARCHAR(32) NOT NULL, + updated_at timestamp without time zone NOT NULL, + previous VARCHAR(50), + is_deleted BOOLEAN DEFAULT FALSE, + PRIMARY KEY (id, version) + ); \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/TestUtils.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/TestUtils.java index 25bbb128f8..527ace7ce0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/TestUtils.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/TestUtils.java @@ -9,10 +9,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.task.AppTask; import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; import modelengine.fit.jober.common.RangedResultSet; @@ -26,6 +27,9 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; +/** + * 测试工具类. + */ public class TestUtils { /** * 测试用的对话实例id @@ -38,15 +42,18 @@ public class TestUtils { public static final String DUMMY_CHILD_INSTANCE_ID = "testChildInstanceId"; private static final String DUMMY_FLOW_CONFIG_ID = "testFlowConfigId"; - private static final String DUMMY_FLOW_DEF_ID = "testFlowDefId"; - private static final String DUMMY_FLOW_CONFIG_VERSION = "1.0.0"; - private static final List TEST_TRACE_IDS = Collections.singletonList("testTraceId"); - private static final String APP_ID = "appId1"; + /** + * 构建流程数据. + * + * @param businessData 业务数据. + * @param dummyPrompt 提示词. + * @return 流程数据. + */ public static List> buildFlowDataWithExtraConfig(Map businessData, String dummyPrompt) { Map flowData = new HashMap<>(); @@ -61,48 +68,44 @@ public static List> buildFlowDataWithExtraConfig(Map> buildFlowDataWithExtraConfig(Map businessData, - String dummyPrompt, boolean logEnable) { - Map flowData = new HashMap<>(); - flowData.put(AippConst.BS_DATA_KEY, businessData); - Map extraJober = new HashMap() { - { - put(AippConst.BS_MODEL_PROMPT_KEY, dummyPrompt); - put(AippConst.BS_LOG_ENABLE_KEY, String.valueOf(logEnable)); - } - }; - - flowData.put(AippConst.CONTEXT_DATA_KEY, Collections.singletonMap(AippConst.BS_EXTRA_CONFIG_KEY, extraJober)); - return Collections.singletonList(flowData); - } - - public static Meta buildMeta() { - LocalDateTime createTime = LocalDateTime.now(); - LocalDateTime modifyTime = LocalDateTime.now(); - Meta expectMeta = new Meta(); - expectMeta.setName("testName"); - expectMeta.setId("testId"); - expectMeta.setCreator("testUser"); - expectMeta.setCreationTime(createTime); - expectMeta.setLastModificationTime(modifyTime); - - Map attribute = new HashMap<>(); - attribute.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, DUMMY_FLOW_CONFIG_ID); - attribute.put(AippConst.ATTR_VERSION_KEY, DUMMY_FLOW_CONFIG_VERSION); - attribute.put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()); - attribute.put(AippConst.ATTR_FLOW_DEF_ID_KEY, DUMMY_FLOW_DEF_ID); - attribute.put(AippConst.ATTR_APP_ID_KEY, APP_ID); - expectMeta.setAttributes(attribute); - expectMeta.setProperties(Collections.emptyList()); - return expectMeta; + /** + * 构造任务对象. + * + * @return {@link AppTask} 对象. + */ + public static AppTask buildTask() { + return AppTask.asEntity() + .setName("testName") + .setAppSuiteId("testId") + .setCreator("testUser") + .setCreationTime(LocalDateTime.now()) + .setLastModificationTime(LocalDateTime.now()) + .setFlowConfigId(DUMMY_FLOW_CONFIG_ID) + .setAttributeVersion(DUMMY_FLOW_CONFIG_VERSION) + .setStatus(AippMetaStatusEnum.INACTIVE.getCode()) + .setFlowDefinitionId(DUMMY_FLOW_DEF_ID) + .setAppId(APP_ID) + .build(); } + /** + * mock 元数据. + * + * @param metaExpected 元数据. + * @param metaServiceMock 元数据服务mock对象. + */ public static void mockMetaListReturnSingleItem5(Meta metaExpected, MetaService metaServiceMock) { Mockito.doReturn(RangedResultSet.create(Collections.singletonList(metaExpected), 0L, 1, 1L)) .when(metaServiceMock) .list(any(), eq(true), eq(0L), eq(1), any()); } + /** + * mock恢复流程. + * + * @param flowInstanceServiceMock 流程实例服务mock. + * @return {@link CountDownLatch} 闭锁. + */ public static CountDownLatch mockResumeFlow(FlowInstanceService flowInstanceServiceMock) { CountDownLatch countDownLatch = new CountDownLatch(1); Mockito.doAnswer((Answer) invocation -> { @@ -112,12 +115,23 @@ public static CountDownLatch mockResumeFlow(FlowInstanceService flowInstanceServ return countDownLatch; } + /** + * mock元数据. + * + * @param metaExpected 元数据对象. + * @param metaServiceMock 元数据服务mock. + */ public static void mockMetaListReturnSingleItem6(Meta metaExpected, MetaService metaServiceMock) { Mockito.doReturn(RangedResultSet.create(Collections.singletonList(metaExpected), 0L, 1, 1L)) - .when(metaServiceMock) - .list(any(), eq(true), eq(0L), eq(1), any()); + .when(metaServiceMock).list(any(), eq(true), eq(0L), eq(1), any()); } + /** + * mock异步任务失败. + * + * @param flowInstanceServiceMock 流程实例服务mock. + * @return {@link CountDownLatch} 闭锁. + */ public static CountDownLatch mockFailAsyncJob(FlowInstanceService flowInstanceServiceMock) { CountDownLatch countDownLatch = new CountDownLatch(1); Mockito.doAnswer((Answer) invocation -> { diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/aop/LocaleAspectTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/aop/LocaleAspectTest.java index 19843338ce..529d9ff481 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/aop/LocaleAspectTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/aop/LocaleAspectTest.java @@ -11,9 +11,6 @@ import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; -import modelengine.jade.authentication.context.UserContext; -import modelengine.jade.authentication.context.UserContextHolder; - import modelengine.fit.jober.aipp.po.AppBuilderAppPo; import modelengine.fit.jober.aipp.service.DatabaseFieldLocaleService; import modelengine.fitframework.aop.ProceedingJoinPoint; @@ -42,9 +39,7 @@ @MockitoSettings(strictness = Strictness.LENIENT) public class LocaleAspectTest { private DatabaseFieldLocaleService localeService; - private ProceedingJoinPoint pjp; - private MockedStatic opContextHolderMock; @BeforeEach @@ -52,7 +47,8 @@ void setUp() { this.localeService = mock(DatabaseFieldLocaleService.class); this.pjp = mock(ProceedingJoinPoint.class); this.opContextHolderMock = mockStatic(UserContextHolder.class); - opContextHolderMock.when(UserContextHolder::get).thenReturn(new UserContext("Jane", "127.0.0.1", "en")); + opContextHolderMock.when(UserContextHolder::get) + .thenReturn(new UserContext("Jane", "127.0.0.1", "en")); } @AfterEach @@ -89,8 +85,8 @@ void shouldSuccessWhenLocalizeJsonString() throws Throwable { AppBuilderAppPo resultPo; if (object instanceof AppBuilderAppPo) { resultPo = (AppBuilderAppPo) object; - assertThat(resultPo.getAttributes()).isEqualTo( - "{\"description:\":\"this is llm description\", \"icon\": \"http://ab\", \"greeting\": " + assertThat(resultPo.getAttributes()) + .isEqualTo("{\"description:\":\"this is llm description\", \"icon\": \"http://ab\", \"greeting\": " + "\"hello\", \"app_type\": \"interview_assistant\"}"); } } @@ -122,8 +118,8 @@ void shouldSuccessWhenLocalizeList() throws Throwable { } if (resultList.get(1) instanceof AppBuilderAppPo) { resultPo2 = (AppBuilderAppPo) resultList.get(1); - assertThat(resultPo2.getAttributes()).isEqualTo( - "{\"description:\":\"this is llm description\", \"icon\": \"http://ab\", \"greeting\": " + assertThat(resultPo2.getAttributes()) + .isEqualTo("{\"description:\":\"this is llm description\", \"icon\": \"http://ab\", \"greeting\": " + "\"hello\", \"app_type\": \"interview_assistant\"}"); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AgentControllerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AgentControllerTest.java index 892ba7f8a8..05f9db55f4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AgentControllerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AgentControllerTest.java @@ -12,10 +12,9 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; -import modelengine.fit.jane.task.gateway.Authenticator; - import modelengine.fit.http.client.HttpClassicClientResponse; import modelengine.fit.jane.common.response.Rsp; +import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.jober.aipp.dto.AgentCreateInfoDto; import modelengine.fit.jober.aipp.service.AgentInfoGenerateService; import modelengine.fitframework.annotation.Fit; diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippChatControllerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippChatControllerTest.java index 40015b1495..06c16985ba 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippChatControllerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippChatControllerTest.java @@ -11,8 +11,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import modelengine.fit.jane.task.gateway.Authenticator; - import modelengine.fit.http.HttpResource; import modelengine.fit.http.protocol.Address; import modelengine.fit.http.protocol.MessageHeaders; @@ -20,6 +18,7 @@ import modelengine.fit.http.protocol.RequestLine; import modelengine.fit.http.protocol.ServerRequest; import modelengine.fit.http.server.support.DefaultHttpClassicServerRequest; +import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.jober.aipp.dto.chat.QueryChatRequest; import modelengine.fit.jober.aipp.service.AippChatService; diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippLogControllerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippLogControllerTest.java index 0228398b1e..82716aec5d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippLogControllerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AippLogControllerTest.java @@ -10,7 +10,6 @@ import static org.mockito.Mockito.verify; import modelengine.fit.jane.task.gateway.Authenticator; - import modelengine.fit.jober.aipp.service.AippLogService; import org.junit.jupiter.api.BeforeEach; diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AppChatControllerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AppChatControllerTest.java index 92df7eec91..d30d60d20f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AppChatControllerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/controller/AppChatControllerTest.java @@ -6,6 +6,7 @@ package modelengine.fit.jober.aipp.controller; +import modelengine.fit.jane.task.gateway.Authenticator; import modelengine.fit.http.protocol.Address; import modelengine.fit.http.protocol.support.DefaultMessageHeaders; import modelengine.fit.http.server.HttpClassicServerRequest; @@ -16,6 +17,11 @@ import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; import modelengine.fit.jober.aipp.service.AppChatService; +import modelengine.fit.http.protocol.Address; +import modelengine.fit.http.protocol.support.DefaultMessageHeaders; +import modelengine.fit.http.server.HttpClassicServerRequest; +import modelengine.fit.http.support.DefaultCookieCollection; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -39,10 +45,8 @@ public class AppChatControllerTest { @Mock private Authenticator authenticator; - @Mock private AppChatService appChatService; - @Mock private HttpClassicServerRequest request; @@ -83,8 +87,8 @@ void testChatFailedByNoQuestion() { @Test @DisplayName("测试app_chat接口") void testChatFailedByNoBody() { - AippParamException exception = Assertions.assertThrows(AippParamException.class, - () -> this.controller.chat(request, "123", null)); + AippParamException exception = + Assertions.assertThrows(AippParamException.class, () -> this.controller.chat(request, "123", null)); Assertions.assertEquals(AippErrCode.APP_CHAT_REQUEST_IS_NULL.getErrorCode(), exception.getCode()); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppServiceTest.java new file mode 100644 index 0000000000..1ae96cdd01 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppServiceTest.java @@ -0,0 +1,165 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app; + +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.enums.AppState.INACTIVE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.converters.impl.AppVersionToAppDtoConverter; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domains.app.service.AppDomainService; +import modelengine.fit.jober.aipp.domains.app.service.impl.AppDomainServiceImpl; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.enums.AppCategory; +import modelengine.fit.jober.aipp.enums.AppTypeEnum; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fitframework.util.StringUtils; +import modelengine.jade.app.engine.base.service.UsrAppCollectionService; +import modelengine.jade.store.service.AppService; +import modelengine.fitframework.util.IoUtils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * {@link AppService。} + * + * @author 张越 + * @since 2025-02-18 + */ +public class AppServiceTest { + private AppDomainService appDomainService; + private AppFactory appFactory; + private AppVersionService appVersionService; + private UploadedFileManageService uploadedFileManageService; + private UsrAppCollectionService usrAppCollectionService; + + @BeforeEach + public void setUp() { + this.appFactory = mock(AppFactory.class); + this.appVersionService = mock(AppVersionService.class); + this.uploadedFileManageService = mock(UploadedFileManageService.class); + this.usrAppCollectionService = mock(UsrAppCollectionService.class); + ConverterFactory converterFactory = new ConverterFactory(List.of(new AppVersionToAppDtoConverter())); + this.appDomainService = new AppDomainServiceImpl(this.appFactory, this.appVersionService, + this.uploadedFileManageService, this.usrAppCollectionService, converterFactory, StringUtils.EMPTY); + } + + @Test + @DisplayName("测试 getVersions") + public void testGetVersions() { + // given. + AppVersion appVersion = mock(AppVersion.class); + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + when(this.appVersionService.retrieval(anyString())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + + App app = mock(App.class); + when(this.appFactory.create(anyString())).thenReturn(app); + doNothing().when(app).delete(any()); + + doNothing().when(this.uploadedFileManageService).cleanAippFiles(any()); + doNothing().when(this.usrAppCollectionService).deleteByAppId(anyString()); + + // when. + this.appDomainService.deleteByAppId("app_version_1", new OperationContext()); + + // then. + verify(app, times(1)).delete(any()); + verify(this.uploadedFileManageService, times(1)).cleanAippFiles( + argThat(strings -> strings.size() == 1 && strings.get(0).equals("app_version_1"))); + verify(this.usrAppCollectionService, times(1)).deleteByAppId(eq("app_version_1")); + } + + @Test + @DisplayName("测试 import导入") + public void testImportApp() throws IOException { + // given. + String appearance = IoUtils.content(AppServiceTest.class, "/appearance.txt"); + + App app = mock(App.class); + when(this.appFactory.create(anyString())).thenReturn(app); + + AppVersion appVersion = mock(AppVersion.class); + when(app.importData(any(), eq(""), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(AppBuilderAppPo.builder() + .id("app_version_1") + .name("myApp") + .type(AppTypeEnum.APP.code()) + .state(INACTIVE.getName()) + .appType(NORMAL.name()) + .version("0.0.1") + .appCategory(AppCategory.APP.getCategory()) + .updateBy("zy") + .createBy("zy") + .build()); + + when(appVersion.getConfig()).thenReturn( + AppBuilderConfig.builder().form(AppBuilderForm.builder().appearance(new HashMap<>()).build()).build()); + when(appVersion.getFlowGraph()).thenReturn(AppBuilderFlowGraph.builder().appearance(appearance).build()); + when(appVersion.getFormProperties()).thenReturn(Collections.emptyList()); + when(appVersion.getAttributes()).thenReturn(new HashMap<>()); + + // when. + String appConfig = IoUtils.content(AppServiceTest.class, "/export-data.txt"); + AppBuilderAppDto dto = this.appDomainService.importApp(appConfig, new OperationContext()); + + // then. + assertEquals("app_version_1", dto.getId()); + assertEquals("myApp", dto.getName()); + assertEquals(AppTypeEnum.APP.code(), dto.getType()); + assertEquals(INACTIVE.getName(), dto.getState()); + assertEquals(NORMAL.name(), dto.getAppType()); + assertEquals("0.0.1", dto.getVersion()); + assertEquals(AppCategory.APP.getCategory(), dto.getAppCategory()); + assertEquals("zy", dto.getUpdateBy()); + assertEquals("zy", dto.getCreateBy()); + } + + @Test + @DisplayName("测试 export导出") + public void testExportApp() { + // given. + AppVersion appVersion = mock(AppVersion.class); + when(this.appVersionService.retrieval(anyString())).thenReturn(appVersion); + + AppExportDto dto = AppExportDto.builder().build(); + when(appVersion.export(any(), any())).thenReturn(dto); + + // when. + AppExportDto result = this.appDomainService.exportApp("app_version_1", new HashMap<>(), new OperationContext()); + + // then. + assertEquals(dto, result); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppTest.java new file mode 100644 index 0000000000..122fb8b30c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/app/AppTest.java @@ -0,0 +1,209 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.app; + +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_NOT_FOUND; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fitframework.util.StringUtils; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.PluginToolService; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * {@link App} 的测试类。 + * + * @author 张越 + * @since 2025-02-18 + */ +public class AppTest { + private AppFactory factory; + private AppVersionService appVersionService; + private AppBuilderConfigRepository appBuilderConfigRepository; + private AppBuilderFlowGraphRepository appBuilderFlowGraphRepository; + private AppBuilderFormPropertyRepository formPropertyRepository; + private AippLogMapper aippLogMapper; + private AppService appService; + private AippChatMapper aippChatMapper; + private AppVersionFactory appVersionFactory; + private PluginToolService pluginToolService; + private PluginService pluginService; + + @BeforeEach + public void setUp() { + this.appVersionService = mock(AppVersionService.class); + this.appBuilderConfigRepository = mock(AppBuilderConfigRepository.class); + this.appBuilderFlowGraphRepository = mock(AppBuilderFlowGraphRepository.class); + this.formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); + this.aippLogMapper = mock(AippLogMapper.class); + this.appService = mock(AppService.class); + this.aippChatMapper = mock(AippChatMapper.class); + AppVersionRepository appVersionRepository = mock(AppVersionRepository.class); + this.appVersionFactory = mock(AppVersionFactory.class); + this.pluginToolService = mock(PluginToolService.class); + this.pluginService = mock(PluginService.class); + + this.factory = new AppFactory(this.appVersionService, this.appBuilderConfigRepository, + this.appBuilderFlowGraphRepository, this.formPropertyRepository, this.aippLogMapper, this.appService, + this.aippChatMapper, + appVersionRepository, + this.appVersionFactory, + new HashMap<>(), + this.pluginToolService, + this.pluginService); + } + + @Test + @DisplayName("测试 getVersions") + public void testGetVersions() { + // given. + App app = this.factory.create("app_1"); + when(this.appVersionService.getByAppSuiteId(anyString())).thenReturn(List.of(mock(AppVersion.class))); + + // when. + List versions = app.getVersions(); + + // then. + assertEquals(1, versions.size()); + verify(this.appVersionService, times(1)).getByAppSuiteId(eq("app_1")); + } + + @Test + @DisplayName("测试 export 异常") + public void testExportException() { + // given. + App app = this.factory.create("app_1"); + when(this.appVersionService.getByAppSuiteId(anyString())).thenReturn(Collections.emptyList()); + + // when. + AippException exception = assertThrows(AippException.class, + () -> app.export(new OperationContext())); + + // then. + assertEquals(APP_NOT_FOUND.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 export") + public void testExport() { + // given. + App app = this.factory.create("app_1"); + AppVersion appVersion = mock(AppVersion.class); + AppBuilderAppPo data = AppBuilderAppPo.builder().updateAt(LocalDateTime.now()).build(); + when(appVersion.getData()).thenReturn(data); + when(this.appVersionService.getByAppSuiteId(anyString())).thenReturn(List.of(appVersion)); + when(appVersion.export(any(), any())).thenReturn(AppExportDto.builder().version("1.0.0").build()); + + // when. + AppExportDto exportDto = app.export(new OperationContext()); + + // then. + assertEquals("1.0.0", exportDto.getVersion()); + } + + @Test + @DisplayName("测试 import") + public void testImport() { + // given. + App app = this.factory.create("app_1"); + AppVersion appVersion = mock(AppVersion.class); + AppBuilderAppPo data = AppBuilderAppPo.builder().updateAt(LocalDateTime.now()).name("myApp").build(); + when(appVersion.getData()).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + doNothing().when(appVersion).importData(any(), anyString(), eq(StringUtils.EMPTY), any(), any()); + doNothing().when(this.appVersionService).validateAppName(anyString(), any()); + doNothing().when(this.appVersionService).save(any()); + + // when. + AppVersion version = app.importData(AppExportDto.builder().build(), StringUtils.EMPTY, new OperationContext()); + + // then. + assertEquals(appVersion, version); + verify(this.appVersionService, times(1)).validateAppName(eq("myApp"), any()); + verify(this.appVersionService, times(1)).save(any()); + } + + @Test + @DisplayName("测试 delete 方法") + public void testDelete() { + // given. + App app = this.factory.create("app_1"); + AppVersion appVersion = mock(AppVersion.class); + AppBuilderAppPo data = AppBuilderAppPo.builder() + .updateAt(LocalDateTime.now()) + .name("myApp") + .configId("config_1") + .flowGraphId("flow_1") + .appId("app_version_1") + .type("app") + .build(); + when(appVersion.getData()).thenReturn(data); + when(this.appVersionService.getByAppSuiteId(anyString())).thenReturn(List.of(appVersion)); + + AppTask task = mock(AppTask.class); + when(appVersion.getTasks(any())).thenReturn(List.of(task)); + doNothing().when(task).delete(any()); + when(task.getEntity()).thenReturn(AppTask.asEntity().setUniqueName("unique_name_1")); + + AppTaskInstance instance = mock(AppTaskInstance.class); + when(task.getInstances(any())).thenReturn(List.of(instance)); + when(instance.getEntity()).thenReturn(AppTaskInstance.asEntity().setInstanceId("instance_1")); + + doNothing().when(this.appBuilderConfigRepository).delete(any()); + doNothing().when(this.appBuilderFlowGraphRepository).delete(any()); + doNothing().when(this.appVersionService).deleteByIds(any()); + doNothing().when(this.formPropertyRepository).deleteByAppIds(any()); + doNothing().when(this.aippLogMapper).deleteByInstanceIds(any()); + when(this.appService.deleteApp(anyString())).thenReturn(""); + when(this.aippChatMapper.deleteAppByAippId(anyString())).thenReturn(1); + + // when. + app.delete(new OperationContext()); + + // then. + verify(this.appBuilderConfigRepository, times(1)).delete(any()); + verify(this.appBuilderFlowGraphRepository, times(1)).delete(any()); + verify(this.appVersionService, times(1)).deleteByIds(any()); + verify(this.formPropertyRepository, times(1)).deleteByAppIds(any()); + verify(this.aippLogMapper, times(1)).deleteByInstanceIds(any()); + verify(this.appService, times(1)).deleteApp(eq("unique_name_1")); + verify(this.aippChatMapper, times(1)).deleteAppByAippId(eq("app_1")); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionServiceTest.java new file mode 100644 index 0000000000..84a36fb31f --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionServiceTest.java @@ -0,0 +1,620 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.AIPP_NAME_IS_DUPLICATE; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.AIPP_NAME_LENGTH_OUT_OF_BOUNDS; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_HAS_ALREADY; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_NAME_IS_INVALID; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_NOT_FOUND; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.condition.AppQueryCondition; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domain.AppTemplate; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.appversion.repository.impl.AppVersionRepositoryImpl; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.appversion.service.impl.AppVersionServiceImpl; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; +import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; +import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; +import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.enums.AppTypeEnum; +import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.fitframework.flowable.Choir; +import modelengine.fitframework.util.MapBuilder; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +/** + * {@link AppVersionService} 的测试类。 + * + * @author 张越 + * @since 2025-02-12 + */ +public class AppVersionServiceTest { + private static final int NAME_LENGTH_MAXIMUM = 10; + + private AppVersionService appVersionService; + private AppVersionRepository appVersionRepository; + private AppChatRepository appChatRepository; + private AppTaskInstanceService appTaskInstanceService; + private UploadedFileManageService uploadedFileManageService; + private AppBuilderConfigRepository configRepository; + private AppBuilderFlowGraphRepository flowGraphRepository; + private AppBuilderFormPropertyRepository formPropertyRepository; + private AppBuilderConfigPropertyRepository configPropertyRepository; + private AppTaskService appTaskService; + private AppVersionFactory appVersionFactory; + private AppBuilderAppMapper appBuilderAppMapper; + + + @BeforeEach + public void setUp() { + this.appVersionFactory = mock(AppVersionFactory.class); + this.appBuilderAppMapper = mock(AppBuilderAppMapper.class); + this.appVersionRepository = new AppVersionRepositoryImpl(this.appBuilderAppMapper, this.appVersionFactory); + this.appChatRepository = mock(AppChatRepository.class); + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.uploadedFileManageService = mock(UploadedFileManageService.class); + this.configRepository = mock(AppBuilderConfigRepository.class); + this.flowGraphRepository = mock(AppBuilderFlowGraphRepository.class); + this.formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); + this.configPropertyRepository = mock(AppBuilderConfigPropertyRepository.class); + this.appTaskService = mock(AppTaskService.class); + this.appVersionService = new AppVersionServiceImpl(this.appVersionRepository, this.appChatRepository, + this.appTaskInstanceService, this.uploadedFileManageService, this.configRepository, + this.flowGraphRepository, this.formPropertyRepository, this.configPropertyRepository, + this.appTaskService, this.appVersionFactory, AppVersionServiceTest.NAME_LENGTH_MAXIMUM); + } + + @Test + @DisplayName("测试getByAppId") + public void testGetByAppId() { + // given. + when(this.appVersionFactory.create(any(), any())).thenReturn(mock(AppVersion.class)); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn( + AppBuilderAppPo.builder().appSuiteId("app_1").build()); + + // when. + Optional versionOptional = this.appVersionService.getByAppId("app_version_1"); + + // then. + assertTrue(versionOptional.isPresent()); + verify(this.appBuilderAppMapper, times(1)).selectWithId(eq("app_version_1")); + } + + @Test + @DisplayName("测试getByPath") + public void testGetByPath() { + // given. + when(this.appVersionFactory.create(any(), any())).thenReturn(mock(AppVersion.class)); + when(this.appBuilderAppMapper.selectWithPath(anyString())).thenReturn( + AppBuilderAppPo.builder().appSuiteId("app_1").build()); + + // when. + Optional versionOptional = this.appVersionService.getByPath("/app_version_1"); + + // then. + assertTrue(versionOptional.isPresent()); + verify(this.appBuilderAppMapper, times(1)).selectWithPath(eq("/app_version_1")); + } + + @Test + @DisplayName("测试 retrieval") + public void testRetrieval() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + when(result.getData()).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + + // when. + AppVersion version = this.appVersionService.retrieval("app_version_1"); + + // then. + assertEquals("app_1", version.getData().getAppSuiteId()); + } + + @Test + @DisplayName("测试 retrieval 异常") + public void testRetrievalException() { + // given. + when(this.appVersionFactory.create(any(), any())).thenReturn(null); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(null); + + // when. + AippException exception = assertThrows(AippException.class, + () -> this.appVersionService.retrieval("app_version_1")); + + // then. + assertEquals(APP_NOT_FOUND.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 getByAppSuiteId") + public void testGetByAppSuiteId() { + // given. + when(this.appVersionFactory.create(any(), any())).thenReturn(null); + when(this.appBuilderAppMapper.selectByAppSuiteId(anyString())).thenReturn(Collections.emptyList()); + + // when. + List results = this.appVersionService.getByAppSuiteId("app_1"); + + // then. + assertEquals(0, results.size()); + verify(this.appBuilderAppMapper, times(1)).selectByAppSuiteId(eq("app_1")); + } + + @Test + @DisplayName("测试 run") + public void testRun() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + when(result.getData()).thenReturn(data); + doNothing().when(result).run(any(), any()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + doNothing().when(this.appChatRepository).saveChat(any(), any()); + + // when. + Choir choir = this.appVersionService.run( + CreateAppChatRequest.builder().question("123").appId("app_version_1").build(), new OperationContext()); + choir.subscribe(); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(RunContext.class); + verify(result, times(1)).run(captor.capture(), any()); + RunContext runContext = captor.getValue(); + assertEquals("123", runContext.getQuestion()); + } + + @Test + @DisplayName("测试 debug") + public void testDebug() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + when(result.getData()).thenReturn(data); + doNothing().when(result).debug(any(), any()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + doNothing().when(this.appChatRepository).saveChat(any(), any()); + + // when. + Choir choir = this.appVersionService.debug( + CreateAppChatRequest.builder().question("123").appId("app_version_1").build(), new OperationContext()); + choir.subscribe((subscription, o) -> { + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(RunContext.class); + verify(result, times(1)).debug(captor.capture(), any()); + RunContext runContext = captor.getValue(); + assertEquals("123", runContext.getQuestion()); + }); + } + + @Test + @DisplayName("测试 restart") + public void testRestart() { + // given. + when(this.appTaskInstanceService.getTaskId(anyString())).thenReturn("task_1"); + + AppTask appTask = mock(AppTask.class); + when(this.appTaskService.getTaskById(anyString(), any())).thenReturn(Optional.of(appTask)); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setAppId("app_version_1")); + + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + when(result.getData()).thenReturn(data); + doNothing().when(result).restart(any(), anyMap(), any(), any(), any()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + doNothing().when(this.appChatRepository).saveChat(any(), any()); + AppTaskInstance instance = mock(AppTaskInstance.class); + when(this.appTaskInstanceService.getInstanceById(anyString(), any())).thenReturn( + Optional.of(instance)); + when(instance.getChats()).thenReturn( + List.of(QueryChatRsp.builder().appId("app_version_1").chatId("chat_1").build())); + + AppLog appLog = mock(AppLog.class); + when(instance.getLogs()).thenReturn(List.of(appLog)); + when(appLog.getLogData()).thenReturn(AippInstLog.builder() + .logData(JsonUtils.toJsonString(MapBuilder.get().put("msg", "你在哪里?").build())) + .build()); + + // when. + Choir choir = this.appVersionService.restart("instance_1", new HashMap<>(), new OperationContext()); + choir.subscribe((subscription, o) -> { + // then. + verify(appTaskInstanceService, times(1)).getTaskId(eq("instance_1")); + verify(appTaskService, times(1)).getTaskById(eq("task_1"), any()); + verify(result, times(1)).restart(any(), anyMap(), any(), any(), any()); + }); + } + + @Test + @DisplayName("测试 create") + public void testCreate() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + AppBuilderConfig config = mock(AppBuilderConfig.class); + when(result.getData()).thenReturn(data); + when(result.getIcon()).thenReturn("/icon.png"); + when(result.getConfig()).thenReturn(config); + doNothing().when(result).cloneVersion(any(), anyString(), anyString(), any()); + when(config.getConfigProperties()).thenReturn(Collections.emptyList()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + this.mockSave(); + + // when. + AppVersion res = this.appVersionService.create("app_template_1", + AppBuilderAppCreateDto.builder().name("myApp").appCategory("gory1").build(), new OperationContext()); + + // then. + assertEquals(result, res); + } + + @Test + @DisplayName("测试 create 异常") + public void testCreateException() { + // given. + when(this.appBuilderAppMapper.selectWithCondition(any())).thenReturn( + List.of(AppBuilderAppPo.builder().build())); + + // when. + AippException exception = assertThrows(AippException.class, + () -> this.appVersionService.create("app_template_1", + AppBuilderAppCreateDto.builder().appCategory("gory1").name("myApp").build(), new OperationContext())); + + // then. + assertEquals(AIPP_NAME_IS_DUPLICATE.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 createByTemplate") + public void testCreateByTemplate() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + AppBuilderConfig config = mock(AppBuilderConfig.class); + when(result.getData()).thenReturn(data); + when(result.getIcon()).thenReturn("/icon.png"); + when(result.getConfig()).thenReturn(config); + doNothing().when(result).cloneVersion(any(), anyString(), anyString(), any()); + when(config.getConfigProperties()).thenReturn(Collections.emptyList()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + this.mockSave(); + + // when. + AppTemplate appTemplate = new AppTemplate(); + appTemplate.setName("myApp"); + appTemplate.setConfigId("config_1"); + appTemplate.setFlowGraphId("graph_1"); + appTemplate.setAttributes(new HashMap<>()); + AppVersion res = this.appVersionService.createByTemplate(appTemplate, new OperationContext()); + + // then. + assertEquals("config_1", res.getData().getConfigId()); + assertEquals("graph_1", res.getData().getFlowGraphId()); + } + + @Test + @DisplayName("测试 upgrade") + public void testUpgrade() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").build(); + AppVersion result = mock(AppVersion.class); + AppBuilderConfig config = mock(AppBuilderConfig.class); + when(result.getData()).thenReturn(data); + when(result.getIcon()).thenReturn("/icon.png"); + when(result.getConfig()).thenReturn(config); + doNothing().when(result).upgrade(any(), anyString(), any()); + when(config.getConfigProperties()).thenReturn(Collections.emptyList()); + when(this.appVersionFactory.create(any(), any())).thenReturn(result); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + this.mockSave(); + + // when. + AppVersion res = this.appVersionService.upgrade("app_version_1", AppBuilderAppCreateDto.builder().build(), + new OperationContext()); + + // then. + assertEquals("app_1", res.getData().getAppSuiteId()); + } + + @Test + @DisplayName("测试 validateAppName 格式不匹配") + public void testValidateAppNameFormatNotMatch() { + // given. + // when. + AippParamException exception = assertThrows(AippParamException.class, + () -> this.appVersionService.validateAppName("my$App", new OperationContext())); + + // then. + assertEquals(APP_NAME_IS_INVALID.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 validateAppName 为空") + public void testValidateAppNameIsBlank() { + // given. + // when. + AippParamException exception = assertThrows(AippParamException.class, + () -> this.appVersionService.validateAppName(" ", new OperationContext())); + + // then. + assertEquals(APP_NAME_IS_INVALID.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 validateAppName 长度过长") + public void testValidateAppNameLengthExceeds() { + // given. + // when. + AippParamException exception = assertThrows(AippParamException.class, + () -> this.appVersionService.validateAppName("myaappppppppppppppppp", new OperationContext())); + + // then. + assertEquals(AIPP_NAME_LENGTH_OUT_OF_BOUNDS.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 getLatestCreatedByAppId") + public void testGetLatestCreatedByAppId() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").name("myApp").build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithCondition(any())).thenReturn(List.of(data)); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + + // when. + Optional versionOptional = this.appVersionService.getLatestCreatedByAppSuiteId("app_1"); + + // then. + assertTrue(versionOptional.isPresent()); + ArgumentCaptor appVersionCaptor = ArgumentCaptor.forClass(AppQueryCondition.class); + verify(this.appBuilderAppMapper, times(1)).selectWithCondition(appVersionCaptor.capture()); + AppQueryCondition condition = appVersionCaptor.getValue(); + assertEquals("create_at", condition.getOrderBy().toLowerCase(Locale.ROOT)); + assertEquals("DESC", condition.getSort().toUpperCase(Locale.ROOT)); + assertEquals(0, condition.getOffset()); + assertEquals(1, condition.getLimit()); + assertEquals("app_1", condition.getAppSuiteId()); + } + + @Test + @DisplayName("测试 pageListByTenantId") + public void testPageListByTenantId() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").name("myApp").build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectByTenantIdWithPage(any(), anyString(), anyLong(), anyInt())).thenReturn( + List.of(data)); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + + when(this.appBuilderAppMapper.countByTenantId(anyString(), any())).thenReturn(1L); + + // when. + RangedResultSet appVersionRangedResultSet = this.appVersionService.pageListByTenantId( + AppQueryCondition.builder().build(), "31f20efc7e0848deab6a6bc10fc3021e", 0, 1); + + // then. + assertFalse(appVersionRangedResultSet.isEmpty()); + assertEquals(appVersion, appVersionRangedResultSet.getResults().get(0)); + } + + @Test + @DisplayName("测试 update version已发布") + public void testUpdateVersionPublished() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().appSuiteId("app_1").name("myApp").build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + when(appVersion.isPublished()).thenReturn(true); + + // when. + AippException exception = assertThrows(AippException.class, + () -> this.appVersionService.update("app_version_1", AppBuilderAppDto.builder().build(), + new OperationContext())); + + // then. + assertEquals(APP_HAS_ALREADY.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 update version") + public void testUpdateVersion() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder() + .appSuiteId("app_1") + .name("myApp") + .state(AppState.IMPORTING.getName()) + .build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + when(appVersion.getIcon()).thenReturn("icon1").thenReturn("icon2"); + doNothing().when(appVersion).putAttributes(any()); + + doNothing().when(this.appBuilderAppMapper).updateOne(any()); + doNothing().when(this.uploadedFileManageService).changeRemovable(anyString(), anyInt()); + doNothing().when(this.appBuilderAppMapper).updateOne(any()); + + // when. + OperationContext context = new OperationContext(); + context.setOperator("zy"); + this.appVersionService.update("app_version_1", AppBuilderAppDto.builder() + .name("myApp") + .type(AppTypeEnum.APP.code()) + .appType(NORMAL.name()) + .state(AppState.INACTIVE.getName()) + .version("1.0.0") + .build(), context); + + // then. + assertEquals("zy", data.getUpdateBy()); + assertEquals(AppTypeEnum.APP.code(), data.getType()); + assertEquals(NORMAL.name(), data.getAppType()); + assertEquals(AppState.INACTIVE.getName(), data.getState()); + assertEquals("1.0.0", data.getVersion()); + verify(this.uploadedFileManageService, times(2)).changeRemovable(anyString(), anyInt()); + } + + @Test + @DisplayName("测试 update 通过graph") + public void testUpdateByGraph() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.getData()).thenReturn(data); + when(appVersion.isPublished()).thenReturn(false); + doNothing().when(appVersion).putAttributes(any()); + + AppBuilderFlowGraph graph = AppBuilderFlowGraph.builder().build(); + when(appVersion.getFlowGraph()).thenReturn(graph); + + AppBuilderConfig config = mock(AppBuilderConfig.class); + when(appVersion.getConfig()).thenReturn(config); + doNothing().when(config).updateByAppearance(anyString()); + + doNothing().when(this.flowGraphRepository).updateOne(any()); + doNothing().when(this.configRepository).updateOne(any()); + doNothing().when(this.appBuilderAppMapper).updateOne(any()); + + // when. + OperationContext context = new OperationContext(); + context.setOperator("zy"); + this.appVersionService.update("app_version_1", + AppBuilderFlowGraphDto.builder().name("myApp").appearance(new HashMap<>()).build(), context); + + // then. + assertEquals("zy", graph.getUpdateBy()); + assertEquals("myApp", graph.getName()); + assertEquals("{}", graph.getAppearance()); + assertEquals("zy", data.getUpdateBy()); + } + + @Test + @DisplayName("测试 update 通过graph,version已发布") + public void testUpdateByGraphAndVersionIsPublished() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.isPublished()).thenReturn(true); + + // when. + OperationContext context = new OperationContext(); + context.setOperator("zy"); + AippException exception = assertThrows(AippException.class, () -> this.appVersionService.update("app_version_1", + AppBuilderFlowGraphDto.builder().name("myApp").appearance(new HashMap<>()).build(), context)); + + // then. + assertEquals(APP_HAS_ALREADY.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试 update 通过config") + public void testUpdateByConfig() { + // given. + AppBuilderAppPo data = AppBuilderAppPo.builder().build(); + AppVersion appVersion = mock(AppVersion.class); + when(this.appBuilderAppMapper.selectWithId(anyString())).thenReturn(data); + when(this.appVersionFactory.create(any(), any())).thenReturn(appVersion); + when(appVersion.isPublished()).thenReturn(true); + when(appVersion.getData()).thenReturn(data); + + AppBuilderFlowGraph flowGraph = AppBuilderFlowGraph.builder().build(); + when(appVersion.getFlowGraph()).thenReturn(flowGraph); + + // when. + OperationContext context = new OperationContext(); + context.setOperator("zy"); + this.appVersionService.update("app_version_1", AppBuilderSaveConfigDto.builder() + .graph("graphxxx") + .input(List.of(AppBuilderConfigFormPropertyDto.builder().build())) + .build(), context); + + // then. + assertEquals("graphxxx", flowGraph.getAppearance()); + verify(this.formPropertyRepository, times(1)).updateMany(anyList()); + verify(this.flowGraphRepository, times(1)).updateOne(eq(flowGraph)); + } + + private void mockSave() { + doNothing().when(this.appBuilderAppMapper).insertOne(any()); + doNothing().when(this.appChatRepository).saveChat(any(), any()); + doNothing().when(this.uploadedFileManageService).updateRecord(anyString(), anyString(), anyInt()); + doNothing().when(this.flowGraphRepository).insertOne(any()); + doNothing().when(this.configRepository).insertOne(any()); + doNothing().when(this.configPropertyRepository).insertMore(anyList()); + doNothing().when(this.formPropertyRepository).insertMore(anyList()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionTest.java new file mode 100644 index 0000000000..3930073b52 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionTest.java @@ -0,0 +1,1470 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.appversion; + +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_HAS_PUBLISHED; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.APP_VERSION_HAS_ALREADY; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.NEW_VERSION_IS_LOWER; +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.ACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.enums.AppTypeEnum.APP; +import static modelengine.fit.jober.aipp.enums.RestartModeEnum.OVERWRITE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fel.tool.service.ToolService; +import modelengine.fit.jade.aipp.model.dto.ModelAccessInfo; +import modelengine.fit.jade.aipp.model.dto.ModelListDto; +import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; +import modelengine.fit.jade.waterflow.service.FlowDefinitionService; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.common.exception.AippParamException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.converters.impl.AppConfigToExportConfigConverter; +import modelengine.fit.jober.aipp.converters.impl.AppExportToAppPoConverter; +import modelengine.fit.jober.aipp.converters.impl.AppGraphToExportGraphConverter; +import modelengine.fit.jober.aipp.converters.impl.AppVersionToExportAppConverter; +import modelengine.fit.jober.aipp.converters.impl.AppVersionToTemplateConverter; +import modelengine.fit.jober.aipp.domain.AppBuilderConfig; +import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; +import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.AippCreateDto; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; +import modelengine.fit.jober.aipp.dto.AppBuilderFlowGraphDto; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.dto.export.AppExportApp; +import modelengine.fit.jober.aipp.dto.export.AppExportConfig; +import modelengine.fit.jober.aipp.dto.export.AppExportDto; +import modelengine.fit.jober.aipp.dto.export.AppExportFlowGraph; +import modelengine.fit.jober.aipp.dto.export.AppExportForm; +import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; +import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AppCategory; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.enums.AppStatus; +import modelengine.fit.jober.aipp.enums.AppTypeEnum; +import modelengine.fit.jober.aipp.factory.AppTemplateFactory; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.fit.jober.common.exceptions.JobberException; +import modelengine.fitframework.util.IoUtils; +import modelengine.fitframework.util.MapBuilder; +import modelengine.jade.common.globalization.LocaleService; +import modelengine.jade.knowledge.KnowledgeCenterService; +import modelengine.jade.knowledge.dto.KnowledgeDto; +import modelengine.jade.store.service.AppService; +import modelengine.jade.store.service.PluginService; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +/** + * {@link AppVersion} 的测试类。 + * + * @author 张越 + * @since 2025-02-12 + */ +public class AppVersionTest { + private AppVersionFactory factory; + private AppBuilderFormPropertyRepository formPropertyRepository; + private AppTaskService appTaskService; + private AppBuilderConfigRepository configRepository; + private AppBuilderFormRepository formRepository; + private AppBuilderConfigPropertyRepository configPropertyRepository; + private AppBuilderFlowGraphRepository flowGraphRepository; + private FlowsService flowsService; + private AppService appService; + private PluginService pluginService; + private ToolService toolService; + private AppChatRepository appChatRepository; + private AppDefinitionService appDefinitionService; + private AippLogService aippLogService; + private UploadedFileManageService uploadedFileManageService; + private AppTemplateFactory templateFactory; + private AppTaskInstanceService appTaskInstanceService; + private LocaleService localeService; + private AippModelCenter aippModelCenter; + private AppVersionRepository appVersionRepository; + private AippFlowDefinitionService aippFlowDefinitionService; + private FlowDefinitionService flowDefinitionService; + private KnowledgeCenterService knowledgeCenterService; + + @BeforeEach + public void setUp() { + this.formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); + this.appTaskService = mock(AppTaskService.class); + this.configRepository = mock(AppBuilderConfigRepository.class); + this.formRepository = mock(AppBuilderFormRepository.class); + this.configPropertyRepository = mock(AppBuilderConfigPropertyRepository.class); + this.flowGraphRepository = mock(AppBuilderFlowGraphRepository.class); + this.flowsService = mock(FlowsService.class); + this.appService = mock(AppService.class); + this.pluginService = mock(PluginService.class); + this.toolService = mock(ToolService.class); + this.appChatRepository = mock(AppChatRepository.class); + this.appDefinitionService = mock(AppDefinitionService.class); + this.aippLogService = mock(AippLogService.class); + this.uploadedFileManageService = mock(UploadedFileManageService.class); + this.templateFactory = mock(AppTemplateFactory.class); + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.localeService = mock(LocaleService.class); + this.aippModelCenter = mock(AippModelCenter.class); + this.appVersionRepository = mock(AppVersionRepository.class); + this.aippFlowDefinitionService = mock(AippFlowDefinitionService.class); + this.flowDefinitionService = mock(FlowDefinitionService.class); + this.knowledgeCenterService = mock(KnowledgeCenterService.class); + ConverterFactory converterFactory = new ConverterFactory( + List.of(new AppExportToAppPoConverter(), new AppConfigToExportConfigConverter(), + new AppGraphToExportGraphConverter(), new AppVersionToExportAppConverter(), + new AppVersionToTemplateConverter())); + this.factory = new AppVersionFactory(this.formPropertyRepository, + this.appTaskService, + this.configRepository, + this.formRepository, + this.configPropertyRepository, + this.flowGraphRepository, + this.flowsService, + this.appService, + this.pluginService, + this.toolService, + this.appChatRepository, + this.appDefinitionService, + this.aippLogService, + this.uploadedFileManageService, + this.templateFactory, + this.appTaskInstanceService, + this.localeService, + this.aippModelCenter, + converterFactory, + this.aippFlowDefinitionService, + this.flowDefinitionService, 20000, 300, this.knowledgeCenterService, "/var/share"); + } + + /** + * 基本测试. + */ + @Nested + @DisplayName("基本测试") + public class BaseTest { + @Test + @DisplayName("属性测试") + public void testAttributes() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAttributes(JsonUtils.toJsonString(MapBuilder.get() + .put("icon", "icon_1") + .put("description", "description_1") + .put("greeting", "你好啊") + .put("app_type", "写作助手") + .build())); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + // when. + // then. + assertEquals("icon_1", appVersion.getIcon()); + assertEquals("description_1", appVersion.getDescription()); + assertEquals("你好啊", appVersion.getGreeting()); + assertEquals("写作助手", appVersion.getClassification()); + } + + @Test + @DisplayName("属性bool方法") + public void testBoolFunction() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAttributes(JsonUtils.toJsonString(MapBuilder.get() + .put(AippConst.ATTR_APP_IS_UPDATE, false) + .build())); + data.setStatus(AppStatus.PUBLISHED.getName()); + data.setType(APP.code()); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + // when. + // then. + assertTrue(appVersion.isPublished()); + assertTrue(appVersion.isApp()); + assertFalse(appVersion.isUpdated()); + } + } + + /** + * 测试getBaselineCreateTime方法 + */ + @Nested + @DisplayName("测试getBaselineCreateTime方法") + public class TestGetBaselineCreateTime { + @Test + @DisplayName("测试有任务") + public void testHasTasks() { + // given. + AppBuilderAppPo data = buildPoData(); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + LocalDateTime creationTime = LocalDateTime.now(); + AppTask appTask = mock(AppTask.class); + when(AppVersionTest.this.appTaskService.getTasksByAppId(anyString(), + any(OperationContext.class))).thenReturn(List.of(appTask)); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setCreationTime(creationTime)); + + // when. + OperationContext context = new OperationContext(); + LocalDateTime baselineCreateTime = appVersion.getBaselineCreateTime(context); + + // then. + assertEquals(creationTime, baselineCreateTime); + } + } + + private AppBuilderAppPo buildPoData() { + return AppBuilderAppPo.builder().id("app_version_1").appId("app_version_1").appSuiteId("app_1").build(); + } + + /** + * 测试publish方法 + */ + @Nested + @DisplayName("测试publish方法") + public class TestPublish { + @Test + @DisplayName("测试已发布") + public void testPublishAlready() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setStatus(AppStatus.PUBLISHED.getName()); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + PublishContext context = new PublishContext(AppBuilderAppDto.builder().build(), new OperationContext()); + + // when. + AippException exception = assertThrows(AippException.class, () -> appVersion.publish(context)); + + // then. + assertEquals(APP_HAS_PUBLISHED.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试当前版本比已发布版本大") + public void testCurrentVersionIsBiggerThanPublishVersion() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setVersion("1.0.1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + PublishContext context = new PublishContext(AppBuilderAppDto.builder().version("1.0.0").build(), + new OperationContext()); + + // when. + AippParamException paramException = assertThrows(AippParamException.class, + () -> appVersion.publish(context)); + + // then. + assertEquals(NEW_VERSION_IS_LOWER.getCode(), paramException.getCode()); + } + + @Test + @DisplayName("测试发布版本已存在") + @SuppressWarnings("unchecked") + public void testPublishVersionExistsAlready() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setVersion("1.0.0"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + RangedResultSet resultSet = mock(RangedResultSet.class); + when(AppVersionTest.this.appTaskService.getTasks(any(), any())).thenReturn(resultSet); + when(resultSet.isEmpty()).thenReturn(false); + + PublishContext context = new PublishContext(AppBuilderAppDto.builder().version("1.0.1").build(), + new OperationContext()); + + // when. + AippException aippException = assertThrows(AippException.class, () -> appVersion.publish(context)); + + // then. + assertEquals(APP_VERSION_HAS_ALREADY.getCode(), aippException.getCode()); + } + + @Test + @DisplayName("测试正常发布") + @SuppressWarnings("unchecked") + public void testPublishNormally() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setVersion("1.0.0"); + data.setFlowGraphId("graph_1"); + data.setConfigId("config_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + String appearance = IoUtils.content(AppVersionTest.class, "/appearance.txt"); + + RangedResultSet resultSet = mock(RangedResultSet.class); + when(AppVersionTest.this.appTaskService.getTasks(any(), any())).thenReturn(resultSet); + when(resultSet.isEmpty()).thenReturn(true); + + this.mockGraph(); + this.mockConfig(); + FlowInfo flowInfo1 = this.mockFlows(); + when(AppVersionTest.this.appService.publishApp(any())).thenReturn("uniqueName_1"); + when(AppVersionTest.this.appTaskService.getPreviewTasks(anyString(), any())).thenReturn( + Collections.emptyList()); + when(AppVersionTest.this.appTaskService.createTask(any(), any())).thenReturn(null); + + // 发布上下文. + PublishContext context = new PublishContext(this.buildPublishData(appearance), new OperationContext()); + + // when. + appVersion.publish(context); + + // then. + this.verifyGraph(appearance); + verify(AppVersionTest.this.formPropertyRepository, times(1)).updateMany(any()); + this.verifyFlows(flowInfo1, context); + assertEquals("uniqueName_1", appVersion.getData().getUniqueName()); + this.verifyTask(); + this.verifyAppVersion(appVersion); + } + + @Test + @DisplayName("测试发布waterFlow") + @SuppressWarnings("unchecked") + public void testPublishWaterFlow() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setVersion("1.0.0"); + data.setFlowGraphId("graph_1"); + data.setConfigId("config_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + String appearance = IoUtils.content(AppVersionTest.class, "/appearance.txt"); + + RangedResultSet resultSet = mock(RangedResultSet.class); + when(AppVersionTest.this.appTaskService.getTasks(any(), any())).thenReturn(resultSet); + when(resultSet.isEmpty()).thenReturn(true); + + when(AppVersionTest.this.pluginService.addPlugin(any())).thenReturn(""); + + this.mockGraph(); + this.mockConfig(); + FlowInfo flowInfo1 = this.mockFlows(); + when(AppVersionTest.this.appTaskService.getPreviewTasks(anyString(), any())).thenReturn( + Collections.emptyList()); + when(AppVersionTest.this.appTaskService.createTask(any(), any())).thenReturn(null); + + // 发布上下文. + AppBuilderAppDto publishData = this.buildPublishData(appearance); + publishData.setType(AppCategory.WATER_FLOW.getType()); + PublishContext context = new PublishContext(publishData, new OperationContext()); + + // when. + appVersion.publish(context); + + // then. + this.verifyGraph(appearance); + verify(AppVersionTest.this.formPropertyRepository, times(1)).updateMany(any()); + this.verifyFlows(flowInfo1, context); + assertNotNull(appVersion.getData().getUniqueName()); + this.verifyTask(); + this.verifyAppVersion(appVersion); + } + + @Test + @DisplayName("测试发布waterFlow并且升级的情况") + @SuppressWarnings("unchecked") + public void testPublishWaterFlowAndUpgrade() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setVersion("1.0.0"); + data.setFlowGraphId("graph_1"); + data.setConfigId("config_1"); + data.setUniqueName("uniqueName_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + String appearance = IoUtils.content(AppVersionTest.class, "/appearance.txt"); + + RangedResultSet resultSet = mock(RangedResultSet.class); + when(AppVersionTest.this.appTaskService.getTasks(any(), any())).thenReturn(resultSet); + when(resultSet.isEmpty()).thenReturn(true); + + this.mockGraph(); + this.mockConfig(); + FlowInfo flowInfo1 = this.mockFlows(); + when(AppVersionTest.this.appTaskService.getPreviewTasks(anyString(), any())).thenReturn( + Collections.emptyList()); + when(AppVersionTest.this.appTaskService.createTask(any(), any())).thenReturn(null); + when(AppVersionTest.this.toolService.upgradeTool(any())).thenReturn("uniqueName_2"); + + // 发布上下文. + AppBuilderAppDto publishData = this.buildPublishData(appearance); + publishData.setType(AppCategory.WATER_FLOW.getType()); + PublishContext context = new PublishContext(publishData, new OperationContext()); + + // when. + appVersion.publish(context); + + // then. + this.verifyGraph(appearance); + verify(AppVersionTest.this.formPropertyRepository, times(1)).updateMany(any()); + this.verifyFlows(flowInfo1, context); + assertEquals("uniqueName_2", appVersion.getData().getUniqueName()); + this.verifyTask(); + this.verifyAppVersion(appVersion); + } + + private void mockGraph() { + when(AppVersionTest.this.flowGraphRepository.selectWithId(anyString())).thenReturn( + AppBuilderFlowGraph.builder().build()); + doNothing().when(AppVersionTest.this.flowGraphRepository).updateOne(any()); + } + + private void mockConfig() { + when(AppVersionTest.this.configRepository.selectWithId(anyString())).thenReturn( + AppBuilderConfig.builder().build()); + doNothing().when(AppVersionTest.this.configRepository).updateOne(any()); + } + + private FlowInfo mockFlows() { + FlowInfo flowInfo = mock(FlowInfo.class); + when(AppVersionTest.this.flowsService.createFlows(any(), any())).thenReturn(flowInfo); + when(flowInfo.getFlowId()).thenReturn("flow_1"); + FlowInfo flowInfo1 = mock(FlowInfo.class); + when(AppVersionTest.this.flowsService.publishFlows(anyString(), anyString(), anyString(), + any())).thenReturn(flowInfo1); + when(flowInfo1.getFlowId()).thenReturn("flow_1"); + return flowInfo1; + } + + private AppBuilderAppDto buildPublishData(String appearance) { + AppBuilderFlowGraphDto appBuilderFlowGraphDto = AppBuilderFlowGraphDto.builder() + .appearance(JsonUtils.parseObject(appearance)) + .name("graph_1") + .build(); + AppBuilderAppDto publishData = AppBuilderAppDto.builder() + .flowGraph(appBuilderFlowGraphDto) + .version("1.0.1") + .name("xxx") + .attributes(new HashMap<>()) + .type(AppCategory.APP.getType()) + .build(); + publishData.setIcon("icon_1"); + publishData.setDescription("description_1"); + publishData.setPublishedDescription("published_description_1"); + publishData.setPublishedUpdateLog("publish log"); + publishData.setConfigFormProperties(Collections.emptyList()); + return publishData; + } + + private void verifyGraph(String appearance) { + ArgumentCaptor graphCaptor = ArgumentCaptor.forClass(AppBuilderFlowGraph.class); + verify(AppVersionTest.this.flowGraphRepository, times(1)).updateOne(graphCaptor.capture()); + AppBuilderFlowGraph graph = graphCaptor.getValue(); + assertEquals("graph_1", graph.getName()); + Map json = JsonUtils.parseObject(appearance); + json.put("version", "1.0.1"); + assertEquals(JsonUtils.toJsonString(json), graph.getAppearance()); + } + + private void verifyFlows(FlowInfo flowInfo1, PublishContext context) { + verify(AppVersionTest.this.flowsService, times(1)).createFlows(anyString(), any()); + verify(AppVersionTest.this.flowsService, times(1)).publishFlows(anyString(), anyString(), anyString(), + any()); + assertEquals(flowInfo1, context.getFlowInfo()); + } + + private void verifyTask() { + ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(AppTask.class); + verify(AppVersionTest.this.appTaskService, times(1)).createTask(taskCaptor.capture(), any()); + AppTask task = taskCaptor.getValue(); + assertEquals("xxx", task.getEntity().getName()); + assertEquals(ACTIVE.getCode(), task.getEntity().getStatus()); + assertEquals("description_1", task.getEntity().getDescription()); + assertEquals("icon_1", task.getEntity().getIcon()); + assertEquals("published_description_1", task.getEntity().getPublishDescription()); + assertEquals("publish log", task.getEntity().getPublishLog()); + assertEquals("1.0.1", task.getEntity().getVersion()); + assertEquals("1.0.1", task.getEntity().getAttributeVersion()); + assertEquals(NORMAL.name(), task.getEntity().getAippType()); + assertEquals("flow_1", task.getEntity().getFlowConfigId()); + } + + private void verifyAppVersion(AppVersion appVersion) { + assertEquals(AppState.PUBLISHED.getName(), appVersion.getData().getState()); + assertEquals(AppStatus.PUBLISHED.getName(), appVersion.getData().getStatus()); + assertEquals(true, appVersion.getData().getIsActive()); + assertEquals("1.0.1", appVersion.getData().getVersion()); + assertEquals("published_description_1", appVersion.getAttributes().get("publishedDescription")); + assertEquals("publish log", appVersion.getAttributes().get("publishedUpdateLog")); + assertEquals(true, appVersion.getAttributes().get("is_update")); + assertNotNull(appVersion.getData().getPath()); + } + } + + /** + * 测试run方法 + */ + @Nested + @DisplayName("测试run方法") + public class TestRun { + @Test + @DisplayName("测试启动当前app.") + public void testStartCurrentApp() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setType(APP.code()); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + appVersion.getAttributes().put("is_update", false); + + // 构建runContext. + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + runContext.setDebug(true); + runContext.setQuestion("123"); + + AppTask appTask = mock(AppTask.class); + when(AppVersionTest.this.appTaskService.getTasksByAppId(anyString(), any())).thenReturn(List.of(appTask)); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setFlowDefinitionId("flow_definition_id_1")); + doNothing().when(appTask).run(any(), eq(null)); + + FlowInfo flowInfo = mock(FlowInfo.class); + when(AppVersionTest.this.flowsService.getFlows(anyString(), any())).thenReturn(flowInfo); + when(flowInfo.getInputParamsByName(anyString())).thenReturn( + List.of(buildInputParam("Question", "String", "问题"))); + + // when. + appVersion.run(runContext, null); + + // then. + assertNotNull(runContext.getChatId()); + assertEquals(OVERWRITE.getMode(), runContext.getRestartMode()); + verify(appTask, times(1)).run(any(), eq(null)); + } + + @Test + @DisplayName("测试启动atApp") + public void testStartAtApp() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setType(APP.code()); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + appVersion.getAttributes().put("is_update", false); + + // 构建runContext. + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + runContext.setDebug(true); + runContext.setQuestion("123"); + runContext.setAtAppId("app_version_2"); + + AppVersion atAppVersion = mock(AppVersion.class); + when(AppVersionTest.this.appVersionRepository.selectById(anyString())).thenReturn( + Optional.of(atAppVersion)); + when(atAppVersion.getData()).thenReturn(AppBuilderAppPo.builder() + .state(AppState.PUBLISHED.getName()) + .appSuiteId("app_1") + .appId("app_version_2") + .version("1.0.2") + .build()); + + doNothing().when(AppVersionTest.this.appChatRepository).saveChat(any(), any()); + + // when. + appVersion.run(runContext, null); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(RunContext.class); + verify(atAppVersion, times(1)).run(captor.capture(), any()); + RunContext atAppRunContext = captor.getValue(); + assertEquals("app_version_1", atAppRunContext.getOriginAppId()); + assertEquals("app_version_2", atAppRunContext.getAppId()); + + ArgumentCaptor chatCaptor = ArgumentCaptor.forClass(ChatCreateEntity.class); + verify(AppVersionTest.this.appChatRepository, times(1)).saveChat(chatCaptor.capture(), any()); + } + } + + private Map buildInputParam(String name, String type, String description) { + return MapBuilder.get() + .put("name", name) + .put("type", type) + .put("description", description) + .put("isRequired", true) + .put("isVisible", true) + .build(); + } + + /** + * 测试restart方法 + */ + @Nested + @DisplayName("测试restart方法") + public class TestRestart { + @Test + @DisplayName("测试正常重启") + public void testRestartNormally() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setType(APP.code()); + data.setAppId("app_version_1"); + data.setAppSuiteId("app_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + appVersion.getAttributes().put("is_update", false); + + AppTaskInstance instance = mock(AppTaskInstance.class); + when(AppVersionTest.this.appTaskInstanceService.getInstanceById(anyString(), any())).thenReturn( + Optional.of(instance)); + when(instance.getChats()).thenReturn( + List.of(QueryChatRsp.builder().appId("app_version_1").chatId("chat_1").build())); + + AppLog appLog = mock(AppLog.class); + when(instance.getLogs()).thenReturn(List.of(appLog)); + when(appLog.getLogData()).thenReturn(AippInstLog.builder() + .logData(JsonUtils.toJsonString(MapBuilder.get().put("msg", "你在哪里?").build())) + .build()); + + AppTask appTask = mock(AppTask.class); + when(AppVersionTest.this.appTaskService.getTaskList(anyString(), anyString(), anyString(), + any())).thenReturn(List.of(appTask)); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setFlowDefinitionId("flow_definition_id_1")); + doNothing().when(appTask).run(any(), eq(null)); + + FlowInfo flowInfo = mock(FlowInfo.class); + when(AppVersionTest.this.flowsService.getFlows(anyString(), any())).thenReturn(flowInfo); + when(flowInfo.getInputParamsByName(anyString())).thenReturn( + List.of(buildInputParam("Question", "String", "问题"))); + + // when. + AtomicReference contextAtomicReference = new AtomicReference<>(); + appVersion.restart(instance, new HashMap<>(), null, new OperationContext(), + contextAtomicReference::set); + + // then. + RunContext context = contextAtomicReference.get(); + assertEquals("app_version_1", context.getAppId()); + assertEquals("chat_1", context.getChatId()); + assertEquals(OVERWRITE.getMode(), context.getRestartMode()); + assertEquals("你在哪里?", context.getQuestion()); + assertNull(context.getAtChatId()); + verify(instance, times(1)).overWrite(); + } + + @Test + @DisplayName("测试重启并带有atChatId") + public void testRestartWithAtChatId() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setType(APP.code()); + data.setAppId("app_version_1"); + data.setAppSuiteId("app_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + appVersion.getAttributes().put("is_update", false); + + QueryChatRsp atChat = QueryChatRsp.builder().appId("app_version_2").chatId("chat_2").build(); + AppTaskInstance instance = mock(AppTaskInstance.class); + when(AppVersionTest.this.appTaskInstanceService.getInstanceById(anyString(), any())).thenReturn( + Optional.of(instance)); + when(instance.getChats()).thenReturn( + List.of(QueryChatRsp.builder().appId("app_version_1").chatId("chat_1").build(), atChat)); + + AppLog appLog = mock(AppLog.class); + when(instance.getLogs()).thenReturn(List.of(appLog)); + when(appLog.getLogData()).thenReturn(AippInstLog.builder() + .logData(JsonUtils.toJsonString(MapBuilder.get().put("msg", "你在哪里?").build())) + .build()); + + when(AppVersionTest.this.appChatRepository.getChatById(anyString(), anyString())).thenReturn( + Optional.of(atChat)); + + AppVersion atAppVersion = mock(AppVersion.class); + when(AppVersionTest.this.appVersionRepository.selectById(anyString())).thenReturn( + Optional.of(atAppVersion)); + when(atAppVersion.getData()).thenReturn(AppBuilderAppPo.builder() + .state(AppState.PUBLISHED.getName()) + .appSuiteId("app_1") + .appId("app_version_2") + .version("1.0.2") + .build()); + + doNothing().when(AppVersionTest.this.appChatRepository).saveChat(any(), any()); + + // when. + AtomicReference contextAtomicReference = new AtomicReference<>(); + OperationContext operationContext = new OperationContext(); + operationContext.setAccount("00xxxxx"); + appVersion.restart(instance, new HashMap<>(), null, operationContext, contextAtomicReference::set); + + // then. + RunContext context = contextAtomicReference.get(); + assertEquals("chat_2", context.getAtChatId()); + } + } + + /** + * 测试onCreate方法 + */ + @Nested + @DisplayName("测试onCreate方法") + public class TestOnCreate { + @Test + @DisplayName("测试创建,graphId为空.") + public void testWhenFlowGraphIdIsNull() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + // when. + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> appVersion.cloneVersion(null, "1.0.1", AppTypeEnum.APP.name(), new OperationContext())); + + // then. + assertEquals("App flow graph id can not be null.", exception.getMessage()); + } + + @Test + @DisplayName("测试创建,configId为空.") + public void testConfigIdIsNull() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setFlowGraphId("flow_graph_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + AppBuilderFlowGraph flowGraph = mock(AppBuilderFlowGraph.class); + when(AppVersionTest.this.flowGraphRepository.selectWithId(anyString())).thenReturn(flowGraph); + when(AppVersionTest.this.aippModelCenter.fetchModelList(anyString(), + anyString(), + any())).thenReturn(mockModelList()); + doNothing().when(flowGraph).setModelInfo(any()); + doNothing().when(flowGraph).clone(any()); + mockKnowledge(); + + // when. + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> appVersion.cloneVersion(null, "1.0.1", AppTypeEnum.APP.name(), new OperationContext())); + + // then. + assertEquals("App config id can not be null.", exception.getMessage()); + } + + @Test + @DisplayName("测试创建,dto为空.") + public void testDtoIsNull() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setFlowGraphId("flow_graph_1"); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setConfigId("app_config_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + mockGraph(); + mockConfig(); + mockKnowledge(); + + AppBuilderFormProperty formProperty = AppBuilderFormProperty.builder().build(); + when(AppVersionTest.this.formPropertyRepository.selectWithAppId(anyString())).thenReturn( + List.of(formProperty)); + + mockPreview(); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setTenantId("model-engine"); + operationContext.setOperator("zy z00xxxxx"); + appVersion.cloneVersion(null, "1.0.1", AppTypeEnum.APP.name(), operationContext); + + // then. + assertEquals("flow_graph_1", appVersion.getData().getFlowGraphId()); + assertEquals("app_config_1", appVersion.getData().getConfigId()); + assertEquals(AppTypeEnum.APP.name(), appVersion.getData().getType()); + assertEquals("model-engine", appVersion.getData().getTenantId()); + assertEquals("zy z00xxxxx", appVersion.getData().getCreateBy()); + assertEquals("zy z00xxxxx", appVersion.getData().getUpdateBy()); + assertNotEquals("app_version_1", appVersion.getData().getId()); + assertNotEquals("app_version_1", appVersion.getData().getAppId()); + assertEquals(false, appVersion.getData().getIsActive()); + assertEquals(AppStatus.DRAFT.getName(), appVersion.getData().getStatus()); + assertEquals("1.0.1", appVersion.getData().getVersion()); + assertEquals("app_1", appVersion.getData().getAppSuiteId()); + } + + @Test + @DisplayName("测试创建,dto不为空.") + public void testDtoIsNotNull() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setFlowGraphId("flow_graph_1"); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setConfigId("app_config_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + mockKnowledge(); + mockGraph(); + mockConfig(); + + AppBuilderFormProperty formProperty = AppBuilderFormProperty.builder().build(); + when(AppVersionTest.this.formPropertyRepository.selectWithAppId(anyString())).thenReturn( + List.of(formProperty)); + + mockPreview(); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setTenantId("model-engine"); + operationContext.setOperator("zy z00xxxxx"); + appVersion.cloneVersion(this.buildAppCreateDto(), "1.0.1", AppTypeEnum.APP.name(), operationContext); + + // then. + assertEquals("这是一个描述", appVersion.getDescription()); + assertEquals("/icon.png", appVersion.getIcon()); + assertEquals("你好啊", appVersion.getGreeting()); + assertEquals(NORMAL.name(), appVersion.getData().getAppType()); + assertEquals("uniqueName_1", appVersion.getData().getUniqueName()); + assertEquals("uniqueName_1", appVersion.getAttributes().get("store_id")); + assertEquals("name_1", appVersion.getData().getName()); + assertEquals(AppTypeEnum.TEMPLATE.name(), appVersion.getData().getType()); + assertEquals(AppCategory.APP.getCategory(), appVersion.getData().getAppCategory()); + assertEquals("app", appVersion.getData().getAppBuiltType()); + } + + private AppBuilderAppCreateDto buildAppCreateDto() { + return AppBuilderAppCreateDto.builder() + .description("这是一个描述") + .icon("/icon.png") + .greeting("你好啊") + .appType(NORMAL.name()) + .storeId("uniqueName_1") + .name("name_1") + .type(AppTypeEnum.TEMPLATE.name()) + .appCategory(AppCategory.APP.getCategory()) + .appBuiltType("app") + .build(); + } + } + + private void mockKnowledge() { + KnowledgeDto knowledgeDto = KnowledgeDto.builder() + .description("description1") + .groupId("group_id1") + .name("name1") + .build(); + List knowledgeDtos = Collections.singletonList(knowledgeDto); + when(AppVersionTest.this.knowledgeCenterService.getSupportKnowledges(any())).thenReturn(knowledgeDtos); + } + + private void mockPreview() { + AppTask appTask = mock(AppTask.class); + when(AppVersionTest.this.appTaskService.getTasksByAppId(anyString(), any())).thenReturn(List.of(appTask)); + when(appTask.isPublished()).thenReturn(true); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setAppSuiteId("app_1").setVersion("1.0.1")); + } + + private void mockConfig() { + AppBuilderConfig config = mock(AppBuilderConfig.class); + when(AppVersionTest.this.configRepository.selectWithId(anyString())).thenReturn(config); + doNothing().when(config).clone(any(), any()); + when(config.getId()).thenReturn("app_config_1"); + } + + private void mockGraph() throws IOException { + String appearance = IoUtils.content(AppVersionTest.class, "/appearance.txt"); + AppBuilderFlowGraph flowGraph = mock(AppBuilderFlowGraph.class); + when(AppVersionTest.this.flowGraphRepository.selectWithId(anyString())).thenReturn(flowGraph); + when(AppVersionTest.this.aippModelCenter.fetchModelList(anyString(), + anyString(), + any())).thenReturn(this.mockModelList()); + doNothing().when(flowGraph).setModelInfo(any()); + doNothing().when(flowGraph).clone(any()); + when(flowGraph.getId()).thenReturn("flow_graph_1"); + when(flowGraph.getAppearance()).thenReturn(appearance); + } + + private ModelListDto mockModelList() { + return ModelListDto.builder() + .models(List.of(ModelAccessInfo.builder().serviceName("model_service_1").tag("inner").build())) + .total(1) + .build(); + } + + /** + * 测试publishTemplate方法 + */ + @Nested + @DisplayName("测试publishTemplate方法") + public class TestPublishTemplate { + @Test + @DisplayName("测试成功") + public void testSuccess() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setFlowGraphId("flow_graph_1"); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setConfigId("app_config_1"); + data.setAppCategory(AppCategory.APP.getCategory()); + data.setAttributes(JsonUtils.toJsonString(MapBuilder.get() + .put("icon", "/icon.png") + .put("description", "这是一个描述") + .build())); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + AppBuilderFlowGraph flowGraph = AppBuilderFlowGraph.builder().build(); + when(AppVersionTest.this.flowGraphRepository.selectWithId(anyString())).thenReturn(flowGraph); + + when(AppVersionTest.this.uploadedFileManageService.copyIconFiles(anyString(), anyString(), + anyString())).thenReturn("./copiedIcon.png"); + + doNothing().when(AppVersionTest.this.uploadedFileManageService) + .updateRecord(anyString(), anyString(), anyInt()); + + mockConfig(); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setOperator("zy z00xxxx"); + operationContext.setAccount("z00xxxx"); + TemplateInfoDto templateInfoDto = appVersion.publishTemplate(TemplateAppCreateDto.builder() + .name("模板1") + .appType(NORMAL.name()) + .description("description_2") + .icon("/icon.png") + .build(), operationContext); + + // then. + assertNotEquals("app_version_1", templateInfoDto.getId()); + assertEquals("模板1", templateInfoDto.getName()); + assertEquals(AppCategory.APP.getCategory(), templateInfoDto.getCategory()); + assertEquals("description_2", templateInfoDto.getDescription()); + assertEquals(NORMAL.name(), templateInfoDto.getAppType()); + assertEquals("./copiedIcon.png", templateInfoDto.getIcon()); + assertEquals("zy z00xxxx", templateInfoDto.getCreator()); + } + } + + /** + * 测试publishTemplate方法 + */ + @Nested + @DisplayName("测试publishTemplate方法") + public class TestUpgrade { + @Test + @DisplayName("测试成功") + public void testSuccess() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setFlowGraphId("flow_graph_1"); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setConfigId("app_config_1"); + data.setVersion("1.0.0"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + mockKnowledge(); + mockGraph(); + mockConfig(); + mockModelList(); + mockPreview(); + + // when. + OperationContext operationContext = new OperationContext(); + appVersion.upgrade(AppBuilderAppCreateDto.builder().build(), APP.code(), operationContext); + + // then. + assertEquals("1.0.0", appVersion.getAttributes().get("latest_version")); + assertEquals("1.0.1", appVersion.getData().getVersion()); + assertEquals(AppState.INACTIVE.getName(), appVersion.getData().getState()); + assertEquals(false, appVersion.getData().getIsActive()); + assertEquals(AppStatus.DRAFT.getName(), appVersion.getData().getStatus()); + } + } + + /** + * 测试导入 + */ + @Nested + @DisplayName("测试导入") + public class TestImport { + @Test + @DisplayName("测试版本不匹配,报错") + public void testException() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(AppVersionTest.this.appVersionRepository.selectWithSimilarName(anyString())).thenReturn( + Collections.emptyList()); + + mockPreview(); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setTenantId("31f20efc7e0848deab6a6bc10fc30111"); + AppExportDto dto = this.buildExportDto(); + dto.setVersion("1.0.0"); + Map exportMeta = MapBuilder.get().put("version", "1.0.1").build(); + AippException exception = assertThrows(AippException.class, + () -> appVersion.importData(dto, "app_1", "", operationContext, exportMeta)); + + // then. + assertEquals(AippErrCode.IMPORT_CONFIG_UNMATCHED_VERSION.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试成功场景") + public void testSuccess() throws IOException { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(AppVersionTest.this.appVersionRepository.selectWithSimilarName(anyString())).thenReturn( + Collections.emptyList()); + + mockPreview(); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setTenantId("31f20efc7e0848deab6a6bc10fc30111"); + AppExportDto dto = this.buildExportDto(); + Map exportMeta = MapBuilder.get().put("version", "1.0.1").build(); + mockKnowledge(); + appVersion.importData(dto, "app_1", "", operationContext, exportMeta); + + // then. + assertEquals("weather", appVersion.getData().getName()); + assertEquals("app", appVersion.getData().getType()); + assertEquals("basic", appVersion.getData().getAppBuiltType()); + assertEquals("1.0.0", appVersion.getData().getVersion()); + assertEquals("chatbot", appVersion.getData().getAppCategory()); + assertEquals("31f20efc7e0848deab6a6bc10fc30111", appVersion.getData().getTenantId()); + assertEquals(AppTypeEnum.TEMPLATE.code(), appVersion.getData().getAppType()); + assertEquals("app_1", appVersion.getData().getAppSuiteId()); + assertEquals(AppState.IMPORTING.getName(), appVersion.getData().getState()); + assertEquals("31f20efc7e0848deab6a6bc10fc30111", appVersion.getConfig().getTenantId()); + assertEquals("flow_1", appVersion.getFlowGraph().getName()); + assertTrue(appVersion.getFormProperties().isEmpty()); + } + + private AppExportDto buildExportDto() throws IOException { + String appearance = IoUtils.content(AppVersionTest.class, "/appearance.txt"); + return AppExportDto.builder() + .version("1.0.1") + .app(AppExportApp.builder() + .name("weather") + .tenantId("31f20efc7e0848deab6a6bc10fc3021e") + .type("app") + .appBuiltType("basic") + .appCategory("chatbot") + .appType(AppTypeEnum.TEMPLATE.code()) + .version("1.0.1") + .build()) + .config(AppExportConfig.builder() + .form(AppExportForm.builder() + .id("form_1") + .appearance(JsonUtils.parseObject(appearance)) + .type("component") + .formSuiteId("form_suite_1") + .version("1.0.1") + .build()) + .configProperties(Collections.emptyList()) + .build()) + .flowGraph(AppExportFlowGraph.builder().name("flow_1").appearance(appearance).build()) + .build(); + } + } + + /** + * 测试导出 + */ + @Nested + @DisplayName("测试导出") + public class TestExport { + @Test + @DisplayName("测试创建者和操作人不一致,抛出异常") + public void testExceptionWhenCreateUserIsNotOperator() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setCreateBy("wla"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setOperator("zy"); + Map exportMeta = MapBuilder.get().put("version", "1.0.1").build(); + AippException exception = assertThrows(AippException.class, + () -> appVersion.export(operationContext, exportMeta)); + + // then. + assertEquals(AippErrCode.EXPORT_CONFIG_UNAUTHED.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试成功场景") + public void testSuccess() { + // given. + AppBuilderAppPo data = this.buildData(); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + AppBuilderConfig appBuilderConfig = mock(AppBuilderConfig.class); + when(AppVersionTest.this.configRepository.selectWithId(anyString())).thenReturn(appBuilderConfig); + when(appBuilderConfig.getConfigProperties()).thenReturn(Collections.emptyList()); + when(appBuilderConfig.getForm()).thenReturn(this.buildForm()); + + AppBuilderFlowGraph graph = mock(AppBuilderFlowGraph.class); + when(AppVersionTest.this.flowGraphRepository.selectWithId(anyString())).thenReturn(graph); + when(graph.getName()).thenReturn("mock_graph_1"); + when(AppVersionTest.this.aippFlowDefinitionService.getParsedGraphData(anyString(), anyString())).thenReturn("testFlowDefinition"); + doNothing().when(AppVersionTest.this.flowDefinitionService).validateDefinitionData(anyString()); + when(graph.getAppearance()).thenReturn("xxxxxxxx"); + + // when. + OperationContext operationContext = new OperationContext(); + operationContext.setName("zy"); + Map exportMeta = MapBuilder.get().put("version", "1.0.1").build(); + AppExportDto exportDto = appVersion.export(operationContext, exportMeta); + + // then. + assertEquals("1.0.1", exportDto.getVersion()); + assertEquals("gameLand", exportDto.getApp().getName()); + assertEquals("31f20efc7e0848deab6a6bc10fc30111", exportDto.getApp().getTenantId()); + assertEquals("app", exportDto.getApp().getType()); + assertEquals("basic", exportDto.getApp().getAppBuiltType()); + assertEquals("1.0.1", exportDto.getApp().getVersion()); + assertEquals("chatbot", exportDto.getApp().getAppCategory()); + assertEquals(AppTypeEnum.TEMPLATE.code(), exportDto.getApp().getAppType()); + + assertEquals("form_1", exportDto.getConfig().getForm().getId()); + assertEquals("smartForm", exportDto.getConfig().getForm().getName()); + assertEquals("component", exportDto.getConfig().getForm().getType()); + assertEquals("form_suite_1", exportDto.getConfig().getForm().getFormSuiteId()); + assertEquals("1.0.1_form", exportDto.getConfig().getForm().getVersion()); + assertTrue(exportDto.getConfig().getForm().getAppearance().isEmpty()); + + assertEquals("mock_graph_1", exportDto.getFlowGraph().getName()); + assertEquals("xxxxxxxx", exportDto.getFlowGraph().getAppearance()); + } + + private AppBuilderForm buildForm() { + return AppBuilderForm.builder() + .id("form_1") + .name("smartForm") + .appearance(new HashMap<>()) + .type("component") + .formSuiteId("form_suite_1") + .version("1.0.1_form") + .build(); + } + + private AppBuilderAppPo buildData() { + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setId("app_version_1"); + data.setAppId("app_version_1"); + data.setCreateBy("zy"); + data.setName("gameLand"); + data.setTenantId("31f20efc7e0848deab6a6bc10fc30111"); + data.setType("app"); + data.setAppBuiltType("basic"); + data.setVersion("1.0.1"); + data.setAppCategory("chatbot"); + data.setAttributes(JsonUtils.toJsonString(new HashMap<>())); + data.setAppType(AppTypeEnum.TEMPLATE.code()); + data.setConfigId("config_1"); + data.setFlowGraphId("graph_1"); + return data; + } + } + + /** + * 测试preview + */ + @Nested + @DisplayName("测试preview") + public class TestPreview { + @Test + @DisplayName("应用最新的任务是已发布状态") + public void testLatestTaskIsPublished() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + AppTask appTask = mock(AppTask.class); + when(AppVersionTest.this.appTaskService.getTasksByAppId(anyString(), any())).thenReturn(List.of(appTask)); + when(appTask.isPublished()).thenReturn(true); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setAppSuiteId("app_1").setVersion("1.0.0")); + + // when. + AippCreateDto aippCreateDto = appVersion.preview("1.0.0", null, new OperationContext()); + + // then. + assertEquals("app_1", aippCreateDto.getAippId()); + assertEquals("1.0.0", aippCreateDto.getVersion()); + } + + @Test + @DisplayName("存在flowDefinition相同的task") + public void testHasSameDefinitionTask() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + AppTask appTask = mock(AppTask.class); + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(new FlowDefinitionResult()); + when(appTaskService.getTasks(any(), any())).thenReturn(RangedResultSet.create(List.of(appTask), 0, 1, 1)); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setAppSuiteId("app_2").setVersion("1.0.1")); + + // when. + AippCreateDto aippCreateDto = appVersion.preview("1.0.0", new AippDto(), new OperationContext()); + + // then. + assertEquals("app_2", aippCreateDto.getAippId()); + assertEquals("1.0.1", aippCreateDto.getVersion()); + } + + @Test + @DisplayName("baseLineVersion是预览版本") + public void testBaseLineVersionIsPreview() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + // when. + AippParamException exception = assertThrows(AippParamException.class, + () -> appVersion.preview("1.0.0-temp", new AippDto(), new OperationContext())); + + // then. + assertEquals(AippErrCode.INPUT_PARAM_IS_INVALID.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试成功") + public void testSuccess() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + FlowInfo flowInfo = mock(FlowInfo.class); + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenReturn( + flowInfo); + + AppTask task = mock(AppTask.class); + when(appTaskService.createTask(any(), any())).thenReturn(task); + when(task.getEntity()).thenReturn(AppTask.asEntity().setAppSuiteId("app_3")); + + // when. + AippCreateDto result = appVersion.preview("1.0.0", aippDto, new OperationContext()); + + // then. + assertEquals("app_3", result.getAippId()); + assertTrue(result.getVersion().contains("-")); + } + + @Test + @DisplayName("测试创建task抛出异常") + public void testRetryException() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenThrow( + new JobberException(ErrorCodes.FLOW_ALREADY_EXIST)); + + // when. + AippException exception = assertThrows(AippException.class, + () -> appVersion.preview("1.0.0", aippDto, new OperationContext())); + + // then. + assertEquals(AippErrCode.INVALID_FLOW_CONFIG.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试流程节点数量异常") + public void testInvalidFlowNodeSizeException() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenThrow( + new JobberException(ErrorCodes.INVALID_FLOW_NODE_SIZE)); + + // when. + AippException exception = assertThrows(AippException.class, + () -> appVersion.preview("1.0.0", aippDto, new OperationContext())); + + // then. + assertEquals(AippErrCode.INVALID_FLOW_NODE_SIZE.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试流程开始节点事件异常") + public void testInvalidStartNodeEventException() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenThrow( + new JobberException(ErrorCodes.INVALID_START_NODE_EVENT_SIZE)); + + // when. + AippException exception = assertThrows(AippException.class, + () -> appVersion.preview("1.0.0", aippDto, new OperationContext())); + + // then. + assertEquals(AippErrCode.INVALID_START_NODE_EVENT_SIZE.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试流程事件异常") + public void testInvalidEventConfigException() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenThrow( + new JobberException(ErrorCodes.INVALID_EVENT_CONFIG)); + + // when. + AippException exception = assertThrows(AippException.class, + () -> appVersion.preview("1.0.0", aippDto, new OperationContext())); + + // then. + assertEquals(AippErrCode.INVALID_EVENT_CONFIG.getCode(), exception.getCode()); + } + + @Test + @DisplayName("测试State类型节点事件异常") + public void testInvalidStateNodeEventConfigException() { + // given. + AppBuilderAppPo data = new AppBuilderAppPo(); + data.setAppId("app_version_1"); + AppVersion appVersion = AppVersionTest.this.factory.create(data, AppVersionTest.this.appVersionRepository); + + when(appTaskService.getTasksByAppId(anyString(), any())).thenReturn(Collections.emptyList()); + when(appDefinitionService.getSameFlowDefinition(any())).thenReturn(null); + + AippDto aippDto = new AippDto(); + aippDto.setAppId("app_version_1"); + aippDto.setFlowViewData( + MapBuilder.get().put(AippConst.FLOW_CONFIG_ID_KEY, "flow_1").build()); + + when(flowsService.publishFlowsWithoutElsa(anyString(), anyString(), anyString(), any())).thenThrow( + new JobberException(ErrorCodes.INVALID_STATE_NODE_EVENT_SIZE)); + + // when. + AippException exception = assertThrows(AippException.class, + () -> appVersion.preview("1.0.0", aippDto, new OperationContext())); + + // then. + assertEquals(AippErrCode.INVALID_EVENT_CONFIG.getCode(), exception.getCode()); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/chat/AppChatRepositoryTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/chat/AppChatRepositoryTest.java new file mode 100644 index 0000000000..197ef0d730 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/chat/AppChatRepositoryTest.java @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.chat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.chat.repository.impl.AppChatRepositoryImpl; +import modelengine.fit.jober.aipp.dto.chat.ChatCreateEntity; +import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.service.DatabaseBaseTest; + +import modelengine.fitframework.annotation.Fit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * {@link AppChatRepository} 的测试类。 + * + * @author 孙怡菲 + * @since 2025-02-20 + */ +public class AppChatRepositoryTest extends DatabaseBaseTest { + @Fit + private final AippChatMapper aippChatMapper = sqlSessionManager.openSession(true).getMapper(AippChatMapper.class); + + private AppChatRepository appChatRepository; + + @BeforeEach + void setUp() { + this.appChatRepository = new AppChatRepositoryImpl(this.aippChatMapper); + } + + @Test + @DisplayName("测试查询") + void TestGetChatById() { + String chatId = "003f0cd8dcfb4aca88af34d8f85750d2"; + String userId = "tester 12345678"; + String appId = "ebc5afee8bd94c5eb5d36da049396864"; + + Optional result = this.appChatRepository.getChatById(chatId, userId); + + Assertions.assertEquals(appId, result.get().getAppId()); + } + + @Test + @DisplayName("测试保存chat") + void TestSaveChat() { + Map attributes = new HashMap<>(); + attributes.put("test", "123"); + ChatCreateEntity mockChatEntity = ChatCreateEntity.builder() + .appId("appId") + .appVersion("1.0.0") + .attributes(attributes) + .chatName("chat") + .chatId("chatId") + .taskInstanceId("instanceId") + .build(); + OperationContext context = new OperationContext(); + context.setOperator("test1"); + + AippChatMapper aippChatMapperMock = mock(AippChatMapper.class); + AppChatRepository appChatRepositoryMock = new AppChatRepositoryImpl(aippChatMapperMock); + + appChatRepositoryMock.saveChat(mockChatEntity, context); + + verify(aippChatMapperMock, times(1)).insertChat(any()); + verify(aippChatMapperMock, times(1)).insertWideRelationship(any()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/definition/AppDefinitionServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/definition/AppDefinitionServiceTest.java new file mode 100644 index 0000000000..ad6d53d4b9 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/definition/AppDefinitionServiceTest.java @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.definition; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import modelengine.fit.jade.waterflow.AippFlowDefinitionService; +import modelengine.fit.jade.waterflow.entity.FlowDefinitionResult; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.definition.service.AppDefinitionService; +import modelengine.fit.jober.aipp.domains.definition.service.impl.AppDefinitionServiceImpl; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fitframework.annotation.Fit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link AppDefinitionService} 的测试类。 + * + * @author 孙怡菲 + * @since 2025-02-20 + */ +@ExtendWith(MockitoExtension.class) +class AppDefinitionServiceTest { + @Mock + private AippFlowDefinitionService flowDefinitionService; + + @Fit + private AppDefinitionService appDefinitionService; + + @BeforeEach + void setUp() { + this.appDefinitionService = new AppDefinitionServiceImpl(flowDefinitionService); + } + + @Test + @DisplayName("测试成功获取相同流程定义") + public void TestGetSameDef() { + String metaId = "id1"; + AippDto mockDto = mockAippDto(); + String mockData = "{\"metaId\":\"id1\"}"; + FlowDefinitionResult mockDef = new FlowDefinitionResult(); + mockDef.setGraph(mockData); + mockDef.setMetaId(metaId); + when(flowDefinitionService.getFlowDefinitionByMetaIdAndPartVersion(any(), any(), any())).thenReturn( + Collections.singletonList(mockDef)); + when(flowDefinitionService.getParsedGraphData(any(), any())).thenReturn(mockData); + + FlowDefinitionResult result = this.appDefinitionService.getSameFlowDefinition(mockDto); + + Assertions.assertEquals(metaId, result.getMetaId()); + } + + @Test + @DisplayName("测试没有获取到相同流程定义") + public void TestNotGetSameDef() { + AippDto mockDto = mockAippDto(); + String mockData = "{\"metaId\":\"id1\"}"; + String mockData1 = "{\"metaId\":\"id2\"}"; + FlowDefinitionResult mockDef = new FlowDefinitionResult(); + mockDef.setGraph(mockData1); + mockDef.setMetaId("id1"); + when(flowDefinitionService.getFlowDefinitionByMetaIdAndPartVersion(any(), any(), any())).thenReturn( + Collections.singletonList(mockDef)); + when(flowDefinitionService.getParsedGraphData(any(), any())).thenReturn(mockData); + + FlowDefinitionResult result = this.appDefinitionService.getSameFlowDefinition(mockDto); + + Assertions.assertNull(result); + } + + private AippDto mockAippDto() { + Map mockViewData = new HashMap<>(); + mockViewData.put(AippConst.FLOW_CONFIG_ID_KEY, "id"); + mockViewData.put(AippConst.FLOW_CONFIG_VERSION_KEY, "1.0.0"); + return AippDto.builder() + .flowViewData(mockViewData) + .build(); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfigTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfigTest.java new file mode 100644 index 0000000000..86cee18a7b --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/jadeconfig/JadeConfigTest.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.jadeconfig; + +import modelengine.fitframework.util.IoUtils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.Optional; + +/** + * {@link JadeConfig} 的测试类。 + * + * @author 孙怡菲 + * @since 2025-02-20 + */ +class JadeConfigTest { + @Test + @DisplayName("测试获取节点信息") + public void TestJadeConfig() throws IOException { + ClassLoader classLoader = JadeConfigTest.class.getClassLoader(); + String appearance = IoUtils.content(classLoader, "appearance.txt"); + String startNodeId = "jade6qm5eg"; + Map input = Map.of("Question", ""); + + JadeConfig jadeConfig = new JadeConfig(appearance); + JadeShape startNode = jadeConfig.getShapeById(startNodeId).get(); + + Assertions.assertEquals(startNodeId, startNode.getId()); + Assertions.assertEquals(Optional.of(input), startNode.getValue("input")); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/log/AppLogTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/log/AppLogTest.java new file mode 100644 index 0000000000..282e3b439b --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/log/AppLogTest.java @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.log; + +import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Optional; + +/** + * {@link AppLog} 的测试类。 + * + * @author 孙怡菲 + * @since 2025-02-20 + */ +public class AppLogTest { + private final AppLogFactory factory = new AppLogFactory(); + + @Test + @DisplayName("测试判断日志是否为question类型") + public void TestIsQuestion() { + AppLog questionLog = this.factory.create(mockLog(AippInstLogType.QUESTION.name(), "{\"msg\": \"你好\"," + + "\"form_id\": null, \"formData\": null, \"form_args\": null, \"form_version\": null," + + " \"formAppearance\": null}")); + + AppLog msgLog = this.factory.create(mockLog(AippInstLogType.MSG.name(), "{\"msg\": \"你好\", " + + "\"form_id\": null, \"formData\": null, \"form_args\": null, \"form_version\": null," + + " \"formAppearance\": null}")); + + Assertions.assertTrue(questionLog.isQuestionType()); + Assertions.assertFalse(msgLog.isQuestionType()); + } + + @Test + @DisplayName("测试判断日志是否为某些类型") + public void TestIsType() { + AppLog questionLog = this.factory.create(mockLog(AippInstLogType.QUESTION.name(), "{\"msg\": \"你好\", " + + "\"form_id\": null, \"formData\": null, \"form_args\": null, \"form_version\": null," + + " \"formAppearance\": null}")); + + Assertions.assertTrue(questionLog.is(AippInstLogType.QUESTION)); + Assertions.assertFalse(questionLog.is(AippInstLogType.MSG, AippInstLogType.FORM)); + } + + @Test + @DisplayName("测试获输入信息") + public void TestGetInput() { + AppLog questionLog = this.factory.create(mockLog(AippInstLogType.QUESTION.name(), "{\"msg\": \"44+44\"," + + " \"infos\": {\"input\": {\"bool\": false, \"number\": null, \"Question\": \"44+44\"}}, \"form_id\": " + + "null, \"formData\": null, \"form_args\": null, \"form_version\": null, \"formAppearance\": null}")); + + Optional> inputs = questionLog.getInput(); + + Assertions.assertEquals(3, inputs.get().size()); + } + + @Test + @DisplayName("测试") + public void TestToBody() { + String logData = "{\"msg\": \"你好\", \"form_id\": null, \"formData\": null, \"form_args\": null, " + + "\"form_version\": null,\"formAppearance\": null}"; + AppLog log = this.factory.create(mockLog(AippInstLogType.QUESTION.name(), logData)); + + AippInstLogDataDto.AippInstanceLogBody logBody = log.toBody(); + + Assertions.assertEquals(AippInstLogType.QUESTION.name(), logBody.getLogType()); + Assertions.assertEquals(logData, logBody.getLogData()); + } + + private AippInstLog mockLog(String type, String logData) { + return AippInstLog.builder() + .createAt(LocalDateTime.now()) + .createUserAccount("t00123456") + .path("/id2/id1") + .logId(1L) + .aippId("aippId") + .version("1.0.0") + .aippType(AippTypeEnum.NORMAL.name()) + .instanceId("id1") + .logData(logData) + .logType(type) + .build(); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskServiceTest.java new file mode 100644 index 0000000000..722b3bd778 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskServiceTest.java @@ -0,0 +1,362 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.INACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.PREVIEW; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.meta.multiversion.MetaService; +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jane.meta.multiversion.definition.MetaDeclarationInfo; +import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.task.service.impl.AppTaskServiceImpl; +import modelengine.fit.jober.aipp.enums.JaneCategory; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.fitframework.util.MapBuilder; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * {@link AppTaskService} 的测试类。 + * + * @author 张越 + * @since 2025-01-13 + */ +public class AppTaskServiceTest { + private MetaService metaService; + private AppTaskService appTaskService; + + @BeforeEach + public void setUp() { + AppTaskFactory factory = new AppTaskFactory(null, null, null, null, null, null, null, null); + this.metaService = mock(MetaService.class); + this.appTaskService = new AppTaskServiceImpl(this.metaService, factory); + } + + @Test + @DisplayName("测试创建方法.") + public void testCreate() { + // given. + Meta meta = new Meta(); + meta.setId("app_suite_id_1"); + meta.setVersionId("task_1"); + when(this.metaService.create(any(), any())).thenReturn(meta); + + // when. + AppTask task = this.appTaskService.createTask(AppTask.asEntity().build(), new OperationContext()); + + // then. + assertEquals("app_suite_id_1", task.getEntity().getAppSuiteId()); + assertEquals("task_1", task.getEntity().getTaskId()); + } + + @Test + @DisplayName("测试修改方法.") + public void testUpdate() { + // given. + doNothing().when(this.metaService).patch(any(), any(), any()); + + // when. + this.appTaskService.updateTask( + AppTask.asEntity().setTaskId("task_1").setName("my_task").setCategory(JaneCategory.AIPP.name()).build(), + new OperationContext()); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaDeclarationInfo.class); + verify(this.metaService).patch(eq("task_1"), captor.capture(), any()); + MetaDeclarationInfo declarationInfo = captor.getValue(); + assertEquals("my_task", declarationInfo.getName().getValue()); + assertEquals(JaneCategory.AIPP.name(), declarationInfo.getCategory().getValue()); + } + + @Test + @DisplayName("测试删除方法.") + public void testDelete() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + + // when. + this.appTaskService.deleteTaskById("task_1", new OperationContext()); + + // then. + verify(this.metaService).delete(eq("task_1"), any()); + } + + @Test + @DisplayName("测试getLatestCreate方法,通过appSuiteId、aippType、status查询.") + public void testGetLatestCreate() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + meta.setId("app_suite_id_1"); + meta.setVersionId("task_1"); + meta.setVersion("1.0"); + meta.setCategory(JaneCategory.AIPP.name()); + meta.setAttributes(MapBuilder.get().put(AippConst.ATTR_AIPP_TYPE_KEY, NORMAL.name()).build()); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + Optional result = this.appTaskService.getLatestCreate("app_suite_id_1", NORMAL.name(), + INACTIVE.getCode(), new OperationContext()); + + // then. + assertTrue(result.isPresent()); + assertEquals("app_suite_id_1", result.get().getEntity().getAppSuiteId()); + assertEquals("task_1", result.get().getEntity().getTaskId()); + assertEquals("1.0", result.get().getEntity().getVersion()); + assertEquals(NORMAL.name(), result.get().getEntity().getAippType()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + + MetaFilter metaFilter = captor.getValue(); + assertEquals("app_suite_id_1", metaFilter.getMetaIds().get(0)); + assertEquals(NORMAL.name(), metaFilter.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY).get(0)); + assertEquals(INACTIVE.getCode(), metaFilter.getAttributes().get(AippConst.ATTR_META_STATUS_KEY).get(0)); + assertEquals("desc(created_at)", metaFilter.getOrderBys().get(0)); + assertEquals(JaneCategory.AIPP.name(), metaFilter.getCategories().get(0)); + } + + @Test + @DisplayName("测试getLatestCreate方法,通过appSuiteId、aippType查询.") + public void testGetLatestCreateUseAppSuiteIdAndAippType() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + meta.setId("app_suite_id_1"); + meta.setVersionId("task_1"); + meta.setVersion("1.0"); + meta.setCategory(JaneCategory.AIPP.name()); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_AIPP_TYPE_KEY, NORMAL.name()) + .put(AippConst.ATTR_META_STATUS_KEY, INACTIVE.getCode()) + .build()); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + Optional result = this.appTaskService.getLatestCreate("app_suite_id_1", NORMAL.name(), + new OperationContext()); + + // then. + assertTrue(result.isPresent()); + assertEquals("app_suite_id_1", result.get().getEntity().getAppSuiteId()); + assertEquals("task_1", result.get().getEntity().getTaskId()); + assertEquals("1.0", result.get().getEntity().getVersion()); + assertEquals(NORMAL.name(), result.get().getEntity().getAippType()); + assertEquals(INACTIVE.getCode(), result.get().getEntity().getStatus()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + + MetaFilter metaFilter = captor.getValue(); + assertEquals("app_suite_id_1", metaFilter.getMetaIds().get(0)); + assertEquals(NORMAL.name(), metaFilter.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY).get(0)); + assertEquals("desc(created_at)", metaFilter.getOrderBys().get(0)); + assertEquals(JaneCategory.AIPP.name(), metaFilter.getCategories().get(0)); + } + + @Test + @DisplayName("测试getLatest方法,通过uniqueName查询.") + public void testGetLatestUserUniqueName() { + // given. + String uniqueName = UUID.randomUUID().toString(); + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_UNIQUE_NAME, uniqueName) + .build()); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + Optional result = this.appTaskService.getLatest(uniqueName, new OperationContext()); + + // then. + assertTrue(result.isPresent()); + assertEquals(uniqueName, result.get().getEntity().getUniqueName()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals(uniqueName, metaFilter.getAttributes().get(AippConst.ATTR_UNIQUE_NAME).get(0)); + } + + @Test + @DisplayName("测试getLatest方法,通过appSuiteId、version查询.") + public void testGetLatestUseAppSuiteIdAndVersion() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + meta.setId("app_suite_id_1"); + meta.setVersion("1.0"); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + Optional result = this.appTaskService.getLatest("app_suite_id_1", "1.0", new OperationContext()); + + // then. + assertTrue(result.isPresent()); + assertEquals("app_suite_id_1", result.get().getEntity().getAppSuiteId()); + assertEquals("1.0", result.get().getEntity().getVersion()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals(JaneCategory.AIPP.name(), metaFilter.getCategories().get(0)); + assertEquals("desc(updated_at)", metaFilter.getOrderBys().get(0)); + } + + @Test + @DisplayName("测试getTaskList方法,通过appSuiteId、aippType、status查询.") + public void testGetTaskList() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + List result = this.appTaskService.getTaskList("app_suite_id_1", NORMAL.name(), INACTIVE.getCode(), + new OperationContext()); + + // then. + assertEquals(1, result.size()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService, times(1)).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals(JaneCategory.AIPP.name(), metaFilter.getCategories().get(0)); + assertEquals("desc(created_at)", metaFilter.getOrderBys().get(0)); + assertEquals(NORMAL.name(), metaFilter.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY).get(0)); + assertEquals(INACTIVE.getCode(), metaFilter.getAttributes().get(AippConst.ATTR_META_STATUS_KEY).get(0)); + } + + @Test + @DisplayName("测试getTaskList方法,通过query查询.") + public void testGetTaskListUseQuery() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + List result = this.appTaskService.getTaskList(AppTask.asQueryEntity(0L, 1).latest().build(), + new OperationContext()); + + // then. + assertEquals(1, result.size()); + verify(this.metaService, times(1)).list(any(), eq(true), eq(0L), eq(10), any()); + } + + @Test + @DisplayName("测试getPreview方.") + public void testGetPreviewTask() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + List result = this.appTaskService.getPreviewTasks("app_suite_id_1", new OperationContext()); + + // then. + assertEquals(1, result.size()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService, times(1)).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals(JaneCategory.AIPP.name(), metaFilter.getCategories().get(0)); + assertEquals("desc(updated_at)", metaFilter.getOrderBys().get(0)); + assertEquals(PREVIEW.name(), metaFilter.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY).get(0)); + } + + @Test + @DisplayName("测试getTasksByAppId方.") + public void testGetTasksByAppId() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + List result = this.appTaskService.getTasksByAppId("app_id_1", new OperationContext()); + + // then. + assertEquals(1, result.size()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService, times(1)).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals("desc(created_at)", metaFilter.getOrderBys().get(0)); + assertEquals("app_id_1", metaFilter.getAttributes().get(AippConst.ATTR_APP_ID_KEY).get(0)); + } + + @Test + @DisplayName("测试getTasksByAppId方,通过appId和aippType.") + public void testGetTasksByAppIdAndAippType() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(meta), 0, 1, 1)); + + // when. + List result = this.appTaskService.getTasksByAppId("app_id_1", NORMAL.name(), new OperationContext()); + + // then. + assertEquals(1, result.size()); + ArgumentCaptor captor = ArgumentCaptor.forClass(MetaFilter.class); + verify(this.metaService, times(1)).list(captor.capture(), anyBoolean(), anyLong(), anyInt(), any()); + MetaFilter metaFilter = captor.getValue(); + assertEquals("app_id_1", metaFilter.getAttributes().get(AippConst.ATTR_APP_ID_KEY).get(0)); + assertEquals(NORMAL.name(), metaFilter.getAttributes().get(AippConst.ATTR_AIPP_TYPE_KEY).get(0)); + } + + @Test + @DisplayName("测试getTaskById.") + public void testGetTasksById() { + // given. + doNothing().when(this.metaService).delete(any(), any()); + Meta meta = new Meta(); + meta.setId("app_suite_id_1"); + meta.setVersionId("task_1"); + when(this.metaService.retrieve(any(), any())).thenReturn(meta); + + // when. + Optional result = this.appTaskService.getTaskById("task_1", new OperationContext()); + + // then. + assertTrue(result.isPresent()); + assertEquals("app_suite_id_1", result.get().getEntity().getAppSuiteId()); + assertEquals("task_1", result.get().getEntity().getTaskId()); + verify(this.metaService, times(1)).retrieve(eq("task_1"), any()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskTest.java new file mode 100644 index 0000000000..ea73835751 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/AppTaskTest.java @@ -0,0 +1,447 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_FILE_DESC_KEY; +import static modelengine.fit.jober.aipp.constants.AippConst.RESTART_MODE; +import static modelengine.fit.jober.aipp.enums.AippInstLogType.FORM; +import static modelengine.fit.jober.aipp.enums.AippInstLogType.QUESTION; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.PREVIEW; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jade.waterflow.FlowsService; +import modelengine.fit.jade.waterflow.dto.FlowInfo; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; +import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; +import modelengine.fit.jober.aipp.enums.JaneCategory; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.enums.RestartModeEnum; +import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; +import modelengine.fit.jober.aipp.service.AopAippLogService; +import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.jober.aipp.service.AppChatSseService; +import modelengine.fit.jober.common.ErrorCodes; +import modelengine.fit.jober.common.exceptions.JobberException; +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.ObjectUtils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * {@link AppTask} 的测试类。 + * + * @author 张越 + * @since 2025-01-13 + */ +public class AppTaskTest { + private AppTaskFactory factory; + private AippLogRepository aippLogRepository; + private AppTaskInstanceService appTaskInstanceService; + private FlowsService flowsService; + private AppVersionRepository appVersionRepository; + private AopAippLogService aopAippLogService; + private AppTaskService appTaskService; + private FlowInstanceService flowInstanceService; + + @BeforeEach + public void setUp() { + this.aippLogRepository = mock(AippLogRepository.class); + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.flowsService = mock(FlowsService.class); + AppChatSessionService appChatSessionService = mock(AppChatSessionService.class); + this.flowInstanceService = mock(FlowInstanceService.class); + this.appTaskService = mock(AppTaskService.class); + this.appVersionRepository = mock(AppVersionRepository.class); + AppBuilderFormPropertyRepository appBuilderFormPropertyRepository = mock( + AppBuilderFormPropertyRepository.class); + this.aopAippLogService = mock(AopAippLogService.class); + AppChatSseService appChatSseService = mock(AppChatSseService.class); + this.factory = new AppTaskFactory(this.aippLogRepository, this.appTaskInstanceService, this.flowsService, + appChatSessionService, flowInstanceService, appBuilderFormPropertyRepository, + this.aopAippLogService, appChatSseService); + } + + @Test + @DisplayName("asCreateEntity测试") + public void testAsCreateEntity() { + AppTask task = AppTask.asCreateEntity().build(); + assertEquals(JaneCategory.AIPP.name(), task.getEntity().getCategory()); + assertEquals(AippMetaStatusEnum.INACTIVE.getCode(), task.getEntity().getStatus()); + assertEquals(AippConst.STATIC_META_ITEMS.size(), task.getEntity().getProperties().size()); + } + + @Test + @DisplayName("asUpdateEntity测试") + public void testAsUpdateEntity() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertEquals("task_id", task.getEntity().getTaskId()); + } + + @Test + @DisplayName("测试isDraft") + public void testIsDraft() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertFalse(task.isDraft()); + + task.getEntity().setBaseLineVersion("1.0"); + task.getEntity().setStatus("inactive"); + assertTrue(task.isDraft()); + } + + @Test + @DisplayName("测试isActive") + public void testIsActive() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertFalse(task.isActive()); + + task.getEntity().setStatus("active"); + assertTrue(task.isActive()); + } + + @Test + @DisplayName("测试isUpgrade") + public void testIsUpgrade() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertTrue(task.isUpgrade("1.0")); + + task.getEntity().setStatus("active"); + assertTrue(task.isUpgrade("1.0")); + + task.getEntity().setVersion("1.0"); + task.getEntity().setStatus("inactive"); + assertFalse(task.isUpgrade("1.0")); + } + + @Test + @DisplayName("测试isBelongApp") + public void testIsBelongApp() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertFalse(task.isBelongApp("app_id")); + + task.getEntity().setAppId("app_id"); + assertTrue(task.isBelongApp("app_id")); + } + + @Test + @DisplayName("测试isPublished") + public void testIsPublished() { + AppTask task = AppTask.asUpdateEntity("task_id").build(); + assertFalse(task.isPublished()); + + task.getEntity().setAippType(PREVIEW.name()); + assertFalse(task.isPublished()); + + task.getEntity().setAippType(PREVIEW.name()); + task.getEntity().setStatus(AippMetaStatusEnum.ACTIVE.getCode()); + assertFalse(task.isPublished()); + + task.getEntity().setAippType(NORMAL.name()); + task.getEntity().setStatus(AippMetaStatusEnum.INACTIVE.getCode()); + assertFalse(task.isPublished()); + + task.getEntity().setAippType(NORMAL.name()); + task.getEntity().setStatus(AippMetaStatusEnum.ACTIVE.getCode()); + assertTrue(task.isPublished()); + } + + /** + * 测试run方法 + */ + @Nested + @DisplayName("测试run方法") + public class TestRun { + @Test + @DisplayName("测试run有文件描述") + public void testRunHasFileDescriptions() { + // given. + this.mockInstance(); + this.mockFlows(); + + Map businessData = MapBuilder.get() + .put(AippConst.INST_NAME_KEY, "instance_name") + .put(RESTART_MODE, RestartModeEnum.INCREMENT.getMode()) + .put(BS_AIPP_FILE_DESC_KEY, + List.of(MapBuilder.get().put("file_url", "https://xxx.com/bb.png").build())) + .build(); + RunContext context = new RunContext(businessData, new OperationContext()); + + Meta meta = buildMeta(); + meta.setAttributes( + MapBuilder.get().put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id").build()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.run(context); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(AppTaskInstance.class); + verify(AppTaskTest.this.appTaskInstanceService, times(1)).createInstance(captor.capture(), any()); + AppTaskInstance createArg = captor.getValue(); + assertEquals("instance_name", createArg.getEntity().getName()); + + verify(AppTaskTest.this.aopAippLogService, times(1)).insertLog(any()); + verify(AppTaskTest.this.appVersionRepository, times(0)).selectById(any()); + verify(AppTaskTest.this.flowsService, times(1)).getFlows(eq("flow_definition_id"), any()); + + assertEquals("https://xxx.com/bb.png", + ObjectUtils.>cast(businessData.get(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY)).get(0)); + } + + @Test + @DisplayName("测试run无文件描述,但是isIncrementMode") + public void testRunNoFileDescriptionsButIsIncrementMode() { + // given. + this.mockInstance(); + this.mockFlows(); + + Map businessData = MapBuilder.get() + .put(AippConst.INST_NAME_KEY, "instance_name") + .put(RESTART_MODE, RestartModeEnum.INCREMENT.getMode()) + .build(); + RunContext context = new RunContext(businessData, new OperationContext()); + + Meta meta = buildMeta(); + meta.setAttributes( + MapBuilder.get().put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id").build()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.run(context); + + // then. + verify(AppTaskTest.this.aopAippLogService, times(1)).insertLog(any()); + assertNull(businessData.get(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY)); + } + + @Test + @DisplayName("测试run无文件描述,但是不是IncrementMode") + public void testRunNoFileDescriptionsButIsNotIncrementMode() { + // given. + this.mockInstance(); + this.mockFlows(); + + Map businessData = MapBuilder.get() + .put(AippConst.INST_NAME_KEY, "instance_name") + .put(RESTART_MODE, RestartModeEnum.OVERWRITE.getMode()) + .build(); + RunContext context = new RunContext(businessData, new OperationContext()); + + Meta meta = buildMeta(); + meta.setAttributes( + MapBuilder.get().put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id").build()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.run(context); + + // then. + verify(AppTaskTest.this.aopAippLogService, times(1)).insertLog(any()); + assertNull(businessData.get(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY)); + } + + @Test + @DisplayName("测试run,有formId和formVersion") + public void testRunWithFormIdAndFormVersion() { + // given. + this.mockInstance(); + this.mockFlows(); + + AppVersion appVersion = mock(AppVersion.class); + when(AppTaskTest.this.appVersionRepository.selectById(any())).thenReturn(Optional.of(appVersion)); + when(appVersion.getFormProperties()).thenReturn(List.of()); + + Map businessData = MapBuilder.get() + .put(AippConst.INST_NAME_KEY, "instance_name") + .put(RESTART_MODE, RestartModeEnum.OVERWRITE.getMode()) + .build(); + RunContext context = new RunContext(businessData, new OperationContext()); + + Meta meta = buildMeta(); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id") + .put(AippConst.ATTR_START_FORM_ID_KEY, "form_id") + .put(AippConst.ATTR_START_FORM_VERSION_KEY, "form_version") + .put(AippConst.ATTR_APP_ID_KEY, "app_id") + .build()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.run(context); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(AippLogCreateDto.class); + verify(AppTaskTest.this.aopAippLogService, times(2)).insertLog(captor.capture()); + List createDtoList = captor.getAllValues(); + assertEquals(QUESTION.name(), createDtoList.get(0).getLogType()); + assertEquals(FORM.name(), createDtoList.get(1).getLogType()); + } + + private void mockFlows() { + FlowInfo flowInfo = mock(FlowInfo.class); + when(AppTaskTest.this.flowsService.getFlows(any(), any())).thenReturn(flowInfo); + when(flowInfo.getInputParamsByName(any())).thenReturn(List.of()); + } + + private void mockInstance() { + AppTaskInstance instance = mock(AppTaskInstance.class); + when(AppTaskTest.this.appTaskInstanceService.createInstance(any(), any())).thenReturn(instance); + when(instance.getId()).thenReturn("instance_id"); + doNothing().when(instance).run(any(), any()); + when(instance.getEntity()).thenReturn(AppTaskInstance.asEntity().setFlowTraceId("flow_1")); + } + } + + private Meta buildMeta() { + Meta meta = new Meta(); + meta.setId("app_suite_id"); + meta.setVersion("1.0"); + meta.setVersionId("task_id"); + return meta; + } + + /** + * 测试cleanResource方法 + */ + @Nested + @DisplayName("测试cleanResource方法") + public class TestCleanResource { + @Test + @DisplayName("测试isActive状态时") + public void testIsActive() { + // given. + Meta meta = buildMeta(); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.ACTIVE.getCode()) + .put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, "flow_config_1") + .build()); + + AppTaskInstance instance = mock(AppTaskInstance.class); + when(AppTaskTest.this.appTaskInstanceService.getInstanceStreamByTaskId(anyString(), anyInt(), + any())).thenReturn(Stream.of(instance)); + when(instance.isRunning()).thenReturn(true); + when(instance.getEntity()).thenReturn(AppTaskInstance.asEntity().setFlowTraceId("flow_1")); + when(instance.getId()).thenReturn("instance_1"); + + doNothing().when(AppTaskTest.this.flowInstanceService).terminateFlows(any(), anyString(), any(), any()); + doNothing().when(AppTaskTest.this.appTaskInstanceService).update(any(), any()); + doNothing().when(AppTaskTest.this.aippLogRepository).deleteAippPreviewLog(anyString(), any()); + doNothing().when(AppTaskTest.this.flowsService).deleteFlowsWithoutElsa(anyString(), anyString(), any()); + doNothing().when(AppTaskTest.this.appTaskService).deleteTaskById(anyString(), any()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.cleanResource(new OperationContext()); + + // then. + verify(AppTaskTest.this.appTaskInstanceService, times(1)) + .getInstanceStreamByTaskId(eq("task_id"), eq(15), any()); + verify(AppTaskTest.this.flowInstanceService, times(1)) + .terminateFlows(eq(null), eq("flow_1"), eq(Collections.emptyMap()), any()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(AppTaskInstance.class); + verify(AppTaskTest.this.appTaskInstanceService, times(1)) + .update(captor.capture(), any()); + AppTaskInstance instance1 = captor.getValue(); + assertEquals("task_id", instance1.getTaskId()); + assertEquals("instance_1", instance1.getId()); + assertEquals(MetaInstStatusEnum.TERMINATED.name(), instance1.getEntity().getStatus().orElse(null)); + + verify(AppTaskTest.this.aippLogRepository, times(1)) + .deleteAippPreviewLog(eq("app_suite_id"), any()); + + verify(AppTaskTest.this.flowsService, times(1)) + .deleteFlowsWithoutElsa(eq("flow_config_1"), eq("1.0"), any()); + + verify(AppTaskTest.this.appTaskService, times(1)) + .deleteTaskById(eq("task_id"), any()); + } + + @Test + @DisplayName("测试isNotActive状态时") + public void testIsInActive() { + // given. + Meta meta = buildMeta(); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()) + .put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, "flow_config_1") + .build()); + + doNothing().when(AppTaskTest.this.aippLogRepository).deleteAippPreviewLog(anyString(), any()); + doNothing().when(AppTaskTest.this.flowsService).deleteFlowsWithoutElsa(anyString(), anyString(), any()); + doNothing().when(AppTaskTest.this.appTaskService).deleteTaskById(anyString(), any()); + + // when. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + appTask.cleanResource(new OperationContext()); + + // then. + verify(AppTaskTest.this.appTaskInstanceService, times(0)) + .getInstanceStreamByTaskId(anyString(), anyInt(), any()); + verify(AppTaskTest.this.flowInstanceService, times(0)) + .terminateFlows(anyString(), anyString(), eq(Collections.emptyMap()), any()); + verify(AppTaskTest.this.aippLogRepository, times(0)) + .deleteAippPreviewLog(anyString(), any()); + } + + @Test + @DisplayName("测试deleteFlows时抛出异常") + public void testDeleteFlowsThrowsException() { + // given. + Meta meta = buildMeta(); + meta.setAttributes(MapBuilder.get() + .put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()) + .put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, "flow_config_1") + .build()); + + doThrow(new JobberException(ErrorCodes.FLOW_DEFINITION_DELETE_ERROR)).when(AppTaskTest.this.flowsService) + .deleteFlowsWithoutElsa(anyString(), anyString(), any()); + doNothing().when(AppTaskTest.this.appTaskService).deleteTaskById(anyString(), any()); + + // when. + // then. + AppTask appTask = AppTaskTest.this.factory.create(meta, AppTaskTest.this.appTaskService); + assertThrows(AippException.class, () -> appTask.cleanResource(new OperationContext())); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskDecoratorTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskDecoratorTest.java new file mode 100644 index 0000000000..6fc90acf7c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskDecoratorTest.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_INST_ID_KEY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fitframework.util.MapBuilder; +import modelengine.jade.common.globalization.LocaleService; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.util.Map; + +/** + * {@link TaskDecorator} 的测试类。 + * + * @author 张越 + * @since 2025-01-13 + */ +public class TaskDecoratorTest { + private AippLogService aippLogService; + private AppTaskInstanceService appTaskInstanceService; + private LocaleService localeService; + + @BeforeEach + public void setUp() { + this.aippLogService = mock(AippLogService.class); + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.localeService = mock(LocaleService.class); + } + + @Test + @DisplayName("异常测试") + public void testException() { + // given. + AppTask appTask = Mockito.mock(AppTask.class); + doThrow(new IllegalStateException()).when(appTask).run(any()); + when(appTask.getEntity()).thenReturn(AppTask.asEntity().setTaskId("task_1")); + Map businessData = MapBuilder.get() + .put(BS_AIPP_INST_ID_KEY, "instance_1") + .build(); + + doNothing().when(this.appTaskInstanceService).update(any(), any()); + when(this.localeService.localize(any())).thenReturn("xxxxxxx"); + when(this.aippLogService.insertLog(any(), any(), any())).thenReturn("xxxxxx"); + + // when. + RunContext runContext = new RunContext(businessData, new OperationContext()); + TaskDecorator.create(appTask, this.aippLogService, this.appTaskInstanceService, this.localeService) + .exceptionLog() + .run(runContext); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(AppTaskInstance.class); + verify(this.appTaskInstanceService, times(1)).update(captor.capture(), any()); + AppTaskInstance instance = captor.getValue(); + Assertions.assertEquals("instance_1", instance.getId()); + Assertions.assertEquals("task_1", instance.getTaskId()); + Assertions.assertTrue(instance.getEntity().getStatus().isPresent()); + Assertions.assertEquals(MetaInstStatusEnum.ERROR.name(), instance.getEntity().getStatus().get()); + + verify(this.localeService, times(1)).localize(eq("aipp.service.impl.AippRunTimeServiceImpl")); + verify(this.aippLogService, times(1)).insertLog(eq(AippInstLogType.ERROR.name()), any(), any()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskEntityTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskEntityTest.java new file mode 100644 index 0000000000..da4bd0d59f --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/task/TaskEntityTest.java @@ -0,0 +1,201 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.task; + +import static modelengine.fit.jober.aipp.enums.AippMetaStatusEnum.ACTIVE; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import modelengine.fit.dynamicform.entity.FormMetaInfo; +import modelengine.fit.jane.meta.multiversion.definition.Meta; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.dto.AippCreateDto; +import modelengine.fit.jober.aipp.dto.AippDto; +import modelengine.fit.jober.aipp.dto.AippNodeForms; +import modelengine.fit.jober.aipp.enums.JaneCategory; +import modelengine.fit.jober.entity.consts.NodeTypes; +import modelengine.fit.jober.entity.task.TaskProperty; +import modelengine.fitframework.util.MapBuilder; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * {@link TaskEntity} 的测试类。 + * + * @author 张越 + * @since 2025-01-13 + */ +public class TaskEntityTest { + @Test + @DisplayName("基本测试") + public void test() { + LocalDateTime createTime = LocalDateTime.now(); + LocalDateTime lastModified = createTime.plusDays(1); + LocalDateTime publishTime = createTime.plusDays(2); + String uniqueName = UUID.randomUUID().toString(); + + TaskEntity entity = AppTask.asEntity(); + entity.setName("test_name"); + entity.setVersion("1.0"); + entity.setAppSuiteId("app_suite_id"); + entity.setCategory(JaneCategory.AIPP.name()); + entity.setAippType(NORMAL.name()); + entity.setStatus(ACTIVE.getCode()); + entity.setTaskId("task_id"); + entity.setAppId("app_id"); + entity.setCreationTime(createTime); + entity.setLastModificationTime(lastModified); + entity.setCreator("zy"); + entity.setDescription("test_description"); + entity.setIcon("http://xxx.com/a.png"); + entity.setFlowConfigId("flow_config_id"); + entity.setPublishDescription("application published"); + entity.setPublishLog("update by zy"); + entity.setUniqueName(uniqueName); + entity.setAttributeVersion("2.0"); + entity.setFlowDefinitionId("flow_definition_id"); + entity.setPublishTime(publishTime.toString()); + entity.setBaseLineVersion("3.0"); + + assertEquals("test_name", entity.getName()); + assertEquals("1.0", entity.getVersion()); + assertEquals("app_suite_id", entity.getAppSuiteId()); + assertEquals(JaneCategory.AIPP.name(), entity.getCategory()); + assertEquals(NORMAL.name(), entity.getAippType()); + assertEquals(ACTIVE.getCode(), entity.getStatus()); + assertEquals("task_id", entity.getTaskId()); + assertEquals("app_id", entity.getAppId()); + assertSame(createTime, entity.getCreationTime()); + assertSame(lastModified, entity.getLastModificationTime()); + assertEquals("zy", entity.getCreator()); + assertEquals("test_description", entity.getDescription()); + assertEquals("http://xxx.com/a.png", entity.getIcon()); + assertEquals("flow_config_id", entity.getFlowConfigId()); + assertEquals("application published", entity.getPublishDescription()); + assertEquals("update by zy", entity.getPublishLog()); + assertEquals(uniqueName, entity.getUniqueName()); + assertEquals("2.0", entity.getAttributeVersion()); + assertEquals("flow_definition_id", entity.getFlowDefinitionId()); + assertEquals(publishTime.toString(), entity.getPublishTime()); + assertEquals("3.0", entity.getBaseLineVersion()); + } + + @Test + @DisplayName("测试从AippDto中提取数据") + public void testFetchFromAippDto() { + TaskEntity entity = AppTask.asEntity(); + Map flowViewData = new HashMap<>(); + flowViewData.put("version", "1.0"); + entity.fetch(AippDto.builder() + .appId("app_id") + .name("task_name") + .version("1.0") + .description("test_description") + .icon("http://xxxx.con/aaa.png") + .flowViewData(flowViewData) + .build()); + + assertEquals("task_name", entity.getName()); + assertEquals("app_id", entity.getAppId()); + assertEquals("1.0", entity.getVersion()); + assertEquals("test_description", entity.getDescription()); + assertEquals("http://xxxx.con/aaa.png", entity.getIcon()); + } + + @Test + @DisplayName("测试从AippCreateDto中提取数据") + public void testFetchFromAippCreateDto() { + TaskEntity entity = AppTask.asEntity(); + entity.fetch(AippCreateDto.builder().version("2.0").aippId("app_suite_id").build()); + + assertEquals("app_suite_id", entity.getAppSuiteId()); + assertEquals("2.0", entity.getBaseLineVersion()); + } + + @Test + @DisplayName("测试从flowView中提取数据") + public void testFetchFromFlowView() { + TaskEntity entity = AppTask.asEntity(); + entity.fetch(MapBuilder.get() + .put(AippConst.FLOW_CONFIG_ID_KEY, "flow_config_id") + .put(AippConst.FLOW_CONFIG_VERSION_KEY, "3.0") + .build()); + + assertEquals("flow_config_id", entity.getFlowConfigId()); + assertEquals("3.0", entity.getAttributeVersion()); + } + + @Test + @DisplayName("测试从nodeForms中提取数据") + public void testFetchFromNodeForms() { + List nodeForms = new ArrayList<>(); + nodeForms.add(AippNodeForms.builder() + .type(NodeTypes.START.getType()) + .metaInfo(List.of(new FormMetaInfo("start_form", "1.0.1"))) + .build()); + nodeForms.add(AippNodeForms.builder() + .type(NodeTypes.END.getType()) + .metaInfo(List.of(new FormMetaInfo("end_form", "2.0.2"))) + .build()); + + TaskEntity entity = AppTask.asEntity(); + entity.fetch(nodeForms); + + assertEquals("start_form", entity.getStartFormId()); + assertEquals("1.0.1", entity.getStartFormVersion()); + assertEquals("end_form", entity.getEndFormId()); + assertEquals("2.0.2", entity.getEndFormVersion()); + } + + @Test + @DisplayName("测试loadFrom") + public void testLoadFrom() { + LocalDateTime creationTime = LocalDateTime.now(); + LocalDateTime lastModificationTime = creationTime.plusDays(2); + TaskProperty property = new TaskProperty(); + property.setId("property_id"); + Meta meta = new Meta(); + meta.setId("app_suite_id"); + meta.setVersionId("task_id"); + meta.setName("test_name"); + meta.setCategory(JaneCategory.AIPP.name()); + meta.setCreator("zy"); + meta.setLastModifier("wla"); + meta.setTenant("cloud"); + meta.setVersion("1.1.1"); + meta.setCreationTime(creationTime); + meta.setLastModificationTime(lastModificationTime); + meta.setAttributes( + MapBuilder.get().put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, "flow_config_id").build()); + meta.setProperties(List.of(property)); + + TaskEntity entity = AppTask.asEntity(); + entity.loadFrom(meta); + + assertEquals("app_suite_id", entity.getAppSuiteId()); + assertEquals("task_id", entity.getTaskId()); + assertEquals("test_name", entity.getName()); + assertEquals(JaneCategory.AIPP.name(), entity.getCategory()); + assertEquals("zy", entity.getCreator()); + assertEquals("wla", entity.getLastModifier()); + assertEquals("cloud", entity.getTenant()); + assertEquals("1.1.1", entity.getVersion()); + assertSame(creationTime, entity.getCreationTime()); + assertSame(lastModificationTime, entity.getLastModificationTime()); + assertEquals("flow_config_id", entity.getFlowConfigId()); + assertEquals("property_id", entity.getProperties().get(0).getId()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactoryTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactoryTest.java new file mode 100644 index 0000000000..fefd156029 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceFactoryTest.java @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jober.aipp.constants.AippConst; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * {@link AppTaskInstanceFactory} 的测试类。 + * + * @author 张越 + * @since 2025-01-12 + */ +public class AppTaskInstanceFactoryTest { + private AppTaskInstanceFactory factory; + + @BeforeEach + public void setUp() { + this.factory = new AppTaskInstanceFactory(null, null, null, null, null); + } + + @Test + @DisplayName("测试AppTaskInstance转换为declarationInfo") + public void testToDeclarationInfo() { + LocalDateTime creatTime = LocalDateTime.now(); + AppTaskInstance appTaskInstance = AppTaskInstance.asEntity() + .setName("instance1") + .setCreateTime(creatTime) + .putTags(List.of("tag1")) + .build(); + InstanceDeclarationInfo info = this.factory.toDeclarationInfo(appTaskInstance); + assertEquals(creatTime, info.getInfo().getValue().get(AippConst.INST_CREATE_TIME_KEY)); + assertEquals("instance1", info.getInfo().getValue().get(AippConst.INST_NAME_KEY)); + assertEquals(1, info.getTags().getValue().size()); + assertEquals("tag1", info.getTags().getValue().get(0)); + } + + @Test + @DisplayName("测试创建AppTaskInstance") + public void testCreate() { + Instance instance = new Instance(); + instance.setId("instance1"); + AppTaskInstance appTaskInstance = this.factory.create(instance, "taskId1", null); + assertEquals("instance1", appTaskInstance.getId()); + assertEquals("instance1", appTaskInstance.getEntity().getInstanceId()); + assertEquals("taskId1", appTaskInstance.getTaskId()); + assertEquals("taskId1", appTaskInstance.getEntity().getTaskId()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceServiceTest.java new file mode 100644 index 0000000000..5ba6f2a6cd --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceServiceTest.java @@ -0,0 +1,216 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.meta.multiversion.MetaInstanceService; +import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.impl.AppTaskInstanceServiceImpl; +import modelengine.fit.jober.common.RangedResultSet; +import modelengine.fitframework.model.support.DefaultRange; +import modelengine.fitframework.util.MapBuilder; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * {@link AppTaskInstanceService} 的测试类。 + * + * @author 张越 + * @since 2025-01-13 + */ +public class AppTaskInstanceServiceTest { + private AppTaskInstanceService appTaskInstanceService; + + private MetaInstanceService metaInstanceService; + + @BeforeEach + public void setUp() { + this.metaInstanceService = mock(MetaInstanceService.class); + AppTaskInstanceFactory factory = new AppTaskInstanceFactory(null, null, null, null, null); + this.appTaskInstanceService = new AppTaskInstanceServiceImpl(this.metaInstanceService, factory); + } + + @Test + @DisplayName("测试getInstance方法,当不存在时返回Optional.empty()") + public void testGetInstanceShouldReturnOptionalEmptyIfNotExists() { + // given. + when(this.metaInstanceService.list(any(), any(), eq(0L), eq(1), any())).thenReturn( + RangedResultSet.create(new ArrayList<>(), new DefaultRange(0, 0), 0)); + + // when. + Optional optionalAppTaskInstance = this.appTaskInstanceService.getInstance("task_id", + "task_instance_id", new OperationContext()); + + // then. + assertTrue(optionalAppTaskInstance.isEmpty()); + } + + @Test + @DisplayName("测试getInstance方法,当存在时返回Optional.present") + public void testGetInstanceShouldReturnOptionalPresentIfExists() { + // given. + Instance instance = new Instance(); + String taskInstanceId = "task_instance_id"; + instance.setId(taskInstanceId); + when(this.metaInstanceService.retrieveById(eq(taskInstanceId), any())).thenReturn(instance); + + // when. + Optional optionalAppTaskInstance = this.appTaskInstanceService.getInstance("task_id", + taskInstanceId, new OperationContext()); + + // then. + assertTrue(optionalAppTaskInstance.isPresent()); + assertEquals(taskInstanceId, optionalAppTaskInstance.get().getId()); + assertEquals("task_id", optionalAppTaskInstance.get().getTaskId()); + } + + @Test + @DisplayName("测试getInstancesByTaskId方法") + public void testGetInstancesByTaskIdShouldReturnList() { + // given. + Instance instance = new Instance(); + instance.setId("task_instance_id"); + + Instance instance1 = new Instance(); + instance1.setId("task_instance_id_1"); + + Instance instance2 = new Instance(); + instance2.setId("task_instance_id_2"); + when(this.metaInstanceService.list(eq("task_id"), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(instance, instance1), new DefaultRange(0, 2), 3)) + .thenReturn(RangedResultSet.create(List.of(instance2), new DefaultRange(0, 2), 3)); + + // when. + List instances = this.appTaskInstanceService.getInstancesByTaskId("task_id", 2, + new OperationContext()); + + // then. + assertEquals(3, instances.size()); + assertEquals("task_instance_id", instances.get(0).getId()); + assertEquals("task_instance_id_1", instances.get(1).getId()); + assertEquals("task_instance_id_2", instances.get(2).getId()); + verify(this.metaInstanceService, times(2)).list(eq("task_id"), anyLong(), anyInt(), any()); + } + + @Test + @DisplayName("测试getInstanceStreamByTaskId方法") + public void testGetInstanceStreamByTaskIdShouldReturnList() { + // given. + Instance instance = new Instance(); + instance.setId("task_instance_id"); + + Instance instance1 = new Instance(); + instance1.setId("task_instance_id_1"); + + when(this.metaInstanceService.list(eq("task_id"), anyLong(), anyInt(), any())).thenReturn( + RangedResultSet.create(List.of(instance, instance1), new DefaultRange(0, 2), 2)); + + // when. + Stream instanceStream = this.appTaskInstanceService.getInstanceStreamByTaskId("task_id", 2, + new OperationContext()); + + List instances = instanceStream.toList(); + + // then. + assertEquals(2, instances.size()); + assertEquals("task_instance_id", instances.get(0).getId()); + assertEquals("task_instance_id_1", instances.get(1).getId()); + verify(this.metaInstanceService, times(1)).list(eq("task_id"), anyLong(), anyInt(), any()); + } + + @Test + @DisplayName("测试update方法") + public void testUpdateShouldOk() { + // given. + AppTaskInstance instance = AppTaskInstance.asUpdate("task_id", "task_instance_id") + .setStatus("active") + .setName("task_name") + .build(); + + // when. + this.appTaskInstanceService.update(instance, new OperationContext()); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(InstanceDeclarationInfo.class); + verify(this.metaInstanceService, times(1)).patchMetaInstance(eq("task_id"), eq("task_instance_id"), + captor.capture(), any()); + + InstanceDeclarationInfo info = captor.getValue(); + assertEquals("task_name", info.getInfo().getValue().get(AippConst.INST_NAME_KEY)); + assertEquals("active", info.getInfo().getValue().get(AippConst.INST_STATUS_KEY)); + } + + @Test + @DisplayName("测试createInstance方法") + public void testCreateInstanceShouldOk() { + // given. + AppTaskInstance appTaskInstance = AppTaskInstance.asCreate("task_id", "zy", "task_name").build(); + Instance instance = new Instance(); + instance.setId("task_instance_id"); + instance.setInfo(MapBuilder.get() + .put(AippConst.INST_NAME_KEY, "task_name") + .put(AippConst.INST_CREATOR_KEY, "zy") + .build()); + when(this.metaInstanceService.createMetaInstance(any(), any(), any())).thenReturn(instance); + + // when. + AppTaskInstance result = this.appTaskInstanceService.createInstance(appTaskInstance, new OperationContext()); + + // then. + assertEquals("task_instance_id", result.getId()); + assertEquals("task_id", result.getTaskId()); + assertEquals("task_name", result.getEntity().getName()); + assertEquals("zy", result.getEntity().getCreator()); + } + + @Test + @DisplayName("测试delete方法") + public void testDeleteShouldOk() { + // given. + // when. + this.appTaskInstanceService.delete("task_id", "task_instance_id", new OperationContext()); + + // then. + verify(this.metaInstanceService, times(1)).deleteMetaInstance(eq("task_id"), eq("task_instance_id"), any()); + } + + @Test + @DisplayName("测试getTaskId方法") + public void testGetTaskIdShouldOk() { + // given. + when(this.metaInstanceService.getMetaVersionId(anyString())).thenReturn("task_id"); + + // when. + String taskId = this.appTaskInstanceService.getTaskId("task_instance_id"); + + // then. + assertEquals("task_id", taskId); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceTest.java new file mode 100644 index 0000000000..3ea717cd45 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/AppTaskInstanceTest.java @@ -0,0 +1,502 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static modelengine.fit.jober.aipp.constants.AippConst.BS_CHAT_ID; +import static modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance.GENERICABLE_ID; +import static modelengine.fit.jober.aipp.enums.AippTypeEnum.NORMAL; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.meta.multiversion.instance.Instance; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.business.MemoryTypeEnum; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.log.AppLog; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.dto.MemoryConfigDto; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; +import modelengine.fit.jober.aipp.mapper.AippChatMapper; +import modelengine.fit.jober.aipp.service.AppChatSseService; +import modelengine.fit.jober.entity.FlowInstanceResult; +import modelengine.fitframework.broker.client.BrokerClient; +import modelengine.fitframework.broker.client.Invoker; +import modelengine.fitframework.broker.client.Router; +import modelengine.fitframework.util.MapBuilder; +import modelengine.fitframework.util.ObjectUtils; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * {@link AppTaskInstance} 的测试类。 + * + * @author 张越 + * @since 2025-01-12 + */ +public class AppTaskInstanceTest { + private AppTaskInstanceFactory factory; + private AppTaskInstanceService appTaskInstanceService; + private FlowInstanceService flowInstanceService; + private BrokerClient client; + private AppChatSseService appChatSSEService; + private AippChatMapper aippChatMapper; + private AippLogRepository aippLogRepository; + + @BeforeEach + public void setUp() throws Exception { + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.flowInstanceService = mock(FlowInstanceService.class); + this.client = mock(BrokerClient.class); + this.appChatSSEService = mock(AppChatSseService.class); + this.aippLogRepository = mock(AippLogRepository.class); + this.aippChatMapper = mock(AippChatMapper.class); + this.factory = new AppTaskInstanceFactory(this.flowInstanceService, this.client, + this.appChatSSEService, this.aippChatMapper, this.aippLogRepository); + } + + @Test + @DisplayName("测试asCreate") + public void testAsCreate() { + TaskInstanceCreateEntity entity = AppTaskInstance.asCreate("taskId", "zy", "name"); + assertEquals("taskId", entity.getTaskId()); + assertEquals("zy", entity.getCreator()); + assertEquals("name", entity.getName()); + } + + @Test + @DisplayName("测试asUpdate") + public void testAsUpdate() { + TaskInstanceUpdateEntity entity = AppTaskInstance.asUpdate("taskId", "instanceId"); + assertEquals("taskId", entity.getTaskId()); + assertEquals("instanceId", entity.getInstanceId()); + } + + @Test + @DisplayName("测试asQuery") + public void testAsQuery() { + TaskInstanceQueryEntity entity = AppTaskInstance.asQuery("create_at", "desc").build().getEntity(); + assertEquals("create_at", entity.getOrder()); + assertEquals("desc", entity.getSort()); + } + + @Test + @DisplayName("测试 isRunning") + public void testIsRunning() { + AppTaskInstance instance = AppTaskInstance.asEntity().setStatus(MetaInstStatusEnum.RUNNING.name()).build(); + assertTrue(instance.isRunning()); + + AppTaskInstance instance1 = AppTaskInstance.asEntity().setStatus(MetaInstStatusEnum.READY.name()).build(); + assertFalse(instance1.isRunning()); + } + + @Test + @DisplayName("测试 is") + public void testIs() { + AppTaskInstance instance = AppTaskInstance.asEntity().setStatus(MetaInstStatusEnum.RUNNING.name()).build(); + assertFalse(instance.is(MetaInstStatusEnum.ARCHIVED)); + assertTrue(instance.is(MetaInstStatusEnum.ARCHIVED, MetaInstStatusEnum.RUNNING)); + } + + @Test + @DisplayName("测试 getParentId, 但parentPath为空") + public void getParentIdShouldReturnNullWhenIsEmpty() { + AppTaskInstance instance = this.factory.create(new Instance(), "taskId", this.appTaskInstanceService); + when(this.aippLogRepository.getParentPath(any())).thenReturn(""); + String parentId = instance.getParentInstanceId(); + assertNull(parentId); + } + + @Test + @DisplayName("测试 getParentId, 但parentPath为空") + public void getParentIdWhenIsNotEmpty() { + AppTaskInstance instance = this.factory.create(new Instance(), "taskId", this.appTaskInstanceService); + when(this.aippLogRepository.getParentPath(any())).thenReturn("xxx/bbb"); + String parentId = instance.getParentInstanceId(); + assertEquals("bbb", parentId); + } + + @Nested + @DisplayName("测试 run 接口") + class TestRun { + @Test + @DisplayName("当上下文中没有AppTask对象时,抛出Aipp异常.") + public void testNoAppTaskThrowsAippException() { + // given. + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + + // when. + // then. + assertThrows(AippException.class, () -> appTaskInstance.run(runContext)); + } + + @Test + @DisplayName("测试带有memory,并且类型是byConversationTurn") + public void testShouldUseMemoryAndMemoryTypeIsByConversationTurn() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(true, + MemoryTypeEnum.BY_CONVERSATION_TURN.type(), "10"); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + AppTaskInstance logInstance = mock(AppTaskInstance.class); + when(AppTaskInstanceTest.this.aippChatMapper.selectFormerInstanceByChat(any(), anyInt())).thenReturn( + List.of("instance1")); + when(AppTaskInstanceTest.this.appTaskInstanceService.getInstance(anyString(), anyString(), + any())).thenReturn(Optional.of(logInstance)); + + AppLog appLog = mock(AppLog.class); + when(logInstance.getLogs()).thenReturn(List.of(appLog)); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + assertEquals(true, businessData.get(AippConst.BS_AIPP_USE_MEMORY_KEY)); + verify(AppTaskInstanceTest.this.flowInstanceService, times(1)).startFlow(any(), any(), any()); + + ArgumentCaptor appTaskInstanceCaptor = ArgumentCaptor.forClass(AppTaskInstance.class); + verify(AppTaskInstanceTest.this.appTaskInstanceService, times(1)).update(appTaskInstanceCaptor.capture(), + any()); + AppTaskInstance instanceArg = appTaskInstanceCaptor.getValue(); + assertEquals("taskId", instanceArg.getTaskId()); + assertEquals("task_instance_1", instanceArg.getId()); + } + + @Test + @DisplayName("测试带有memory,并且类型是NotUserMemory") + public void testShouldUseMemoryButNotUse() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(true, + MemoryTypeEnum.NOT_USE_MEMORY.type(), ""); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + assertEquals(false, businessData.get(AippConst.BS_AIPP_USE_MEMORY_KEY)); + } + + @Test + @DisplayName("测试带有memory,并且类型是Customizing,但不存在fitableId") + public void testShouldUseMemoryAndCustomizingButFitableIdNotExists() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(true, + MemoryTypeEnum.CUSTOMIZING.type(), null); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + verify(AppTaskInstanceTest.this.client, times(0)).getRouter(eq(GENERICABLE_ID)); + } + + @Test + @DisplayName("测试带有memory,并且类型是Customizing, 存在fitableId") + public void testShouldUseMemoryAndCustomizingButFitableIdExists() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(true, + MemoryTypeEnum.CUSTOMIZING.type(), "fitable_id_1"); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + Router router = mock(Router.class); + Invoker invoker = mock(Invoker.class); + when(AppTaskInstanceTest.this.client.getRouter(anyString())).thenReturn(router); + when(router.route(any())).thenReturn(invoker); + when(invoker.invoke(any(), any(), any(), any())).thenReturn(new ArrayList<>()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + assertEquals(0, + ObjectUtils.>>cast(businessData.get(AippConst.BS_AIPP_MEMORIES_KEY)) + .size()); + verify(invoker, times(1)).invoke(anyMap().isEmpty(), eq("app_1"), eq(NORMAL.name()), eq(context)); + } + + @Test + @DisplayName("测试带有memory,并且类型是UserSelect") + @SuppressWarnings("unchecked") + public void testShouldUseMemoryAndTypeIsUserSelect() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(true, + MemoryTypeEnum.USER_SELECT.type(), ""); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + Instance instance = new Instance(); + instance.setId("task_instance_1"); + + ChatSession session = Mockito.mock(ChatSession.class); + + // when + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext, session); + + // then. + ArgumentCaptor captor = ArgumentCaptor.forClass(MemoryConfigDto.class); + verify(AppTaskInstanceTest.this.appChatSSEService, times(1)).sendToAncestorLastData(eq("task_instance_1"), + captor.capture()); + assertEquals(MemoryTypeEnum.USER_SELECT.type(), captor.getValue().getMemory()); + assertEquals("task_instance_1", captor.getValue().getInstanceId()); + assertSame(businessData, captor.getValue().getInitContext()); + } + + @Test + @DisplayName("测试不带有memory") + public void testShouldNotUseMemory() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(false, + MemoryTypeEnum.CUSTOMIZING.type(), "fitable_id_1"); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + businessData.put(AippConst.BS_AIPP_MEMORIES_KEY, List.of(MapBuilder.get().put("xxx", "111").build())); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + assertEquals(0, + ObjectUtils.>>cast(businessData.get(AippConst.BS_AIPP_MEMORIES_KEY)) + .size()); + } + + @Test + @DisplayName("测试不带有memory,memoryType是user_select") + public void testShouldNotUseMemoryAndMemoryTypeIsUserSelect() { + // given. + List> memoryConfigs = modelengine.fit.jober.aipp.domains.taskinstance.TestUtils.buildMemoryConfigs(false, + MemoryTypeEnum.USER_SELECT.type(), "fitable_id_1"); + + Map businessData = new HashMap<>(); + businessData.put(BS_CHAT_ID, "chat_id"); + businessData.put(AippConst.BS_AIPP_MEMORIES_KEY, List.of(MapBuilder.get().put("xxx", "111").build())); + + OperationContext context = buildOperation(); + RunContext runContext = new RunContext(businessData, context); + runContext.setMemoryConfig(memoryConfigs); + + AppTask appTask = mock(AppTask.class); + when(appTask.getEntity()).thenReturn( + AppTask.asEntity().setFlowDefinitionId("flow_1").setAppSuiteId("app_1")); + runContext.setAppTask(appTask); + + when(AppTaskInstanceTest.this.flowInstanceService.startFlow(any(), any(), any())).thenReturn( + new FlowInstanceResult("trace1")); + doNothing().when(AppTaskInstanceTest.this.appTaskInstanceService).update(any(), any()); + + // when + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + appTaskInstance.run(runContext); + + // then. + List> memories = ObjectUtils.cast(businessData.get(AippConst.BS_AIPP_MEMORIES_KEY)); + assertEquals("111", memories.get(0).get("xxx")); + } + } + + private OperationContext buildOperation() { + OperationContext context = new OperationContext(); + context.setOperator("张越"); + return context; + } + + @Nested + @DisplayName("测试 getPath 接口") + class TestGetPath { + @Test + @DisplayName("测试没有parent的情况.") + public void testNoParent() { + // given. + when(AppTaskInstanceTest.this.aippLogRepository.getParentPath(anyString())).thenReturn(""); + + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + + // when. + String path = appTaskInstance.getPath(buildOperation()); + + // then. + assertEquals("/task_instance_1", path); + } + + @Test + @DisplayName("测试有parent的情况.") + public void testHasParent() { + // given. + when(AppTaskInstanceTest.this.aippLogRepository.getParentPath(anyString())).thenReturn( + "/instance_parent_1"); + + AppTaskInstance parent = mock(AppTaskInstance.class); + when(AppTaskInstanceTest.this.appTaskInstanceService.getInstance(anyString(), anyString(), + any())).thenReturn(Optional.of(parent)); + when(parent.getPath(any())).thenReturn("/instance_parent_1"); + + Instance instance = new Instance(); + instance.setId("task_instance_1"); + AppTaskInstance appTaskInstance = AppTaskInstanceTest.this.factory.create(instance, "taskId", + appTaskInstanceService); + + // when. + String path = appTaskInstance.getPath(buildOperation()); + + // then. + assertEquals("/instance_parent_1/task_instance_1", path); + } + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecoratorTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecoratorTest.java new file mode 100644 index 0000000000..038b37673a --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceDecoratorTest.java @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.domains.business.MemoryTypeEnum; +import modelengine.fit.jober.aipp.domains.business.RunContext; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.jober.aipp.service.AppChatSseService; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * {@link TaskInstanceDecorator} 的测试类。 + * + * @author 张越 + * @since 2025-01-12 + */ +public class TaskInstanceDecoratorTest { + private AppTaskInstanceService appTaskInstanceService; + private AippLogService logService; + private AppChatSseService appChatSSEService; + private AppChatSessionService appChatSessionService; + + @BeforeEach + public void setUp() throws Exception { + this.appTaskInstanceService = mock(AppTaskInstanceService.class); + this.logService = mock(AippLogService.class); + this.appChatSSEService = mock(AppChatSseService.class); + this.appChatSessionService = mock(AppChatSessionService.class); + } + + @Test + @DisplayName("测试testChat,但session为null") + public void testChatWhenSessionIsNull() { + AppTaskInstance instance = mock(AppTaskInstance.class); + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + doNothing().when(instance).run(any(), any()); + TaskInstanceDecorator.create(instance) + .chat(this.appChatSessionService, this.appChatSSEService) + .run(runContext, null); + verify(this.appChatSessionService, times(0)).addSession(any(), any()); + } + + @Test + @DisplayName("测试testChat,session不为null") + @SuppressWarnings("unchecked") + public void testChatWhenSessionIsNotNull() { + AppTaskInstance instance = mock(AppTaskInstance.class); + List> memoryConfigs = TestUtils.buildMemoryConfigs(true, MemoryTypeEnum.CUSTOMIZING.type(), + ""); + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + runContext.setMemoryConfig(memoryConfigs); + doNothing().when(instance).run(any(), any()); + TaskInstanceDecorator.create(instance) + .chat(this.appChatSessionService, this.appChatSSEService) + .run(runContext, Mockito.mock(ChatSession.class)); + verify(this.appChatSessionService, times(1)).addSession(any(), any()); + verify(this.appChatSSEService, times(2)).send(any(), any()); + } + + @Test + @DisplayName("测试testChat,session不为null,但memory类型是UserSelect") + @SuppressWarnings("unchecked") + public void testChatWhenSessionIsNotNullButMemoryIsUserSelect() { + AppTaskInstance instance = mock(AppTaskInstance.class); + List> memoryConfigs = TestUtils.buildMemoryConfigs(true, MemoryTypeEnum.USER_SELECT.type(), + ""); + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + runContext.setMemoryConfig(memoryConfigs); + doNothing().when(instance).run(any(), any()); + TaskInstanceDecorator.create(instance) + .chat(this.appChatSessionService, this.appChatSSEService) + .run(runContext, Mockito.mock(ChatSession.class)); + verify(this.appChatSessionService, times(1)).addSession(any(), any()); + verify(this.appChatSSEService, times(1)).send(any(), any()); + } + + @Test + @DisplayName("测试exceptionLog,但session为null") + public void testExceptionLogWhenThrowException() { + AppTaskInstance instance = mock(AppTaskInstance.class); + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + doThrow(new AippException(AippErrCode.FLOW_ERROR)).when(instance).run(any(), any()); + TaskInstanceDecorator.create(instance) + .exceptionLog(this.appTaskInstanceService, this.logService) + .run(runContext, null); + verify(this.appTaskInstanceService, times(1)).update(any(), any()); + verify(this.logService, times(1)).insertLog(any(), any(), any()); + } + + @Test + @DisplayName("测试exceptionLog和Chat一起生效") + @SuppressWarnings("unchecked") + public void testChatAndExceptionLog() { + AppTaskInstance instance = mock(AppTaskInstance.class); + RunContext runContext = new RunContext(new HashMap<>(), new OperationContext()); + doNothing().when(instance).run(any(), any()); + TaskInstanceDecorator.create(instance) + .chat(this.appChatSessionService, this.appChatSSEService) + .exceptionLog(this.appTaskInstanceService, this.logService) + .run(runContext, Mockito.mock(ChatSession.class)); + verify(this.appChatSessionService, times(1)).addSession(any(), any()); + verify(this.appChatSSEService, times(2)).send(any(), any()); + verify(this.appTaskInstanceService, times(0)).update(any(), any()); + verify(this.logService, times(0)).insertLog(any(), any(), any()); + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntityTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntityTest.java new file mode 100644 index 0000000000..f2f97bb1a1 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TaskInstanceEntityTest.java @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.entity.task.TaskProperty; +import modelengine.fitframework.util.StringUtils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * {@link TaskInstanceEntity} 的测试类。 + * + * @author 张越 + * @since 2025-01-12 + */ +public class TaskInstanceEntityTest { + private static final DateTimeFormatter DF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Test + @DisplayName("基本测试") + public void test() { + Map infos = this.buildInfos(); + TaskInstanceDomainEntity entity = AppTaskInstance.asEntity().putInfos(infos).putTags(new ArrayList<>()); + entity.setInstanceId("instanceId"); + entity.setTaskId("taskId"); + assertEquals("instance-name", entity.getName()); + assertEquals("instanceId", entity.getInstanceId()); + assertEquals("taskId", entity.getTaskId()); + assertEquals("zy", entity.getCreator()); + Assertions.assertTrue(entity.getStatus().isPresent()); + assertEquals("active", entity.getStatus().get()); + assertEquals("10", entity.getProgress()); + assertEquals("trace1", entity.getFlowTranceId()); + assertEquals("form1", entity.getFormId()); + assertEquals("1.0", entity.getFormVersion()); + assertEquals("startNodeId", entity.getCurrentNodeId()); + assertEquals("childInstanceId", entity.getChildInstanceId()); + assertEquals("output", entity.getLlmOutput()); + assertEquals(10L, entity.getResumeDuration()); + } + + @Test + @DisplayName("set方法测试") + public void testSetMethods() { + LocalDateTime createTime = LocalDateTime.now(); + String currentTimeStr = DF.format(createTime); + LocalDateTime finishTime = createTime.plusDays(1); + String finishTimeStr = DF.format(finishTime); + LocalDateTime smartFormTime = createTime.plusDays(2); + String smartFormTimeStr = DF.format(smartFormTime); + + TaskInstanceDomainEntity entity = AppTaskInstance.asEntity(); + entity.setCurrentNodeId("currentNodeId"); + entity.setFlowTraceId("flowTraceId"); + entity.setTaskId("taskId"); + entity.setInstanceId("instanceId"); + entity.setChildInstanceId("childInstanceId"); + entity.setCreator("createCreator"); + entity.setCreateTime(createTime); + entity.setFinishTime(finishTime); + entity.setFormId("formId"); + entity.setName("name"); + entity.setStatus("active"); + entity.setProgress("20"); + entity.setFormVersion("1.0"); + entity.setLlmOutput("llmOutput"); + entity.setSmartFormTime(smartFormTime); + entity.setResumeDuration("20"); + + assertEquals("currentNodeId", entity.getCurrentNodeId()); + assertEquals("flowTraceId", entity.getFlowTranceId()); + assertEquals("taskId", entity.getTaskId()); + assertEquals("instanceId", entity.getInstanceId()); + assertEquals("childInstanceId", entity.getChildInstanceId()); + assertEquals("createCreator", entity.getCreator()); + assertEquals(currentTimeStr, entity.getCreateTime()); + assertEquals(finishTimeStr, entity.getFinishTime(null)); + assertEquals("formId", entity.getFormId()); + assertEquals("name", entity.getName()); + assertTrue(entity.getStatus().isPresent()); + assertEquals("active", entity.getStatus().get()); + assertEquals("20", entity.getProgress()); + assertEquals("1.0", entity.getFormVersion()); + assertEquals("llmOutput", entity.getLlmOutput()); + assertEquals(smartFormTimeStr, entity.getSmartFormTime().orElse(StringUtils.EMPTY)); + assertEquals(20L, entity.getResumeDuration()); + } + + @Test + @DisplayName("fetch测试") + public void testFetch() { + Map businessData = new HashMap<>(); + businessData.put(AippConst.INST_NAME_KEY, "zy"); + businessData.put(AippConst.INST_CREATOR_KEY, "wla"); + + TaskProperty property = new TaskProperty(); + property.setId("propertyId"); + property.setName(AippConst.INST_NAME_KEY); + List props = new ArrayList<>(); + props.add(property); + + TaskInstanceDomainEntity entity = AppTaskInstance.asEntity(); + entity.fetch(businessData, props); + + assertEquals("zy", entity.getName()); + assertNull(entity.getCreator()); + } + + private Map buildInfos() { + Map infos = new HashMap<>(); + infos.put(AippConst.INST_NAME_KEY, "instance-name"); + infos.put(AippConst.INST_CREATOR_KEY, "zy"); + infos.put(AippConst.INST_STATUS_KEY, "active"); + infos.put(AippConst.INST_PROGRESS_KEY, "10"); + infos.put(AippConst.INST_FLOW_INST_ID_KEY, "trace1"); + infos.put(AippConst.INST_CURR_FORM_ID_KEY, "form1"); + infos.put(AippConst.INST_CURR_FORM_VERSION_KEY, "1.0"); + infos.put(AippConst.INST_CURR_NODE_ID_KEY, "startNodeId"); + infos.put(AippConst.INST_RESUME_DURATION_KEY, "10"); + infos.put(AippConst.INST_CHILD_INSTANCE_ID, "childInstanceId"); + infos.put("llmOutput", "output"); + return infos; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TestUtils.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TestUtils.java new file mode 100644 index 0000000000..2062bd85ef --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/domains/taskinstance/TestUtils.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.domains.taskinstance; + +import modelengine.fit.jober.aipp.constants.AippConst; + +import modelengine.fitframework.util.MapBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 测试工具类。 + * + * @author 张越 + * @since 2025-01-12 + */ +public class TestUtils { + /** + * 构建memory配置. + * + * @param isMemoryEnable 是否启用memory. + * @param type 类型. + * @param value 值. + * @return 配置对象. + */ + public static List> buildMemoryConfigs(boolean isMemoryEnable, String type, + String value) { + List> memoryConfigs = new ArrayList<>(); + memoryConfigs.add(MapBuilder.get() + .put("name", AippConst.MEMORY_SWITCH_KEY) + .put("value", isMemoryEnable) + .build()); + memoryConfigs.add(MapBuilder.get().put("name", "type").put("value", type).build()); + memoryConfigs.add(MapBuilder.get().put("name", "value").put("value", value).build()); + return memoryConfigs; + } +} diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AippLogVoTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AippLogVoTest.java index 0dc47b0d78..3be9c271a8 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AippLogVoTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AippLogVoTest.java @@ -25,18 +25,9 @@ public class AippLogVoTest { @Test void testAippLogVO() { - AippLogCreateDto aippLogCreateDto = AippLogCreateDto.builder() - .aippId("123") - .logId("123") - .version("1.0.0") - .aippType("PREVIEW") - .instanceId("123") - .logData("123") - .logType("MSG") - .path("/123") - .chatId("123") - .atChatId("123") - .build(); + AippLogCreateDto aippLogCreateDto = AippLogCreateDto.builder().aippId("123") + .logId("123").version("1.0.0").aippType("PREVIEW").instanceId("123") + .logData("123").logType("MSG").path("/123").chatId("123").atChatId("123").build(); AippLogVO aippLogVO = AippLogVO.fromCreateDto(aippLogCreateDto); Assertions.assertEquals(aippLogVO.getLogId(), "123"); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDtoTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDtoTest.java index 3ac9169e58..24fb415ff9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDtoTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dto/AppBuilderSaveConfigDtoTest.java @@ -40,7 +40,10 @@ public void testAllArgsConstruction() { @Test @DisplayName("测试构建器构造传输类") public void testBuilderConstruction() { - AppBuilderSaveConfigDto dto = AppBuilderSaveConfigDto.builder().input(Collections.EMPTY_LIST).graph("").build(); + AppBuilderSaveConfigDto dto = AppBuilderSaveConfigDto.builder() + .input(Collections.EMPTY_LIST) + .graph("") + .build(); Assertions.assertEquals(Collections.EMPTY_LIST, dto.getInput()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dummy/OperationContextDummy.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dummy/OperationContextDummy.java index 5180c0ea19..804391db7e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dummy/OperationContextDummy.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/dummy/OperationContextDummy.java @@ -32,8 +32,8 @@ public static OperationContext getDummy() { public static boolean operationContextDummyMatcher(OperationContext context) { return DUMMY_TENANT_ID.equals(context.getTenantId()) && DUMMY_OPERATOR.equals(context.getOperator()) - && DUMMY_GLOBAL_USER_ID.equals(context.getGlobalUserId()) && DUMMY_ACCOUNT.equals( - context.getAccount()) + && DUMMY_GLOBAL_USER_ID.equals(context.getGlobalUserId()) + && DUMMY_ACCOUNT.equals(context.getAccount()) && DUMMY_EMPLOYEE_NUMBER.equals(context.getEmployeeNumber()) && DUMMY_NAME.equals(context.getName()) && DUMMY_OPERATOR_IP.equals(context.getOperatorIp()) && DUMMY_SOURCE_PLATFORM.equals(context.getSourcePlatform()) diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallbackTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallbackTest.java index 641f104e22..be6f946d0d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallbackTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowEndCallbackTest.java @@ -25,12 +25,12 @@ import modelengine.fit.jade.aipp.formatter.constant.Constant; import modelengine.fit.jade.aipp.formatter.support.ResponsibilityResult; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jober.aipp.TestUtils; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderForm; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.enums.AippInstLogType; import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; @@ -66,32 +66,24 @@ */ @FitTestWithJunit(includeClasses = {AippFlowEndCallback.class}) class AippFlowEndCallbackTest { - @Mock - private MetaService metaService; - @Mock private AippLogService aippLogService; - @Mock private ConversationRecordService conversationRecordService; - @Mock private AppBuilderFormService formService; - - @Mock - private MetaInstanceService metaInstanceService; - @Mock private AppChatSseService appChatSseService; - @Mock private AppBuilderFormRepository formRepository; - @Mock private AppBuilderAppFactory appFactory; - @Mock private OutputFormatterChain formatterChain; + @Mock + private AppTaskService appTaskService; + @Mock + private AppTaskInstanceService appTaskInstanceService; @Fit private AippFlowEndCallback aippFlowEndCallback; @@ -123,7 +115,7 @@ void test_callback_should_ok_when_test_data_combination() { AppBuilderFormPropertyRepository formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); AppBuilderForm appBuilderForm = new AppBuilderForm(formPropertyRepository); when(this.formService.selectWithId(anyString())).thenReturn(appBuilderForm); - when(this.metaService.retrieve(anyString(), any(OperationContext.class))).thenReturn(TestUtils.buildMeta()); + when(this.appTaskService.getTaskById(any(), any())).thenReturn(Optional.of(TestUtils.buildTask())); when(this.formatterChain.handle(any())).thenReturn(Optional.empty()); this.aippFlowEndCallback.callback(TestUtils.buildFlowDataWithExtraConfig(buildBusinessData(), null)); @@ -136,12 +128,12 @@ void test_callback_should_ok_when_final_output_with_map() { AppBuilderFormPropertyRepository formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); AppBuilderForm appBuilderForm = new AppBuilderForm(formPropertyRepository); when(this.formService.selectWithId(anyString())).thenReturn(appBuilderForm); - when(this.metaService.retrieve(anyString(), any(OperationContext.class))).thenReturn(TestUtils.buildMeta()); + when(this.appTaskService.getTaskById(any(), any())).thenReturn(Optional.of(TestUtils.buildTask())); when(this.formatterChain.handle(any())).thenReturn(Optional.empty()); Map businessData = buildBusinessData(); - businessData.put(AippConst.BS_AIPP_FINAL_OUTPUT, - MapBuilder.get().put("key0", "value0").put("key1", "value1").build()); + businessData.put(AippConst.BS_AIPP_FINAL_OUTPUT, MapBuilder.get() + .put("key0", "value0").put("key1", "value1").build()); this.aippFlowEndCallback.callback(TestUtils.buildFlowDataWithExtraConfig(businessData, null)); verify(this.formatterChain).handle(argThat(args -> { @@ -157,7 +149,7 @@ void should_ok_when_callback_with_normal_formatter_chain() { AppBuilderFormPropertyRepository formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); AppBuilderForm appBuilderForm = new AppBuilderForm(formPropertyRepository); when(this.formService.selectWithId(anyString())).thenReturn(appBuilderForm); - when(this.metaService.retrieve(anyString(), any(OperationContext.class))).thenReturn(TestUtils.buildMeta()); + when(this.appTaskService.getTaskById(any(), any())).thenReturn(Optional.of(TestUtils.buildTask())); doAnswer(args -> { Object argument = args.getArgument(0); return Optional.of(new ResponsibilityResult(new OutputMessageStub(argument), Constant.LLM_OUTPUT)); @@ -173,11 +165,13 @@ void test_callback_should_ok_when_test_with_form_data() { AppBuilderFormPropertyRepository formPropertyRepository = mock(AppBuilderFormPropertyRepository.class); AppBuilderForm appBuilderForm = new AppBuilderForm(formPropertyRepository); when(this.formService.selectWithId(anyString())).thenReturn(appBuilderForm); - when(this.metaService.retrieve(anyString(), any(OperationContext.class))).thenReturn(TestUtils.buildMeta()); + when(this.appTaskService.getTaskById(any(), any())).thenReturn(Optional.of(TestUtils.buildTask())); doNothing().when(this.appChatSseService).sendToAncestorLastData(anyString(), any()); when(this.formRepository.selectWithId(anyString())).thenReturn(null); when(this.formatterChain.handle(any())).thenReturn(Optional.empty()); - AppBuilderApp app = AppBuilderApp.builder().formProperties(Collections.emptyList()).build(); + AppBuilderApp app = AppBuilderApp.builder() + .formProperties(Collections.emptyList()) + .build(); when(this.appFactory.create("appId1")).thenReturn(app); Map businessData = buildBusinessData(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandleTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandleTest.java index 8cbd23806d..91afdd41e0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandleTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowExceptionHandleTest.java @@ -13,17 +13,15 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; -import modelengine.fit.waterflow.entity.FlowErrorInfo; -import modelengine.fit.waterflow.spi.FlowExceptionService; -import modelengine.jade.common.globalization.LocaleService; - -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppChatSessionService; +import modelengine.fit.waterflow.entity.FlowErrorInfo; +import modelengine.fit.waterflow.spi.FlowExceptionService; import modelengine.fitframework.broker.client.BrokerClient; import modelengine.fitframework.broker.client.FitableNotFoundException; import modelengine.fitframework.broker.client.Invoker; @@ -44,7 +42,6 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -68,9 +65,6 @@ public class AippFlowExceptionHandleTest { @Mock private AippLogService aippLogService; - @Mock - private MetaInstanceService metaInstanceService; - @Mock private LocaleService localeService; @@ -83,21 +77,20 @@ public class AippFlowExceptionHandleTest { @Mock private BrokerClient brokerClient; + @Mock + private AppTaskInstanceService appTaskInstanceService; + @BeforeEach void setUp() { - this.aippFlowExceptionHandle = new AippFlowExceptionHandle(this.aippLogService, - this.metaInstanceService, - this.localeService, - this.appChatSessionService, - this.toolExceptionHandle, - this.brokerClient); + this.aippFlowExceptionHandle = new AippFlowExceptionHandle(this.aippLogService, this.localeService, + this.appChatSessionService, this.toolExceptionHandle, this.brokerClient, this.appTaskInstanceService); } @Test @DisplayName("测试构造方法") void shouldSuccessWhenConstruct() { String opContext = "{\"tenantId\": \"test\"," + "\"operator\": \"test\"," + "\"globalUserId\":\"test\"," - + "\"account\": \"account\"," + "\"employeeNumber\": \"employeeNumber\"," + "\"name\": \"name\"," + + "\"account\":\"account\"," + "\"employeeNumber\": \"employeeNumber\"," + "\"name\": \"name\"," + "\"operatorIp\": \"operatorIp\"," + "\"sourcePlatform\": \"sourcePlatform\"," + "\"language\": \"language\"}"; List> flowData = Arrays.asList(MapBuilder.get() @@ -113,12 +106,8 @@ void shouldSuccessWhenConstruct() { Mockito.when(this.localeService.localize(any(Locale.class), eq(UI_WORD_KEY_HINT))).thenReturn("test"); Mockito.when(this.appChatSessionService.getSession(anyString())).thenReturn(Optional.of(chatSession)); Mockito.when(this.toolExceptionHandle.getFixErrorMsg(any(), any(), any())).thenReturn("errorMessage"); - Instance instance = new Instance("id", - MapBuilder.get() - .put(AippConst.INST_STATUS_KEY, MetaInstStatusEnum.RUNNING.name()) - .build(), - new ArrayList<>()); - Mockito.when(this.metaInstanceService.retrieveById(any(), any())).thenReturn(instance); + AppTaskInstance instance = AppTaskInstance.asEntity().setStatus(MetaInstStatusEnum.RUNNING.name()).build(); + Mockito.when(this.appTaskInstanceService.getInstanceById(any(), any())).thenReturn(Optional.of(instance)); FlowErrorInfo flowErrorInfo = new FlowErrorInfo(); flowErrorInfo.setErrorCode(10000); flowErrorInfo.setErrorMessage("errorMessage"); @@ -147,9 +136,12 @@ void shouldCallParentExceptionHandlerWhenHandleGivenParent() { Mockito.when(this.appChatSessionService.getSession(anyString())).thenReturn(Optional.empty()); Router router = Mockito.mock(Router.class); Invoker invoker = Mockito.mock(Invoker.class); - Mockito.when(router.route(ArgumentMatchers.argThat(arg -> (arg instanceof FitableIdFilter) && arg.toString() - .equals("FitableIdFilter{fitableIds=[parent]}")))).thenReturn(invoker); - Mockito.when(invoker.invoke(nodeId, flowData, flowErrorInfo)).thenReturn(null); + Mockito.when(router.route(ArgumentMatchers.argThat( + arg -> (arg instanceof FitableIdFilter) && arg.toString() + .equals("FitableIdFilter{fitableIds=[parent]}")))) + .thenReturn(invoker); + Mockito.when(invoker.invoke(nodeId, flowData, flowErrorInfo)) + .thenReturn(null); Mockito.when(this.brokerClient.getRouter(FlowExceptionService.class, FlowExceptionService.HANDLE_EXCEPTION_GENERICABLE)).thenReturn(router); @@ -177,8 +169,7 @@ void shouldNotThrowWhenHandleGivenParentThrowFitException() { FlowExceptionService.HANDLE_EXCEPTION_GENERICABLE)) .thenThrow(new FitableNotFoundException("not found")); - Assertions.assertDoesNotThrow(() -> this.aippFlowExceptionHandle.handleException(nodeId, - flowData, - flowErrorInfo)); + Assertions.assertDoesNotThrow( + () -> this.aippFlowExceptionHandle.handleException(nodeId, flowData, flowErrorInfo)); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowRuntimeInfoServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowRuntimeInfoServiceTest.java index f4d1f779e2..e85b913c89 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowRuntimeInfoServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowRuntimeInfoServiceTest.java @@ -10,28 +10,19 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jane.meta.multiversion.instance.MetaInstanceFilter; -import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; import modelengine.fit.jober.aipp.service.AippFlowRuntimeInfoService; import modelengine.fit.jober.aipp.service.impl.AippFlowRuntimeInfoServiceImpl; -import modelengine.fit.jober.common.RangeResult; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fit.jober.entity.consts.NodeTypes; import modelengine.fit.runtime.entity.Parameter; import modelengine.fit.runtime.entity.RuntimeData; @@ -45,9 +36,7 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; /** @@ -59,10 +48,10 @@ @ExtendWith(MockitoExtension.class) public class AippFlowRuntimeInfoServiceTest { @Mock - private MetaService metaService; + private AppTaskInstanceService appTaskInstanceService; @Mock - private MetaInstanceService metaInstanceService; + private AppTaskService appTaskService; @Mock private AppBuilderRuntimeInfoRepository repository; @@ -74,7 +63,8 @@ public class AippFlowRuntimeInfoServiceTest { */ @BeforeEach void setUp() { - this.service = new AippFlowRuntimeInfoServiceImpl(this.metaService, this.metaInstanceService, this.repository); + this.service = new AippFlowRuntimeInfoServiceImpl(this.repository, this.appTaskInstanceService, + this.appTaskService); } /** @@ -82,26 +72,12 @@ void setUp() { */ @Test void shouldOptionalEmptyWhenNoRuntimeInfo() { - // before - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - meta.setVersionId("version1"); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - doReturn(metaRangedResultSet).when(this.metaService) - .list(any(MetaFilter.class), anyBoolean(), anyLong(), anyInt(), any(OperationContext.class)); - - Map info = new HashMap<>(); - info.put(AippConst.INST_FLOW_INST_ID_KEY, "trace1"); - Instance instance = new Instance(); - instance.setInfo(info); - RangedResultSet resultSet = new RangedResultSet<>(); - resultSet.setResults(Collections.singletonList(instance)); - resultSet.setRange(new RangeResult(10, 10, 10)); - doReturn(resultSet).when(this.metaInstanceService) - .list(anyList(), anyLong(), anyInt(), any(OperationContext.class)); + doReturn(Optional.of(AppTask.asEntity().setTaskId("version1").setAppId("app1").build())).when( + this.appTaskService).getLatest(anyString(), anyString(), any(OperationContext.class)); + + Optional result = Optional.of(AppTaskInstance.asEntity().setFlowTraceId("trace1").build()); + doReturn(result).when(this.appTaskInstanceService) + .getInstance(anyString(), anyString(), any(OperationContext.class)); doReturn(Collections.emptyList()).when(this.repository).selectByTraceId(anyString()); @@ -118,26 +94,13 @@ void shouldOptionalEmptyWhenNoRuntimeInfo() { */ @Test void shouldThrowIllegalStateExceptionWhenNoStartNodeInfo() { - // before - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - meta.setVersionId("version1"); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - doReturn(metaRangedResultSet).when(this.metaService) - .list(any(MetaFilter.class), anyBoolean(), anyLong(), anyInt(), any(OperationContext.class)); - - Map info = new HashMap<>(); - info.put(AippConst.INST_FLOW_INST_ID_KEY, "trace1"); - Instance instance = new Instance(); - instance.setInfo(info); - RangedResultSet resultSet = new RangedResultSet<>(); - resultSet.setResults(Collections.singletonList(instance)); - resultSet.setRange(new RangeResult(10, 10, 10)); - doReturn(resultSet).when(this.metaInstanceService) - .list(anyList(), anyLong(), anyInt(), any(OperationContext.class)); + doReturn(Optional.of(AppTask.asEntity().setTaskId("version1").setAppId("app1").build())).when( + this.appTaskService).getLatest(anyString(), anyString(), any(OperationContext.class)); + + Optional instanceOp = Optional.of( + AppTaskInstance.asEntity().setFlowTraceId("trace1").build()); + doReturn(instanceOp).when(this.appTaskInstanceService) + .getInstance(anyString(), anyString(), any(OperationContext.class)); List infos = new ArrayList<>(); infos.add(AppBuilderRuntimeInfo.builder().nodeType(NodeTypes.STATE.getType()).build()); @@ -190,25 +153,12 @@ void shouldBeNormalWhenInfosIsRight() { } private void mockData() { - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - meta.setVersionId("version1"); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - doReturn(metaRangedResultSet).when(this.metaService) - .list(any(MetaFilter.class), anyBoolean(), anyLong(), anyInt(), any(OperationContext.class)); - - Map info = new HashMap<>(); - info.put(AippConst.INST_FLOW_INST_ID_KEY, "trace1"); - Instance instance = new Instance(); - instance.setInfo(info); - RangedResultSet resultSet = new RangedResultSet<>(); - resultSet.setResults(Collections.singletonList(instance)); - resultSet.setRange(new RangeResult(10, 10, 10)); - doReturn(resultSet).when(this.metaInstanceService) - .list(anyList(), anyLong(), anyInt(), any(OperationContext.class)); + doReturn(Optional.of(AppTask.asEntity().setTaskId("version1").setAppId("app1").build())).when( + this.appTaskService).getLatest(anyString(), anyString(), any(OperationContext.class)); + + Optional result = Optional.of(AppTaskInstance.asEntity().setFlowTraceId("trace1").build()); + doReturn(result).when(this.appTaskInstanceService) + .getInstance(anyString(), anyString(), any(OperationContext.class)); List infos = new ArrayList<>(); Parameter startParameter = new Parameter(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandleTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandleTest.java index b64d402764..bc9479bbb4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandleTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AippFlowSmartFormHandleTest.java @@ -12,12 +12,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.service.AippLogService; import modelengine.fit.jober.aipp.service.AppBuilderFormService; import modelengine.fit.jober.aipp.service.AppChatSseService; @@ -31,7 +32,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -48,7 +49,10 @@ public class AippFlowSmartFormHandleTest { private AppBuilderFormService appBuilderFormService; @Mock - private MetaInstanceService metaInstanceService; + private AppTaskService appTaskService; + + @Mock + private AppTaskInstanceService appTaskInstanceService; @Mock private AppChatSseService appChatSseService; @@ -57,24 +61,26 @@ public class AippFlowSmartFormHandleTest { private AippLogService aippLogService; @Mock - private AppBuilderAppFactory appFactory; + private AppVersionService appVersionService; private AippFlowSmartFormHandle service; @BeforeEach void setUp() { - this.service = new AippFlowSmartFormHandle(this.appBuilderFormService, this.metaInstanceService, - this.appChatSseService, this.aippLogService, this.appFactory, null, null, null, null); + this.service = new AippFlowSmartFormHandle(this.appBuilderFormService, this.appChatSseService, + this.aippLogService, this.appTaskService, this.appTaskInstanceService, this.appVersionService, null, + null, null); } @Test void testHandleSmartForm() { - List> flowData = Arrays.asList(MapBuilder.get() + List> flowData = Collections.singletonList(MapBuilder.get() .put(AippConst.BS_DATA_KEY, MapBuilder.get() .put(AippConst.BS_NODE_ID_KEY, "123") .put(AippConst.PARENT_INSTANCE_ID, "123") .put(AippConst.BS_AIPP_INST_ID_KEY, "123") - .put(AippConst.BS_CHAT_ID, "123").put(AippConst.BS_HTTP_CONTEXT_KEY, "{\"account\":\"123\"}") + .put(AippConst.BS_CHAT_ID, "123") + .put(AippConst.BS_HTTP_CONTEXT_KEY, "{\"account\":\"123\"}") .put(AippConst.BS_AT_CHAT_ID, "atChatId") .put(AippConst.BS_META_VERSION_ID_KEY, "version") .put(AippConst.CONTEXT_APP_ID, "123") @@ -82,17 +88,14 @@ void testHandleSmartForm() { .build()); Map defaultValue = new HashMap<>(); defaultValue.put("hello", "world"); - AppBuilderFormProperty formProperty = AppBuilderFormProperty.builder() - .formId("id1") - .name("fp1") - .defaultValue(defaultValue) - .build(); + AppBuilderFormProperty formProperty = + AppBuilderFormProperty.builder().formId("id1").name("fp1").defaultValue(defaultValue).build(); List list = new ArrayList<>(); list.add(formProperty); AppBuilderForm form = AppBuilderForm.builder().id("id1").name("form1").tenantId("tenantId").build(); Mockito.when(this.appBuilderFormService.selectWithId(anyString())).thenReturn(form); - AppBuilderApp mockApp = mock(AppBuilderApp.class); - Mockito.when(this.appFactory.create(anyString())).thenReturn(mockApp); + AppVersion mockApp = mock(AppVersion.class); + Mockito.when(this.appVersionService.retrieval(anyString())).thenReturn(mockApp); Mockito.when(mockApp.getFormProperties()).thenReturn(list); this.service.handleSmartForm(flowData, "111"); verify(this.appChatSseService, times(1)).sendToAncestorLastData(any(), any()); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListenerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListenerTest.java index f8d88dcbb8..889269df11 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListenerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/AsyncAppNodeListenerTest.java @@ -58,9 +58,8 @@ void shouldCallResumeAsyncJobWhenCallbackGivenValidContext() { MapBuilder.get().put(endNodeId, endExecuteInfo).build()) .build()); - Map contextData = MapBuilder.get() - .put(FlowDataConstant.FLOW_NODE_ID, endNodeId) - .build(); + Map contextData = + MapBuilder.get().put(FlowDataConstant.FLOW_NODE_ID, endNodeId).build(); Map context = MapBuilder.get() .put(AippConst.BS_DATA_KEY, businessData) @@ -82,9 +81,8 @@ void shouldNotCallResumeAsyncJobWhenCallbackGivenEmptyExecuteInfo() { Map businessData = buildAppBasicBusinessData(flowDataId); String endNodeId = "endNodeId"; - Map contextData = MapBuilder.get() - .put(FlowDataConstant.FLOW_NODE_ID, endNodeId) - .build(); + Map contextData = + MapBuilder.get().put(FlowDataConstant.FLOW_NODE_ID, endNodeId).build(); Map context = MapBuilder.get() .put(AippConst.BS_DATA_KEY, businessData) @@ -103,9 +101,8 @@ void shouldCallFailAsyncJobWhenHandleExceptionGivenValidContext() { Map businessData = buildAppBasicBusinessData(flowDataId); String exceptionNodeId = "endNodeId"; - Map contextData = MapBuilder.get() - .put(FlowDataConstant.FLOW_NODE_ID, exceptionNodeId) - .build(); + Map contextData = + MapBuilder.get().put(FlowDataConstant.FLOW_NODE_ID, exceptionNodeId).build(); Map context = MapBuilder.get() .put(AippConst.BS_DATA_KEY, businessData) diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriberTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriberTest.java index 2ef5eb18f9..9310e70e82 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriberTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/FlowPublishSubscriberTest.java @@ -11,27 +11,25 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.entity.ChatSession; -import modelengine.fit.jober.aipp.enums.AppState; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; import modelengine.fit.jober.aipp.service.AppChatSessionService; import modelengine.fit.jober.aipp.service.impl.RuntimeInfoServiceImpl; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fit.jober.entity.consts.NodeTypes; import modelengine.fit.waterflow.domain.enums.FlowNodeStatus; import modelengine.fit.waterflow.entity.FlowErrorInfo; @@ -49,7 +47,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -65,12 +62,6 @@ */ @ExtendWith(MockitoExtension.class) public class FlowPublishSubscriberTest { - @Mock - private MetaService metaService; - - @Mock - private AppBuilderAppFactory appFactory; - @Mock private AppBuilderRuntimeInfoRepository repository; @@ -82,13 +73,22 @@ public class FlowPublishSubscriberTest { @Mock private ToolExceptionHandle toolExceptionHandle; + @Mock + private AppTaskService appTaskService; + + @Mock + private AppVersionService appVersionService; + + @Mock + private AppTaskInstanceService appTaskInstanceService; + /** * 初始化. */ @BeforeEach void setUp() { - RuntimeInfoServiceImpl runtimeInfoService = new RuntimeInfoServiceImpl(this.metaService, this.appFactory, null, - null); + RuntimeInfoServiceImpl runtimeInfoService = new RuntimeInfoServiceImpl(null, this.appTaskService, + this.appTaskInstanceService, this.appVersionService); this.flowPublishSubscriber = new FlowPublishSubscriber(this.repository, this.toolExceptionHandle, this.appChatSessionService, null, runtimeInfoService); } @@ -102,18 +102,12 @@ void shouldAttributesMatchWhenOnPublish() { FlowPublishContext context = this.buildFlowPublishContext(); FlowNodePublishInfo publishInfo = this.buildFlowNodePublishInfo(context); - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - doReturn(metaRangedResultSet).when(this.metaService) - .list(any(MetaFilter.class), anyBoolean(), anyLong(), anyInt(), any(OperationContext.class)); - - AppBuilderApp appBuilderApp = new AppBuilderApp(null, null, null, null, null); - appBuilderApp.setState(AppState.PUBLISHED.getName()); - doReturn(appBuilderApp).when(this.appFactory).create(anyString()); + doReturn(Optional.of(AppTask.asEntity().setAppId("app1").build())).when(this.appTaskService) + .getLatest(anyString(), anyString(), any(OperationContext.class)); + + AppVersion appVersion = mock(AppVersion.class); + when(appVersion.isPublished()).thenReturn(true); + doReturn(appVersion).when(this.appVersionService).retrieval(anyString()); AtomicReference reference = new AtomicReference<>(); doAnswer(invocationOnMock -> { diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/LlmComponentTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/LlmComponentTest.java index 725d1ca8c9..26e3a18417 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/LlmComponentTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/LlmComponentTest.java @@ -13,6 +13,25 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; +import modelengine.fit.jade.aipp.model.dto.ModelListDto; +import modelengine.fit.jade.aipp.model.service.AippModelCenter; +import modelengine.fit.jade.aipp.prompt.PromptBuilder; +import modelengine.fit.jade.aipp.prompt.PromptMessage; +import modelengine.fit.jade.aipp.prompt.PromptStrategy; +import modelengine.fit.jade.aipp.prompt.UserAdvice; +import modelengine.fit.jade.aipp.prompt.repository.PromptBuilderChain; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.jober.aipp.TestUtils; +import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; +import modelengine.fit.jober.aipp.fel.WaterFlowAgent; +import modelengine.fit.jober.aipp.service.AippLogService; +import modelengine.fit.jober.aipp.service.AippLogStreamService; +import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.fit.waterflow.entity.FlowErrorInfo; + import modelengine.fel.core.chat.ChatMessage; import modelengine.fel.core.chat.ChatModel; import modelengine.fel.core.chat.ChatOption; @@ -58,7 +77,6 @@ import modelengine.jade.common.globalization.LocaleService; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -86,52 +104,22 @@ */ @ExtendWith(MockitoExtension.class) public class LlmComponentTest { - private static final String TOOL_DEFAULT_VALUE = "\"tool_async\""; - @Mock private FlowInstanceService flowInstanceService; - - @Mock - private MetaInstanceService metaInstanceService; - - @Mock - private MetaService metaService; - @Mock private ToolProvider toolProvider; - @Mock private AippLogService aippLogService; - @Mock private AippLogStreamService aippLogStreamService; - @Mock - private PromptBuilderChain promptBuilderChain; - + private AppTaskInstanceService appTaskInstanceService; @Mock - private BrokerClient client; - + private PromptBuilderChain promptBuilderChain; private final ObjectSerializer serializer = new JacksonObjectSerializer(null, null, null); - - private LocaleService localeService; - @Mock private AippModelCenter aippModelCenter; - @BeforeEach - void setUp() { - Mockito.when(toolProvider.getTool(any())).thenReturn(Collections.emptyList()); - doAnswer(invocationOnMock -> { - Object advice = invocationOnMock.getArgument(0); - Object context = invocationOnMock.getArgument(1); - return new PromptBuilderStub().build(ObjectUtils.cast(advice), ObjectUtils.cast(context)); - }).when(this.promptBuilderChain).build(any(), any()); - - when(this.aippModelCenter.getModelAccessInfo(any(), any(), any())).thenReturn( - ModelAccessInfo.builder().tag("tag").build()); - } - static class PromptBuilderStub implements PromptBuilder { @Override public Optional build(UserAdvice userAdvice, Map context) { @@ -232,6 +220,7 @@ public List getTool(List name) { @Disabled("多线程阻塞,无法唤醒") void shouldOkWhenWaterFlowAgentWithoutAsyncTool() throws InterruptedException { // stub + this.prepareModel(); AbstractAgent agent = this.getWaterFlowAgent(this.buildChatStreamModel(null), false); LlmComponent llmComponent = getLlmComponent(agent); @@ -239,11 +228,10 @@ void shouldOkWhenWaterFlowAgentWithoutAsyncTool() throws InterruptedException { Mockito.doNothing().when(aippLogStreamService).send(any()); CountDownLatch countDownLatch = mockResumeFlow(flowInstanceService); Mockito.doAnswer((Answer) invocation -> { - InstanceDeclarationInfo info = ObjectUtils.cast(invocation.getArgument(2)); - Map value = info.getInfo().getValue(); - Assertions.assertEquals("0123", value.get("llmOutput")); + AppTaskInstance instance = ObjectUtils.cast(invocation.getArgument(0)); + Assertions.assertEquals("0123", instance.getEntity().getLlmOutput()); return null; - }).when(metaInstanceService).patchMetaInstance(any(), any(), any(), any()); + }).when(this.appTaskInstanceService).update(any(), any()); // run llmComponent.handleTask(TestUtils.buildFlowDataWithExtraConfig(buildLlmTestData(), null)); @@ -253,6 +241,7 @@ void shouldOkWhenWaterFlowAgentWithoutAsyncTool() throws InterruptedException { @Test void shouldFailWhenWaterFlowAgentThrowException() throws InterruptedException { // stub + this.prepareModel(); AbstractAgent agent = this.getWaterFlowAgent(this.buildChatStreamModel("exceptionMsg"), false); LlmComponent llmComponent = getLlmComponent(agent); @@ -268,6 +257,7 @@ void shouldFailWhenWaterFlowAgentThrowException() throws InterruptedException { @Disabled("多线程阻塞,无法唤醒") void shouldOkWhenWaterFlowAgentWithAsyncTool() throws InterruptedException { // stub + this.prepareModel(); AbstractAgent agent = this.getWaterFlowAgent(this.buildChatStreamModel(null), true); LlmComponent llmComponent = getLlmComponent(agent); @@ -277,9 +267,8 @@ void shouldOkWhenWaterFlowAgentWithAsyncTool() throws InterruptedException { CountDownLatch countDownLatch = mockResumeFlow(flowInstanceService); Mockito.doAnswer((Answer) invocation -> { - InstanceDeclarationInfo info = ObjectUtils.cast(invocation.getArgument(2)); - Map value = info.getInfo().getValue(); - String childInstanceId = ObjectUtils.cast(value.get(AippConst.INST_CHILD_INSTANCE_ID)); + AppTaskInstance instance = ObjectUtils.cast(invocation.getArgument(0)); + String childInstanceId = instance.getEntity().getChildInstanceId(); if (childInstanceId != null) { Assertions.assertEquals(TestUtils.DUMMY_CHILD_INSTANCE_ID, childInstanceId); Map businessData = new HashMap<>(); @@ -290,10 +279,10 @@ void shouldOkWhenWaterFlowAgentWithAsyncTool() throws InterruptedException { llmComponent.callback(TestUtils.buildFlowDataWithExtraConfig(businessData, null)); } else { resCnt.getAndIncrement(); - Assertions.assertEquals("0123", value.get("llmOutput")); + Assertions.assertEquals("0123", instance.getEntity().getLlmOutput()); } return null; - }).when(metaInstanceService).patchMetaInstance(any(), any(), any(), any()); + }).when(this.appTaskInstanceService).update(any(), any()); Mockito.when(toolProvider.getTool(any())).thenReturn(Collections.emptyList()); // run @@ -305,6 +294,7 @@ void shouldOkWhenWaterFlowAgentWithAsyncTool() throws InterruptedException { @Test void shouldOkWhenNoTool() throws InterruptedException { // stub + this.prepareModel(); AiProcessFlow testAgent = AiFlows.create() .map(m -> ObjectUtils.cast(ChatMessages.from(new AiMessage("bad")))) .close(); @@ -322,12 +312,20 @@ void shouldOkWhenNoTool() throws InterruptedException { @Test void shouldFailedWhenNoTool() throws InterruptedException { // stub + this.prepareModel(); AiProcessFlow testAgent = AiFlows.create().just(m -> { int err = 1 / 0; }).close(); AbstractAgent agent = this.buildStubAgent(testAgent); - LlmComponent llmComponent = new LlmComponent(flowInstanceService, metaInstanceService, toolProvider, agent, - aippLogService, null, client, serializer, localeService, aippModelCenter, promptBuilderChain); + LlmComponent llmComponent = new LlmComponent(flowInstanceService, + toolProvider, + agent, + aippLogService, + null, + serializer, + aippModelCenter, + promptBuilderChain, + this.appTaskInstanceService); // mock CountDownLatch countDownLatch = mockFailAsyncJob(flowInstanceService); @@ -341,27 +339,34 @@ void shouldFailedWhenNoTool() throws InterruptedException { void shouldOkWhenUseWorkflowNoReturn() throws InterruptedException { AtomicReference prompt = new AtomicReference<>(); // stub + this.prepareModel(); AiProcessFlow testAgent = AiFlows.create() .just(prompt::set) .map(m -> ObjectUtils.cast(ChatMessages.from(new ToolMessage("1", "\"tool_async\"")))) .close(); AbstractAgent agent = this.buildStubAgent(testAgent); - LlmComponent llmComponent = new LlmComponent(flowInstanceService, metaInstanceService, toolProvider, agent, - this.aippLogService, null, client, serializer, localeService, aippModelCenter, promptBuilderChain); + LlmComponent llmComponent = new LlmComponent(flowInstanceService, + toolProvider, + agent, + aippLogService, + null, + serializer, + aippModelCenter, + promptBuilderChain, + this.appTaskInstanceService); // mock CountDownLatch countDownLatch = mockResumeFlow(flowInstanceService); Mockito.doAnswer((Answer) invocation -> { - InstanceDeclarationInfo info = ObjectUtils.cast(invocation.getArgument(2)); - Map value = info.getInfo().getValue(); - String childInstanceId = ObjectUtils.cast(value.get(AippConst.INST_CHILD_INSTANCE_ID)); + AppTaskInstance instance = ObjectUtils.cast(invocation.getArgument(0)); + String childInstanceId = instance.getEntity().getChildInstanceId(); Assertions.assertEquals("tool_async", childInstanceId); Map businessData = new HashMap<>(); businessData.put(AippConst.PARENT_INSTANCE_ID, TestUtils.DUMMY_FLOW_INSTANCE_ID); businessData.put(AippConst.BS_AIPP_OUTPUT_IS_NEEDED_LLM, false); llmComponent.callback(TestUtils.buildFlowDataWithExtraConfig(businessData, null)); return null; - }).when(metaInstanceService).patchMetaInstance(any(), any(), any(), any()); + }).when(this.appTaskInstanceService).update(any(), any()); // run llmComponent.handleTask(TestUtils.buildFlowDataWithExtraConfig(buildLlmTestData(), null)); @@ -372,6 +377,7 @@ void shouldOkWhenUseWorkflowNoReturn() throws InterruptedException { @Test void shouldOkWhenUseWorkflowNormalReturn() throws InterruptedException { // stub + this.prepareModel(); AtomicBoolean flag = new AtomicBoolean(false); List prompts = new ArrayList<>(); AiProcessFlow testAgent = AiFlows.create().just(m -> prompts.add(m)).map(m -> { @@ -385,19 +391,25 @@ void shouldOkWhenUseWorkflowNormalReturn() throws InterruptedException { return ObjectUtils.cast(chatMessages); }).just(m -> flag.set(true)).close(); AbstractAgent agent = this.buildStubAgent(testAgent); - LlmComponent llmComponent = new LlmComponent(flowInstanceService, metaInstanceService, toolProvider, agent, - this.aippLogService, null, client, serializer, localeService, aippModelCenter, promptBuilderChain); + LlmComponent llmComponent = new LlmComponent(flowInstanceService, + toolProvider, + agent, + aippLogService, + null, + serializer, + aippModelCenter, + promptBuilderChain, + this.appTaskInstanceService); // mock CountDownLatch countDownLatch = mockResumeFlow(flowInstanceService); Mockito.doAnswer((Answer) invocation -> { - InstanceDeclarationInfo info = ObjectUtils.cast(invocation.getArgument(2)); - Map value = info.getInfo().getValue(); - String childInstanceId = ObjectUtils.cast(value.get(AippConst.INST_CHILD_INSTANCE_ID)); - generateBusinessDataAndCallBack(childInstanceId, value, llmComponent); + AppTaskInstance instance = ObjectUtils.cast(invocation.getArgument(0)); + generateBusinessDataAndCallBack(instance.getEntity().getChildInstanceId(), instance.getEntity().getInfos(), + llmComponent); return null; - }).when(metaInstanceService).patchMetaInstance(any(), any(), any(), any()); + }).when(this.appTaskInstanceService).update(any(), any()); Mockito.when(toolProvider.getTool(any())).thenReturn(Collections.emptyList()); // run @@ -428,10 +440,15 @@ private void generateBusinessDataAndCallBack(String childInstanceId, Map testAgent = AiFlows.create().just(m -> { - List messages = m.messages(); - Assertions.assertEquals(2, messages.size()); - }).map(m -> ObjectUtils.cast(ChatMessages.from(new AiMessage("bad")))).close(); + this.prepareModel(); + AiProcessFlow testAgent = + AiFlows.create() + .just(m -> { + List messages = m.messages(); + Assertions.assertEquals(2, messages.size()); + }) + .map(m -> ObjectUtils.cast(ChatMessages.from(new AiMessage("bad")))) + .close(); AbstractAgent agent = this.buildStubAgent(testAgent); LlmComponent llmComponent = getLlmComponent(agent); @@ -450,21 +467,20 @@ void shouldOkWhenUseMaxMemoryRounds() throws InterruptedException { @Test void shouldFailLLmNodeWhenHandleGivenWorkflowException() throws InterruptedException { // given + this.prepareModel(); AbstractAgent agent = this.getWaterFlowAgent(this.buildChatStreamModel(null), true); LlmComponent llmComponent = getLlmComponent(agent); CountDownLatch countDownLatch = mockFailAsyncJob(flowInstanceService); Mockito.doAnswer((Answer) invocation -> { - InstanceDeclarationInfo info = ObjectUtils.cast(invocation.getArgument(2)); - Map value = info.getInfo().getValue(); - String childInstanceId = ObjectUtils.cast(value.get(AippConst.INST_CHILD_INSTANCE_ID)); - Assertions.assertNotNull(childInstanceId); + AppTaskInstance instance = ObjectUtils.cast(invocation.getArgument(0)); + Assertions.assertNotNull(instance.getEntity().getChildInstanceId()); Map businessData = new HashMap<>(); businessData.put(AippConst.PARENT_INSTANCE_ID, TestUtils.DUMMY_FLOW_INSTANCE_ID); llmComponent.handleException("nodeId", TestUtils.buildFlowDataWithExtraConfig(businessData, null), new FlowErrorInfo(123, "error", null, null, null, null)); return null; - }).when(metaInstanceService).patchMetaInstance(any(), any(), any(), any()); + }).when(this.appTaskInstanceService).update(any(), any()); // when llmComponent.handleTask(TestUtils.buildFlowDataWithExtraConfig(buildLlmTestData(), null)); @@ -498,7 +514,26 @@ void shouldFailWhenDebugAndLlmNotAvailable() throws InterruptedException { } private LlmComponent getLlmComponent(final AbstractAgent agent) { - return new LlmComponent(flowInstanceService, metaInstanceService, toolProvider, agent, aippLogService, - aippLogStreamService, client, serializer, localeService, aippModelCenter, promptBuilderChain); + return new LlmComponent(flowInstanceService, + toolProvider, + agent, + aippLogService, + aippLogStreamService, + serializer, + aippModelCenter, + promptBuilderChain, + this.appTaskInstanceService); + } + + private void prepareModel() { + Mockito.when(toolProvider.getTool(any())).thenReturn(Collections.emptyList()); + doAnswer(invocationOnMock -> { + Object advice = invocationOnMock.getArgument(0); + Object context = invocationOnMock.getArgument(1); + return new PromptBuilderStub().build(ObjectUtils.cast(advice), ObjectUtils.cast(context)); + }).when(this.promptBuilderChain).build(any(), any()); + + when(this.aippModelCenter.getModelAccessInfo(any(), any(), any())).thenReturn( + ModelAccessInfo.builder().tag("tag").build()); } } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponentTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponentTest.java index 2cebe45813..7f2f144a88 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponentTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/fitable/NaiveRAGComponentTest.java @@ -12,10 +12,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import modelengine.jade.app.engine.knowledge.service.KnowledgeBaseService; - import modelengine.fit.jober.aipp.TestUtils; import modelengine.fit.jober.aipp.util.DataUtils; +import modelengine.jade.app.engine.knowledge.service.KnowledgeBaseService; import modelengine.fitframework.util.StringUtils; import org.jetbrains.annotations.NotNull; @@ -42,15 +41,10 @@ @ExtendWith(MockitoExtension.class) public class NaiveRAGComponentTest { private static final String NAIVE_RAG_KNOWLEDGE_KEY = "knowledge"; - private static final String NAIVE_RAG_QUERY_KEY = "query"; - private static final String NAIVE_RAG_MAXIMUM_KEY = "maximum"; - private static final String NAIVE_RAG_OUTPUT = "retrievalOutput"; - private static final String DUMMY_QUERY = "This is query."; - private static final Integer DUMMY_MAXIMUM = 3; @Mock @@ -107,8 +101,8 @@ void shouldOkWhenUseKnowledge() { Map exceptResult = new HashMap() {{ put("retrievalOutput", exceptNaiveRAGOutput); }}; - when(this.knowledgeBaseServiceMock.vectorSearchKnowledgeTable(any())).thenReturn( - Arrays.asList(exceptNaiveRAGOutput)); + when(this.knowledgeBaseServiceMock.vectorSearchKnowledgeTable(any())) + .thenReturn(Arrays.asList(exceptNaiveRAGOutput)); List> flowData = TestUtils.buildFlowDataWithExtraConfig(businessData, null); List> resultFlowData = this.naiveRAGComponent.handleTask(flowData); Map resultBusinessData = DataUtils.getBusiness(resultFlowData); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/init/AippComponentInitiatorTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/init/AippComponentInitiatorTest.java index 467e9bfd66..d3e3e925b3 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/init/AippComponentInitiatorTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/init/AippComponentInitiatorTest.java @@ -37,25 +37,16 @@ @DisplayName("测试 AippComponentInitiatorTest") public class AippComponentInitiatorTest { private static final String RESOURCE_PATH = "component"; - private static final String FLOW_ZH_PATH = "/flow_zh.json"; - private static final String FLOW_EN_PATH = "/flow_en.json"; - private static final String FORM_ZH_PATH = "/form_zh.json"; - private static final String FORM_EN_PATH = "/form_en.json"; - private static final String BASIC_NODE_ZH_PATH = "/basic_node_zh.json"; - private static final String BASIC_NODE_EN_PATH = "/basic_node_en.json"; - private static final String JSON_STRING = "{\"groups\": [], \"items\": []}"; private AippComponentInitiator aippComponentInitiator; - private Plugin plugin; - private MockedStatic resourceLoaderMockedStatic; @BeforeEach diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AippChatServiceAdapterImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AippChatServiceAdapterImplTest.java index c496c99d4c..3b877d89f1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AippChatServiceAdapterImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AippChatServiceAdapterImplTest.java @@ -41,11 +41,9 @@ @DisplayName("测试 AippChatServiceAdapterImpl") public class AippChatServiceAdapterImplTest { private final AippChatService aippChatService = mock(AippChatService.class); - private final ObjectSerializer serializer = new JacksonObjectSerializer(null, null, null); - - private final AippChatServiceAdapterImpl aippChatServiceAdapterImpl = new AippChatServiceAdapterImpl( - aippChatService, serializer); + private final AippChatServiceAdapterImpl aippChatServiceAdapterImpl = + new AippChatServiceAdapterImpl(aippChatService, serializer); @Test @DisplayName("当查询会话列表时,返回结果正确。") diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderAppServiceAdapterImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderAppServiceAdapterImplTest.java index 621b7b141b..6192346bf0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderAppServiceAdapterImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderAppServiceAdapterImplTest.java @@ -31,9 +31,8 @@ @DisplayName("测试 AppBuilderAppServiceAdapterImpl") public class AppBuilderAppServiceAdapterImplTest { private final AppBuilderAppService appBuilderAppService = mock(AppBuilderAppService.class); - - private final AppBuilderAppServiceAdapterImpl appBuilderAppServiceAdapterImpl = new AppBuilderAppServiceAdapterImpl( - appBuilderAppService); + private final AppBuilderAppServiceAdapterImpl appBuilderAppServiceAdapterImpl = + new AppBuilderAppServiceAdapterImpl(appBuilderAppService); @Test @DisplayName("测试应用元数据类转换为适配器类。") @@ -51,11 +50,11 @@ void testDtoConvertToAdapter() { when(appMetadata2.getType()).thenReturn("testType2"); when(appMetadata2.getName()).thenReturn("testName2"); - Rsp> rsp = Rsp.ok( - RangedResultSet.create(Arrays.asList(appBuilderAppMetadataDto1, appBuilderAppMetadataDto2), + Rsp> rsp = + Rsp.ok(RangedResultSet.create(Arrays.asList(appBuilderAppMetadataDto1, appBuilderAppMetadataDto2), new RangeResult(1, 2, 3))); - RangedResultSet result = appBuilderAppServiceAdapterImpl.appMetadataDtoConvertToAdapter( - rsp.getData()); + RangedResultSet result = + appBuilderAppServiceAdapterImpl.appMetadataDtoConvertToAdapter(rsp.getData()); assertThat(result.getResults()).hasSize(2) .extracting(AppMetadata::getName) .containsExactly("testName1", "testName2"); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImplTest.java index ea32363340..d905df124d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/AppBuilderPromptServiceAdapterImplTest.java @@ -39,11 +39,9 @@ @DisplayName("测试 AppBuilderPromptServiceAdapterImpl") public class AppBuilderPromptServiceAdapterImplTest { private final ObjectSerializer serializer = new JacksonObjectSerializer(null, null, null); - private final AppBuilderPromptService appBuilderPromptService = mock(AppBuilderPromptService.class); - - private final AppBuilderPromptServiceAdapterImpl appBuilderPromptServiceAdapter - = new AppBuilderPromptServiceAdapterImpl(appBuilderPromptService, serializer); + private final AppBuilderPromptServiceAdapterImpl appBuilderPromptServiceAdapter = + new AppBuilderPromptServiceAdapterImpl(appBuilderPromptService, serializer); @Test @DisplayName("当查询灵感类别时返回正确结果。") @@ -55,8 +53,11 @@ void shouldReturnOkListPromptCategories() { categoryDto1.setId("Id1"); categoryDto1.setTitle("Title1"); categoryDto1.setDisable(false); - categoryDto1.setChildren( - Collections.singletonList(new AppBuilderPromptCategoryDto("Title1", "Id1", null, false, null))); + categoryDto1.setChildren(Collections.singletonList(new AppBuilderPromptCategoryDto("Title1", + "Id1", + null, + false, + null))); AppBuilderPromptCategoryDto categoryDto2 = new AppBuilderPromptCategoryDto(); categoryDto2.setId("Id2"); categoryDto2.setTitle("Title2"); @@ -66,8 +67,8 @@ void shouldReturnOkListPromptCategories() { Rsp> mockResponse = Rsp.ok(categoryDtos); when(appBuilderPromptService.listPromptCategories(appId, operationContext, isDebug)).thenReturn(mockResponse); - List result = appBuilderPromptServiceAdapter.listPromptCategories(appId, operationContext, - isDebug); + List result = + appBuilderPromptServiceAdapter.listPromptCategories(appId, operationContext, isDebug); assertThat(result).isNotNull().hasSize(2); PromptCategory adapter1 = result.get(0); assertThat(adapter1).extracting(PromptCategory::getId, PromptCategory::getTitle) @@ -84,13 +85,25 @@ void shouldReturnOkListPromptCategories() { @DisplayName("当灵感大全数据类转换成适配器类时返回正确结果。") void shouldReturnOkWhenDtoConvertToAdapter() { AppBuilderPromptDto dto = new AppBuilderPromptDto(); - dto.setInspirations(Collections.singletonList( - new AppBuilderPromptDto.AppBuilderInspirationDto("name", "id", "prompt", "promptTemplate", "category", - "description", true, Arrays.asList( - new AppBuilderPromptDto.AppBuilderPromptVarDataDto("key", "var", "varType", "sourceType", - "sourceInfo", true), - new AppBuilderPromptDto.AppBuilderPromptVarDataDto("key2", "var2", "varType2", "sourceType2", - "sourceInfo2", true))))); + dto.setInspirations(Collections.singletonList(new AppBuilderPromptDto.AppBuilderInspirationDto("name", + "id", + "prompt", + "promptTemplate", + "category", + "description", + true, + Arrays.asList(new AppBuilderPromptDto.AppBuilderPromptVarDataDto("key", + "var", + "varType", + "sourceType", + "sourceInfo", + true), + new AppBuilderPromptDto.AppBuilderPromptVarDataDto("key2", + "var2", + "varType2", + "sourceType2", + "sourceInfo2", + true))))); dto.setCategories(new ArrayList<>()); PromptInfo result = appBuilderPromptServiceAdapter.appBuilderPromptDtoConvertToAdapter(dto); assertThat(result.getInspirations().get(0).getId()).isEqualTo("id"); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImplTest.java index cf842b7b51..2c2d45f209 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/northbound/FileServiceAdapterImplTest.java @@ -8,8 +8,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -17,13 +17,14 @@ import modelengine.fit.http.entity.FileEntity; import modelengine.fit.http.entity.PartitionedEntity; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.FileRspDto; import modelengine.fit.jober.aipp.dto.chat.FileUploadInfo; import modelengine.fit.jober.aipp.genericable.adapter.FileServiceAdapter; +import modelengine.fit.jober.aipp.po.AppBuilderAppPo; +import modelengine.fit.jober.aipp.service.AppBuilderAppServiceImplTest; import modelengine.fit.jober.aipp.service.FileService; -import modelengine.fit.jober.aipp.util.MetaUtils; import org.junit.Test; import org.junit.jupiter.api.BeforeEach; @@ -41,21 +42,14 @@ @DisplayName("测试 FileServiceAdapterImpl") public class FileServiceAdapterImplTest { private final FileService fileService = mock(FileService.class); - private final String tenantId = UUID.randomUUID().toString(); - - private final String fileName = "testFile"; - - private final String appId = "testApp"; - private final PartitionedEntity partitionedEntity = mock(PartitionedEntity.class); + private final AppVersionService appVersionService = mock(AppVersionService.class); + private final FileServiceAdapter fileServiceAdapterImpl = new FileServiceAdapterImpl(fileService, + appVersionService); private final FileEntity fileEntity = mock(FileEntity.class); - private final MetaService metaService = mock(MetaService.class); - - private final FileServiceAdapter fileServiceAdapterImpl = new FileServiceAdapterImpl(fileService, metaService); - private OperationContext operationContext; @BeforeEach @@ -66,15 +60,17 @@ void setUp() { @Test @DisplayName("测试上传文件。") public void testUploadFile() throws IOException, AippTaskNotFoundException { - String aippId = "testAippId"; - mockStatic(MetaUtils.class); - when(MetaUtils.getAippIdByAppId(this.metaService, appId, operationContext)).thenReturn(aippId); + String appSuiteId = "appSuiteId"; + String appId = "testApp"; + AppBuilderAppPo appPo = AppBuilderAppPo.builder().appId(appId).appSuiteId(appSuiteId).build(); + when(appVersionService.retrieval(anyString())).thenReturn(AppBuilderAppServiceImplTest.mockAppVersion(appPo)); FileRspDto fileRspDto = new FileRspDto(); when(fileService.uploadFile(any(), any(), any(), any(), any())).thenReturn(fileRspDto); + String fileName = "testFile"; FileUploadInfo result = fileServiceAdapterImpl.uploadFile(operationContext, tenantId, fileName, appId, - partitionedEntity); + partitionedEntity); assertThat(result.getFileName()).isEqualTo(fileRspDto.getFileName()); assertThat(result.getFilePath()).isEqualTo(fileRspDto.getFilePath()); - verify(fileService, times(1)).uploadFile(operationContext, tenantId, fileName, aippId, fileEntity); + verify(fileService, times(1)).uploadFile(operationContext, tenantId, fileName, appSuiteId, fileEntity); } } \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/AppTemplateRepositoryTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/AppTemplateRepositoryTest.java index d45272cdcd..2bae6a3f48 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/AppTemplateRepositoryTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/AppTemplateRepositoryTest.java @@ -30,8 +30,8 @@ */ public class AppTemplateRepositoryTest extends DatabaseBaseTest { @Fit - private final AppTemplateMapper templateMapper = sqlSessionManager.openSession(true) - .getMapper(AppTemplateMapper.class); + private final AppTemplateMapper templateMapper = + sqlSessionManager.openSession(true).getMapper(AppTemplateMapper.class); private AppTemplateRepository templateRepository; @@ -75,11 +75,14 @@ void testQueryTemplateWithAppType() { @Test @DisplayName("测试查询应用模板并排序") void testQueryTemplateWithOrderBy() { - TemplateQueryCondition cond = TemplateQueryCondition.builder().orderBy("usage").offset(0).limit(8).build(); + TemplateQueryCondition cond = TemplateQueryCondition.builder() + .orderBy("usage") + .offset(0) + .limit(8) + .build(); List result = this.templateRepository.selectWithCondition(cond); assertThat(result).hasSize(6) .element(0) - .extracting(AppTemplate::getId) - .isEqualTo("3e29eb82f92f43259b4c514ddb96c0a8"); + .extracting(AppTemplate::getId).isEqualTo("3e29eb82f92f43259b4c514ddb96c0a8"); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java new file mode 100644 index 0000000000..8b71beafec --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AippInstanceLogRepositoryImplTest.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository.impl; + +import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; +import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository; +import modelengine.fit.jober.aipp.service.DatabaseBaseTest; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +/** + * {@link AippInstanceLogRepositoryImpl} 对应测试类。 + * + * @author 杨祥宇 + * @since 2025-04-10 + */ +public class AippInstanceLogRepositoryImplTest extends DatabaseBaseTest { + private final AippLogMapper mapper = sqlSessionManager.openSession(true).getMapper(AippLogMapper.class); + private AippInstanceLogRepository repo; + + @BeforeEach + void setUp() { + this.repo = new AippInstanceLogRepositoryImpl(this.mapper); + } + + @Test + @DisplayName("测试成功刪除调试信息") + void testForceDeleteInstanceLogsSuccess() { + AippLogCreateDto dto = AippLogCreateDto.builder() + .logId("1") + .aippId("1") + .aippType("PREVIEW") + .instanceId("1") + .logData("{}") + .logType("QUESTION") + .createUserAccount("yyy") + .build(); + this.mapper.insertOne(dto); + this.repo.forceDeleteInstanceLogs(Collections.singletonList(1L)); + List expirePreviewInstanceLogs = this.repo.selectByLogIds(Collections.singletonList(1L)); + Assertions.assertEquals(0, expirePreviewInstanceLogs.size()); + } + + @Test + @DisplayName("测试成功获取过期调试信息") + void testGetExpiredPreviewInstanceLogsSuccess() { + AippLogCreateDto dto = AippLogCreateDto.builder() + .logId("2") + .aippId("2") + .aippType("PREVIEW") + .instanceId("2") + .logData("{}") + .logType("QUESTION") + .createUserAccount("yyy") + .build(); + this.mapper.insertOne(dto); + List expirePreviewInstanceLogs = this.repo.selectByLogIds(Collections.singletonList(2L)); + Assertions.assertEquals(1, expirePreviewInstanceLogs.size()); + } + + @Test + @DisplayName("测试根据对话 id 成功查询对话详细信息") + void testSelectByLogIdSuccess() { + AippLogCreateDto dto = AippLogCreateDto.builder() + .logId("3") + .aippId("3") + .aippType("PREVIEW") + .instanceId("3") + .logData("{}") + .logType("QUESTION") + .createUserAccount("yyy") + .build(); + this.mapper.insertOne(dto); + List aippInstLogs = this.repo.selectByLogIds(Collections.singletonList(3L)); + Assertions.assertEquals(1, aippInstLogs.size()); + Assertions.assertEquals("3", aippInstLogs.get(0).getInstanceId()); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java new file mode 100644 index 0000000000..2c8bea58bc --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/repository/impl/AppBuilderRuntimeInfoRepositoryImplTest.java @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.repository.impl; + +import modelengine.fit.jober.aipp.domain.AppBuilderRuntimeInfo; +import modelengine.fit.jober.aipp.mapper.AppBuilderRuntimeInfoMapper; +import modelengine.fit.jober.aipp.repository.AppBuilderRuntimeInfoRepository; +import modelengine.fit.jober.aipp.service.DatabaseBaseTest; +import modelengine.fit.runtime.entity.Parameter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; + +/** + * {@link AppBuilderRuntimeInfoRepositoryImpl} 对应测试类。 + * + * @author 杨祥宇 + * @since 2025-04-10 + */ +class AppBuilderRuntimeInfoRepositoryImplTest extends DatabaseBaseTest { + private final AppBuilderRuntimeInfoMapper mapper = + sqlSessionManager.openSession(true).getMapper(AppBuilderRuntimeInfoMapper.class); + private AppBuilderRuntimeInfoRepository runtimeInfoRepository; + + @BeforeEach + void setUp() { + this.runtimeInfoRepository = new AppBuilderRuntimeInfoRepositoryImpl(mapper); + } + + @Test + @DisplayName("测试成功获取超期的运行时信息") + void testGetExpiredRuntimeInfosSuccess() { + AppBuilderRuntimeInfo info = genRuntimeInfo(); + this.runtimeInfoRepository.insertOne(info); + List expiredRuntimeInfos = this.runtimeInfoRepository.selectByTraceId("0"); + Assertions.assertEquals(0, expiredRuntimeInfos.size()); + } + + private AppBuilderRuntimeInfo genRuntimeInfo() { + Parameter parameter = new Parameter(); + return AppBuilderRuntimeInfo.builder() + .traceId("1") + .flowDefinitionId("1") + .instanceId("1") + .nodeId("1") + .nodeType("PREVIEW") + .startTime(1) + .endTime(2) + .status("ERROR") + .parameters(List.of(parameter)) + .createAt(LocalDateTime.now()) + .updateAt(LocalDateTime.now()) + .build(); + } + + @Test + @DisplayName("测试成功根据 id 列表删除运行信息") + void testDeleteRuntimeInfosSuccess() { + this.runtimeInfoRepository.insertOne(genRuntimeInfo()); + this.mapper.deleteRuntimeInfos(Arrays.asList(1L, 2L)); + List expiredRuntimeInfos = this.runtimeInfoRepository.selectByTraceId("1"); + Assertions.assertEquals(0, expiredRuntimeInfos.size()); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippChatServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippChatServiceTest.java index ddc0eaa39a..8f37f42f09 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippChatServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippChatServiceTest.java @@ -13,8 +13,9 @@ import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; +import modelengine.fit.jober.aipp.domains.appversion.repository.AppVersionRepository; +import modelengine.fit.jober.aipp.domains.chat.repository.AppChatRepository; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; import modelengine.fit.jober.aipp.dto.chat.QueryChatRequest; import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; import modelengine.fit.jober.aipp.dto.chat.QueryChatRspDto; @@ -23,14 +24,12 @@ import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; import modelengine.fit.jober.aipp.service.impl.AippChatServiceImpl; -import modelengine.fit.jober.common.RangeResult; import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.annotation.Fit; import modelengine.fitframework.test.annotation.FitTestWithJunit; import modelengine.fitframework.test.annotation.Mock; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import java.sql.Timestamp; import java.time.LocalDateTime; @@ -57,18 +56,31 @@ public class AippChatServiceTest { @Mock private AppBuilderAppMapper appBuilderAppMapper; + @Mock + private AppTaskService appTaskService; + @Mock private AippLogService aippLogService; + @Mock + private AppVersionRepository appVersionRepository; + @Mock private AippLogMapper aippLogMapper; + @Mock + private AppChatRepository appChatRepository; + @Mock private AppBuilderAppRepository appRepository; @Test void testQueryChatList() { - QueryChatRequest body = QueryChatRequest.builder().appId("test-appid").offset(0).limit(10).build(); + QueryChatRequest body = QueryChatRequest.builder() + .appId("test-appid") + .offset(0) + .limit(10) + .build(); QueryChatRsp rsp = QueryChatRsp.builder() .attributes("{\"state\": \"active\", \"instId\": \"f2070d7ee84c4aa787a609807dc75957\"}") .updateTime(Timestamp.valueOf(LocalDateTime.now()).toString()) @@ -76,18 +88,9 @@ void testQueryChatList() { when(aippChatMapper.selectChatList(any(), any(), any())).thenReturn(Collections.singletonList(rsp)); when(aippChatMapper.selectMsgByInstanceIds(any())).thenReturn(new ArrayList<>()); when(aippChatMapper.getChatListCount(any(), anyString(), anyString())).thenReturn(1L); - Mockito.when( - this.metaService.list(Mockito.any(MetaFilter.class), Mockito.eq(false), Mockito.eq(0L), Mockito.eq(10), - Mockito.any(OperationContext.class))) - .thenReturn(new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 10, 1))); - RangedResultSet rangedResultSet = aippChatService.queryChatList(body, new OperationContext()); + RangedResultSet rangedResultSet = + aippChatService.queryChatList(body, new OperationContext()); assertThat(rangedResultSet.getResults().size()).isEqualTo(1); assertThat(rangedResultSet.getResults().get(0).getMsgId()).isEqualTo("f2070d7ee84c4aa787a609807dc75957"); } - - private Meta mockMeta() { - Meta meta = new Meta(); - meta.setId("metaId"); - return meta; - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippFlowServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippFlowServiceTest.java index 42d61720f6..7ac4532f8e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippFlowServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippFlowServiceTest.java @@ -10,78 +10,50 @@ import static modelengine.fit.jober.common.ErrorCodes.INPUT_PARAM_IS_EMPTY; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; -import modelengine.fit.dynamicform.entity.FormMetaItem; import modelengine.fit.jade.waterflow.FlowsService; import modelengine.fit.jade.waterflow.dto.FlowInfo; -import modelengine.fit.jade.waterflow.entity.FlowNodeFormInfo; -import modelengine.fit.jade.waterflow.entity.FlowNodeInfo; import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaDeclarationInfo; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.property.MetaPropertyDeclarationInfo; import modelengine.fit.jober.aipp.common.PageResponse; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippForbiddenException; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.condition.AippQueryCondition; import modelengine.fit.jober.aipp.condition.PaginationCondition; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.convertor.TaskPropertyConvertor; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; import modelengine.fit.jober.aipp.dto.AippCreateDto; import modelengine.fit.jober.aipp.dto.AippDetailDto; import modelengine.fit.jober.aipp.dto.AippDto; import modelengine.fit.jober.aipp.dto.AippOverviewRspDto; import modelengine.fit.jober.aipp.enums.AippMetaStatusEnum; -import modelengine.fit.jober.aipp.enums.AppCategory; -import modelengine.fit.jober.aipp.enums.JaneCategory; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; -import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fit.jober.aipp.service.impl.AippFlowServiceImpl; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fit.jober.common.exceptions.JobberException; import modelengine.fit.jober.common.exceptions.JobberParamException; -import modelengine.fit.waterflow.domain.enums.FlowNodeType; -import modelengine.fitframework.util.MapBuilder; -import modelengine.fitframework.util.ObjectUtils; -import modelengine.fitframework.util.StringUtils; -import modelengine.fel.tool.service.ToolService; -import modelengine.jade.store.entity.transfer.PluginData; -import modelengine.jade.store.service.AppService; +import modelengine.fit.jober.entity.task.TaskProperty; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.stubbing.Answer; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.Optional; @ExtendWith(MockitoExtension.class) class AippFlowServiceTest { @@ -96,26 +68,11 @@ class AippFlowServiceTest { @InjectMocks private AippFlowServiceImpl aippFlowServiceImpl; - @Mock - private MetaService metaServiceMock; - - @Mock - private AppBuilderAppFactory appFactory; - @Mock private FlowsService flowsServiceMock; @Mock - private AppBuilderFormRepository appBuilderFormRepositoryMock; - - @Mock - private AppService appService; - - @Mock - private ToolService toolService; - - @Mock - private AppBuilderAppMapper appBuilderAppMapperMock; + private AppTaskService appTaskService; @BeforeEach void setUp() { @@ -172,38 +129,25 @@ OperationContext GenTestOperationContext() { @Disabled void testQueryAippDetailThenOk() { final String defaultVersion = "1.0.0"; - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, DUMMY_FLOW_CONFIG_ID); - attributes.put(AippConst.ATTR_VERSION_KEY, defaultVersion); - attributes.put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()); - - Meta meta = new Meta(); - meta.setId("testAippId"); - meta.setName("testMeta"); - meta.setCreator("testUser"); - meta.setAttributes(attributes); - FlowInfo flowInfo = new FlowInfo(); - flowInfo.setFlowId((String) attributes.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)); - flowInfo.setVersion((String) attributes.get(AippConst.ATTR_VERSION_KEY)); + flowInfo.setFlowId(DUMMY_FLOW_CONFIG_ID); + flowInfo.setVersion(defaultVersion); flowInfo.setConfigData("{\"id\": \"testFlowConfigId\"}"); flowInfo.setFlowDefinitionId("testFlowDefinitionId"); - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(meta), 0L, 1, 1); - when(metaServiceMock.list(argThat(metaFilter -> metaFilter.getCategories().size() == 1 - && metaFilter.getCategories().get(0).equals(JaneCategory.AIPP.name()) - && metaFilter.getMetaIds().size() == 1 && metaFilter.getMetaIds().get(0).equals("testAippId") - && metaFilter.getVersions().size() == 1 && metaFilter.getVersions().get(0).equals(defaultVersion)), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); - when(flowsServiceMock.getFlows(eq((String) attributes.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)), - eq((String) attributes.get(AippConst.ATTR_VERSION_KEY)), - any(OperationContext.class))).thenReturn(flowInfo); - - Rsp rsp = - aippFlowServiceImpl.queryAippDetail(meta.getId(), defaultVersion, GenTestOperationContext()); + when(this.appTaskService.getLatest(any(), any(), any())).thenReturn(Optional.of(AppTask.asEntity() + .setAppSuiteId("testAippId") + .setName("testMeta") + .setCreator("testUser") + .setFlowConfigId(DUMMY_FLOW_CONFIG_ID) + .setVersion(defaultVersion) + .setStatus(AippMetaStatusEnum.INACTIVE.getCode()) + .build())); + + when(flowsServiceMock.getFlows(anyString(), anyString(), any(OperationContext.class))).thenReturn(flowInfo); + + Rsp rsp = aippFlowServiceImpl.queryAippDetail("testAippId", defaultVersion, + GenTestOperationContext()); Assertions.assertEquals(0, rsp.getCode()); Assertions.assertTrue(rsp.getData().getFlowViewData().containsKey("id")); @@ -213,31 +157,20 @@ void testQueryAippDetailThenOk() { @Test void testQueryAippDetailWithGetFlowsErrorThenFail() { final String defaultVersion = "1.0.0"; - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, DUMMY_FLOW_CONFIG_ID); - attributes.put(AippConst.ATTR_VERSION_KEY, defaultVersion); - attributes.put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()); - - Meta meta = new Meta(); - meta.setId("testAippId"); - meta.setName("testMeta"); - meta.setCreator("testUser"); - meta.setAttributes(attributes); - - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(meta), 0L, 1, 1); - when(metaServiceMock.list(any(MetaFilter.class), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); + when(this.appTaskService.getLatest(any(), any(), any())).thenReturn(Optional.of(AppTask.asEntity() + .setAppSuiteId("testAippId") + .setName("testMeta") + .setCreator("testUser") + .setFlowConfigId(DUMMY_FLOW_CONFIG_ID) + .setVersion(defaultVersion) + .setStatus(AippMetaStatusEnum.INACTIVE.getCode()) + .build())); doThrow(new JobberParamException(INPUT_PARAM_IS_EMPTY, "flowId")).when(flowsServiceMock) - .getFlows(eq(ObjectUtils.cast(attributes.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY))), - eq(ObjectUtils.cast(attributes.get(AippConst.ATTR_VERSION_KEY))), - any(OperationContext.class)); + .getFlows(eq(DUMMY_FLOW_CONFIG_ID), eq(defaultVersion), any(OperationContext.class)); Assertions.assertThrows(AippException.class, () -> { - this.aippFlowServiceImpl.queryAippDetail(meta.getId(), defaultVersion, GenTestOperationContext()); + this.aippFlowServiceImpl.queryAippDetail("testAippId", defaultVersion, GenTestOperationContext()); }); } @@ -246,18 +179,9 @@ void testQueryAippDetailWithGetFlowsErrorThenFail() { void shouldSetDraftVersionWhenCallListAippWithInactiveAipp() { Meta expectMeta = GenerateInactiveMeta(); - when(metaServiceMock.list(any(MetaFilter.class), - eq(true), - eq(0L), - eq(10), - any(OperationContext.class))).thenReturn(RangedResultSet.create(Collections.singletonList(expectMeta), - 0L, - 10, - 1L)); - PageResponse rsp = - aippFlowServiceImpl.listAipp(AippQueryCondition.builder().name("testName").build(), - PaginationCondition.builder().pageNum(1).pageSize(10).build(), - GenTestOperationContext()); + PageResponse rsp = aippFlowServiceImpl.listAipp( + AippQueryCondition.builder().name("testName").build(), + PaginationCondition.builder().pageNum(1).pageSize(10).build(), GenTestOperationContext()); Assertions.assertEquals(1, rsp.getTotal()); List data = rsp.getItems(); @@ -298,29 +222,17 @@ void testCreateAippThenOk() { flowInfo.setVersion(DUMMY_FLOW_CONFIG_VERSION); when(flowsServiceMock.createFlows(any(String.class), any(OperationContext.class))).thenReturn(flowInfo); - when(metaServiceMock.create(any(MetaDeclarationInfo.class), any(OperationContext.class))).thenAnswer(var -> { - MetaDeclarationInfo info = var.getArgument(0); - Assertions.assertEquals(info.getName().getValue(), aipp.getName()); - Assertions.assertEquals(info.getAttributes().getValue().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY), - flowInfo.getFlowId()); - Assertions.assertEquals(info.getVersion().getValue(), flowInfo.getVersion()); - Assertions.assertEquals(info.getAttributes().getValue().get(AippConst.ATTR_META_STATUS_KEY), - AippMetaStatusEnum.INACTIVE.getCode()); - List props = info.getProperties().getValue(); + when(this.appTaskService.createTask(any(), any())).thenAnswer(var -> { + AppTask task = var.getArgument(0); + Assertions.assertEquals(task.getEntity().getName(), aipp.getName()); + Assertions.assertEquals(task.getEntity().getFlowConfigId(), flowInfo.getFlowId()); + Assertions.assertEquals(task.getEntity().getVersion(), flowInfo.getVersion()); + Assertions.assertEquals(task.getEntity().getStatus(), AippMetaStatusEnum.INACTIVE.getCode()); + List props = task.getEntity().getProperties(); for (int i = 0; i < props.size(); i++) { - Assertions.assertEquals(props.get(i).getName().getValue(), AippConst.STATIC_META_ITEMS.get(i).getKey()); + Assertions.assertEquals(props.get(i).getName(), AippConst.STATIC_META_ITEMS.get(i).getKey()); } - - Meta meta = new Meta(); - meta.setName(info.getName().getValue()); - meta.setId("testMetaId"); - meta.setAttributes(info.getAttributes().getValue()); - meta.setProperties(info.getProperties() - .getValue() - .stream() - .map(TaskPropertyConvertor.INSTANCE::fromMetaPropertyDeclarationInfo) - .collect(Collectors.toList())); - return meta; + return task.getEntity().clone().setAppSuiteId("testMetaId").build(); }); AippCreateDto rsp = aippFlowServiceImpl.create(aipp, GenTestOperationContext()); @@ -335,15 +247,6 @@ void testUpdateAippWithInvalidConditionThenFail() { String aippId = expectMeta.getId(); // test update active aipp - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(expectMeta), 0L, 1, 1); - when(metaServiceMock.list(argThat(metaFilter -> metaFilter.getCategories().size() == 1 - && metaFilter.getCategories().get(0).equals(JaneCategory.AIPP.name()) - && metaFilter.getMetaIds().size() == 1 && metaFilter.getMetaIds().get(0).equals("testId") - && metaFilter.getVersions().size() == 1 && metaFilter.getVersions() - .get(0) - .equals(DUMMY_META_VERSION_OLD)), eq(true), eq(0L), eq(1), any(OperationContext.class))).thenReturn( - mockResult); - Assertions.assertThrows(AippForbiddenException.class, () -> { AippDto aippDto = new AippDto(); aippDto.setId(aippId); @@ -384,23 +287,6 @@ void testUpdateAippThenOk() { String aippId = expectMeta.getId(); AippDto aipp = AippDto.builder().name(expectMeta.getName()).description("testDescription").build(); - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(expectMeta), 0L, 1, 1); - when(metaServiceMock.list(argThat(metaFilter -> metaFilter.getCategories().size() == 1 - && metaFilter.getCategories().get(0).equals(JaneCategory.AIPP.name()) - && metaFilter.getMetaIds().size() == 1 && metaFilter.getMetaIds().get(0).equals("testId") - && metaFilter.getVersions().isEmpty()), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); - doAnswer(var -> { - MetaDeclarationInfo info = var.getArgument(1); - Assertions.assertEquals(expectMeta.getName(), info.getName().getValue()); - Assertions.assertEquals(aipp.getDescription(), - info.getAttributes().getValue().get(AippConst.ATTR_DESCRIPTION_KEY)); - return null; - }).when(metaServiceMock).patch(any(), any(), any()); - aipp.setId(aippId); AippCreateDto rsp = aippFlowServiceImpl.update(aipp, GenTestOperationContext()); Assertions.assertEquals(rsp.getAippId(), aippId); @@ -411,8 +297,7 @@ void testUpdateAippThenOk() { aipp.setFlowViewData(flowData); doReturn(new FlowInfo()).when(flowsServiceMock) .updateFlows(eq((String) expectMeta.getAttributes().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)), - eq((String) expectMeta.getAttributes().get(AippConst.ATTR_VERSION_KEY)), - any(), + eq((String) expectMeta.getAttributes().get(AippConst.ATTR_VERSION_KEY)), any(), any(OperationContext.class)); rsp = this.aippFlowServiceImpl.update(aipp, GenTestOperationContext()); Assertions.assertEquals(rsp.getAippId(), aippId); @@ -420,37 +305,38 @@ void testUpdateAippThenOk() { @Test void testUpdateAippWithUpdateFlowsFailThenFail() { - Meta expectMeta = GenTestMeta(); - String aippId = expectMeta.getId(); - AippDto aipp = AippDto.builder().name(expectMeta.getName()).description("testDescription").build(); + AippDto aipp = AippDto.builder().name("testMeta").description("testDescription").version("1.0.0").build(); + when(this.appTaskService.getLatest(any(), any(), any())).thenReturn(Optional.of( + AppTask.asEntity() + .setName("testMeta") + .setAppSuiteId("testAippId") + .setCreator("testUser") + .setCreationTime(LocalDateTime.now()) + .setLastModificationTime(LocalDateTime.now()) + .setVersion(DUMMY_META_VERSION_OLD) + .setFlowConfigId(DUMMY_FLOW_CONFIG_ID) + .setAttributeVersion(DUMMY_FLOW_CONFIG_VERSION) + .setStatus(AippMetaStatusEnum.INACTIVE.getCode()) + .setAppId("appId1") + .build())); - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(expectMeta), 0L, 1, 1); - when(metaServiceMock.list(any(MetaFilter.class), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); doAnswer(var -> { - MetaDeclarationInfo info = var.getArgument(1); - Assertions.assertEquals(expectMeta.getName(), info.getName().getValue()); - Assertions.assertEquals(aipp.getDescription(), - info.getAttributes().getValue().get(AippConst.ATTR_DESCRIPTION_KEY)); + AppTask task = var.getArgument(0); + Assertions.assertEquals("testMeta", task.getEntity().getName()); + Assertions.assertEquals(aipp.getDescription(), task.getEntity().getDescription()); return null; - }).when(metaServiceMock).patch(any(), any(), any()); + }).when(this.appTaskService).updateTask(any(), any()); - aipp.setId(aippId); + aipp.setId("testAippId"); AippCreateDto rsp = aippFlowServiceImpl.update(aipp, GenTestOperationContext()); - Assertions.assertEquals(rsp.getAippId(), aippId); + Assertions.assertEquals(rsp.getAippId(), "testAippId"); // flowData not null or empty Map flowData = new HashMap<>(); flowData.put("testFlowKey", "testFlowData"); aipp.setFlowViewData(flowData); doThrow(new JobberException(FLOW_GRAPH_SAVE_ERROR, "1", "1.0.0")).when(flowsServiceMock) - .updateFlows(eq(ObjectUtils.cast(expectMeta.getAttributes().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY))), - eq(ObjectUtils.cast(expectMeta.getAttributes().get(AippConst.ATTR_VERSION_KEY))), - any(), - any(OperationContext.class)); + .updateFlows(eq(DUMMY_FLOW_CONFIG_ID), eq(DUMMY_META_VERSION_OLD), any(), any(OperationContext.class)); Assertions.assertThrows(AippException.class, () -> { this.aippFlowServiceImpl.update(aipp, GenTestOperationContext()); }); @@ -459,221 +345,25 @@ void testUpdateAippWithUpdateFlowsFailThenFail() { @Test void testDeleteAippWithDeleteFlowsErrorThenFail() { final String defaultVersion = "1.0.0"; - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_FLOW_CONFIG_ID_KEY, DUMMY_FLOW_CONFIG_ID); - attributes.put(AippConst.ATTR_VERSION_KEY, defaultVersion); - attributes.put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.INACTIVE.getCode()); - - Meta meta = new Meta(); - meta.setId("testAippId"); - meta.setName("testMeta"); - meta.setCreator("testUser"); - meta.setVersion(defaultVersion); - meta.setAttributes(attributes); - - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(meta), 0L, 1, 1); - when(metaServiceMock.list(any(MetaFilter.class), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); + when(this.appTaskService.getLatest(any(), any(), any())).thenReturn(Optional.of( + AppTask.asEntity() + .setName("testMeta") + .setAppSuiteId("testAippId") + .setCreator("testUser") + .setCreationTime(LocalDateTime.now()) + .setLastModificationTime(LocalDateTime.now()) + .setVersion(defaultVersion) + .setFlowConfigId(DUMMY_FLOW_CONFIG_ID) + .setAttributeVersion(defaultVersion) + .setStatus(AippMetaStatusEnum.INACTIVE.getCode()) + .setAppId("appId1") + .build())); doThrow(new JobberParamException(INPUT_PARAM_IS_EMPTY, "flowId")).when(flowsServiceMock) - .deleteFlows(eq(ObjectUtils.cast(attributes.get(AippConst.ATTR_FLOW_CONFIG_ID_KEY))), - eq(ObjectUtils.cast(attributes.get(AippConst.ATTR_VERSION_KEY))), - any(OperationContext.class)); + .deleteFlows(eq(DUMMY_FLOW_CONFIG_ID), eq(defaultVersion), any(OperationContext.class)); Assertions.assertThrows(AippException.class, () -> { - this.aippFlowServiceImpl.deleteAipp(meta.getId(), defaultVersion, GenTestOperationContext()); - }); - } - - FlowNodeFormInfo buildFlowNodeFormInfo() { - FlowNodeFormInfo expectedFormInfo = new FlowNodeFormInfo(); - expectedFormInfo.setFormId("testFormId"); - expectedFormInfo.setVersion("testFormVersion"); - - return expectedFormInfo; - } - - private void publishFlowsMock(FlowNodeFormInfo expectedFormInfo) { - doAnswer(var -> { - String flowDataStr = var.getArgument(2); - FlowInfo flowInfo = new FlowInfo(); - flowInfo.setConfigData(flowDataStr); - flowInfo.setFlowId(var.getArgument(0)); - flowInfo.setVersion(var.getArgument(1)); - - FlowNodeInfo nodeInfo = new FlowNodeInfo(); - nodeInfo.setType(FlowNodeType.START.getCode()); - nodeInfo.setFlowNodeForm(expectedFormInfo); - nodeInfo.setProperties(this.buildFlowNodesProperties()); - flowInfo.setFlowNodes(Collections.singletonList(nodeInfo)); - return flowInfo; - }).when(flowsServiceMock).publishFlows(any(), any(), any(), any(OperationContext.class)); - } - - private Map buildFlowNodesProperties() { - return MapBuilder.get() - .put("inputParams", Collections.singletonList(this.buildInputParams())) - .build(); - } - - private Map buildInputParams() { - Map inputValue = new HashMap<>(); - return MapBuilder.get() - .put("name", "input") - .put("value", Collections.singletonList(inputValue)) - .build(); - } - - private void publishBasicMock(Meta expectMeta) { - RangedResultSet mockResult = RangedResultSet.create(Collections.singletonList(expectMeta), 0L, 1, 1); - when(metaServiceMock.list(argThat(metaFilter -> metaFilter.getCategories().size() == 1 - && metaFilter.getCategories().get(0).equals(JaneCategory.AIPP.name()) - && metaFilter.getMetaIds().size() == 1 && metaFilter.getMetaIds().get(0).equals("testId") - && metaFilter.getVersions().isEmpty()), - eq(true), - eq(0L), - eq(1), - any(OperationContext.class))).thenReturn(mockResult); - FlowNodeFormInfo expectedFormInfo = buildFlowNodeFormInfo(); - publishFlowsMock(expectedFormInfo); - } - - AippDto GenAippDtoWithData(String name) { - Map flowData = new HashMap<>(); - flowData.put("testFlowKey", "testFlowData"); - flowData.put("id", DUMMY_FLOW_CONFIG_ID); - flowData.put("version", DUMMY_FLOW_CONFIG_VERSION); - return AippDto.builder().name(name).flowViewData(flowData).build(); - } - - private void getFlowsMockRetry() { - doAnswer(new Answer() { - private int times = 0; - - public Object answer(InvocationOnMock invocation) { - if (++times == 1) { - // 触发第一次 - FlowInfo info = new FlowInfo(); - info.setFlowId(DUMMY_FLOW_CONFIG_ID); - info.setVersion(DUMMY_FLOW_CONFIG_VERSION); - return info; - } - // 触发第二次 - throw new IllegalStateException(); - } - }).when(flowsServiceMock).getFlows(anyString(), anyString(), any()); - } - - @Test - @Disabled - void shouldOkWhenPreviewAipp() { - Meta expectMeta = GenTestMeta(); - expectMeta.getAttributes().put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.ACTIVE.getCode()); - - getFlowsMockRetry(); - publishFlowsMock(buildFlowNodeFormInfo()); - - FormMetaItem expectedFormMetaItem = new FormMetaItem("testKey", "testName", "TEXT", null, null); - - when(metaServiceMock.create(any(), any())).thenAnswer(var -> { - MetaDeclarationInfo info = var.getArgument(0); - Assertions.assertEquals(info.getName().getValue(), expectMeta.getName()); - Assertions.assertEquals(expectMeta.getAttributes().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY), - info.getAttributes().getValue().get(AippConst.ATTR_FLOW_CONFIG_ID_KEY)); - - String previewVersion = info.getVersion().getValue(); - String expectedVersion = (String) expectMeta.getAttributes().get(AippConst.ATTR_VERSION_KEY); - Assertions.assertNotEquals(expectedVersion.length(), previewVersion.length()); - Assertions.assertEquals(expectedVersion, previewVersion.substring(0, expectedVersion.length())); - Assertions.assertEquals(AippMetaStatusEnum.ACTIVE.getCode(), - info.getAttributes().getValue().get(AippConst.ATTR_META_STATUS_KEY)); - - List props = info.getProperties().getValue(); - List totalFormMetaItem = new ArrayList<>(AippConst.STATIC_META_ITEMS); - totalFormMetaItem.add(expectedFormMetaItem); - for (int i = 0; i < props.size(); i++) { - Assertions.assertEquals(props.get(i).getName().getValue(), totalFormMetaItem.get(i).getKey()); - } - return expectMeta; + this.aippFlowServiceImpl.deleteAipp("testAippId", defaultVersion, GenTestOperationContext()); }); - - AippDto aipp = GenAippDtoWithData(expectMeta.getName()); - aipp.setId(expectMeta.getId()); - AippCreateDto rsp = - aippFlowServiceImpl.previewAipp((String) expectMeta.getAttributes().get(AippConst.ATTR_VERSION_KEY), - aipp, - GenTestOperationContext()); - Assertions.assertEquals(expectMeta.getId(), rsp.getAippId()); - } - - @Test - @Disabled - void shouldFailedWhenCreatePreviewAippFailed() { - Meta expectMeta = GenTestMeta(); - expectMeta.getAttributes().put(AippConst.ATTR_META_STATUS_KEY, AippMetaStatusEnum.ACTIVE.getCode()); - - FlowInfo info = new FlowInfo(); - info.setFlowId(DUMMY_FLOW_CONFIG_ID); - info.setVersion(DUMMY_FLOW_CONFIG_VERSION); - - when(this.flowsServiceMock.getFlows(anyString(), anyString(), any())).thenReturn(info); - - AippDto aipp = GenAippDtoWithData(expectMeta.getName()); - aipp.setId(expectMeta.getId()); - Assertions.assertThrows(AippException.class, - () -> this.aippFlowServiceImpl.previewAipp(ObjectUtils.cast(expectMeta.getAttributes() - .get(AippConst.ATTR_VERSION_KEY)), aipp, GenTestOperationContext())); - } - - @Test - @DisplayName("发布应用成功") - void testPublishAppThenOk() { - String uniqueName = "testUniqueName"; - Meta expectMeta = GenTestMeta(); - publishBasicMock(expectMeta); - this.setUpPublishMock(); - AippDto aipp = this.buildAppDto(expectMeta, AppCategory.APP.getType()); - Mockito.when(this.appService.publishApp(any())).thenReturn(uniqueName); - AppBuilderApp app = AppBuilderApp.builder().formProperties(Collections.emptyList()).build(); - Rsp rsp = this.aippFlowServiceImpl.publish(aipp, app, GenTestOperationContext()); - Assertions.assertEquals(rsp.getCode(), AippErrCode.OK.getErrorCode()); - } - - @Test - @DisplayName("发布工具流成功") - void testPublishWaterFlowThenOk() { - String uniqueName = "testUniqueName"; - PluginData pluginData = new PluginData(); - Meta expectMeta = GenTestMeta(); - publishBasicMock(expectMeta); - this.setUpPublishMock(); - AippDto aipp = this.buildAppDto(expectMeta, AppCategory.WATER_FLOW.getType()); - Mockito.when(this.toolService.upgradeTool(any())).thenReturn(uniqueName); - AppBuilderApp app = AppBuilderApp.builder().formProperties(Collections.emptyList()).build(); - Rsp rsp = this.aippFlowServiceImpl.publish(aipp, app, GenTestOperationContext()); - Assertions.assertEquals(rsp.getCode(), AippErrCode.OK.getErrorCode()); - } - - private void setUpPublishMock() { - doAnswer((Answer) invocation -> { - MetaDeclarationInfo declaration = invocation.getArgument(1); - Assertions.assertTrue(declaration.getAttributes().getDefined()); - return null; - }).when(metaServiceMock).patch(any(), any(), any()); - doNothing().when(this.appBuilderAppMapperMock).updateAppWithStoreId(any(), any(), any()); - } - - private AippDto buildAppDto(Meta expectMeta, String type) { - AippDto aipp = GenAippDtoWithData(expectMeta.getName()); - aipp.setId(expectMeta.getId()); - aipp.setVersion(DUMMY_META_VERSION_OLD); - aipp.setType(type); - aipp.setIcon(StringUtils.EMPTY); - aipp.setDescription(StringUtils.EMPTY); - aipp.setUniqueName(StringUtils.EMPTY); - return aipp; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippLogServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippLogServiceTest.java index e4d67cc9dd..28a42ecffe 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippLogServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippLogServiceTest.java @@ -7,27 +7,27 @@ package modelengine.fit.jober.aipp.service; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import modelengine.fit.dynamicform.DynamicFormService; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippParamException; import modelengine.fit.jober.aipp.constants.AippConst; +import modelengine.fit.jober.aipp.domains.log.repository.AippLogRepository; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.aipplog.AippInstLogDataDto; import modelengine.fit.jober.aipp.dto.aipplog.AippLogCreateDto; import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; @@ -41,16 +41,13 @@ import modelengine.fit.jober.aipp.mapper.AippLogMapper; import modelengine.fit.jober.aipp.service.impl.AippLogServiceImpl; import modelengine.fit.jober.aipp.service.impl.AopAippLogServiceImpl; -import modelengine.fit.jober.aipp.util.JsonUtils; import modelengine.fit.jober.aipp.util.SensitiveFilterTools; -import modelengine.fit.jober.common.RangeResult; import modelengine.fit.jober.common.RangedResultSet; + import modelengine.fitframework.util.MapBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -58,6 +55,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import java.time.LocalDateTime; @@ -70,54 +68,44 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.LongStream; import java.util.stream.Stream; @ExtendWith(MockitoExtension.class) public class AippLogServiceTest { private static final String DUMMY_ID = "someRandomId"; - private static final String DUMMY_AIPP_TYPE = "normal"; - private static final String DUMMY_VERSION = "1.0.0"; - private static final String DUMMY_LOG_MSG = "some random log message"; - private static final String DUMMY_ACCOUNT = "z00000001"; - private static final String DUMMY_META_VERSION_NEW = "1.0.1"; - private static final String DUMMY_PATH = "/123"; @InjectMocks private AippLogServiceImpl aippLogService; - @InjectMocks private AopAippLogServiceImpl aopAippLogService; - @Mock private AippLogMapper aippLogMapperMock; - @Mock private AippChatMapper aippChatMapperMock; - @Mock - private MetaInstanceService metaInstanceServiceMock; - + private DynamicFormService dynamicFormServiceMock; @Mock private UploadedFileManageService uploadedFileManageServiceMock; - - @Mock - private MetaService metaServiceMock; - @Mock private SensitiveFilterTools sensitiveFilterTools; + @Mock + private AppTaskInstanceService appTaskInstanceService; + @Mock + private AppTaskService appTaskService; + @Mock + private AippLogRepository aippLogRepository; private AtomicLong logId; - private Function generateAippInstLogFunc; static Stream invalidAippLogCreateDtoCreatorForTest() { @@ -170,16 +158,9 @@ void setUp() { }; } - private void mockMta() { - Meta expectMeta = GenerateInactiveMeta(); - when(this.metaServiceMock.list(any(MetaFilter.class), - eq(false), - eq(0L), - eq(10), - any(OperationContext.class))).thenReturn(RangedResultSet.create(Collections.singletonList(expectMeta), - 0L, - 10, - 1L)); + private void mockTask() { + AppTask task = generateTask(); + when(this.appTaskService.getTasksByAppId(any(), any(), any())).thenReturn(List.of(task)); } @Test @@ -203,23 +184,16 @@ void shouldInsertIntoDbWhenCallInsertLog() { void shouldDeleteWhenCallDeleteLogWithLastNotRunning() { final String dummyAippInstanceStatus = MetaInstStatusEnum.ARCHIVED.name(); final String dummyInstId = DUMMY_LOG_MSG; - this.mockMta(); - RangedResultSet metaInstanceResult = new RangedResultSet<>(); - metaInstanceResult.setRange(new RangeResult(0, 1, 1)); - metaInstanceResult.setResults(Collections.singletonList(new Instance(dummyInstId, - Collections.singletonMap(AippConst.INST_STATUS_KEY, dummyAippInstanceStatus), - null))); + this.mockTask(); when(this.aippLogMapperMock.selectNormalInstanceIdOrderByTimeDesc(eq(Collections.singletonList(DUMMY_ID)), eq(AippTypeEnum.getType(DUMMY_AIPP_TYPE).type()), eq(OperationContextDummy.DUMMY_ACCOUNT))).thenReturn(Collections.singletonList(dummyInstId)); - when(this.metaInstanceServiceMock.getMetaVersionId(any())).thenReturn(DUMMY_ID); - when(this.metaInstanceServiceMock.list(anyList(), - eq(0L), - eq(1), - argThat(OperationContextDummy::operationContextDummyMatcher))).thenReturn(metaInstanceResult); + when(this.appTaskInstanceService.getTaskId(any())).thenReturn(DUMMY_ID); + when(this.appTaskInstanceService.getInstance(any(), any(), any())).thenReturn(Optional.of( + AppTaskInstance.asEntity().setInstanceId(dummyInstId).setStatus(dummyAippInstanceStatus).build())); aippLogService.deleteAippInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()); verify(aippLogMapperMock, times(1)).selectNormalInstanceIdOrderByTimeDesc(any(), any(), any()); - verify(metaInstanceServiceMock, times(1)).list(anyList(), anyLong(), anyInt(), any()); + verify(appTaskInstanceService, times(1)).getInstance(any(), any(), any()); verify(uploadedFileManageServiceMock, times(1)).cleanAippFiles(any()); verify(aippLogMapperMock, times(1)).delete(eq(Collections.singletonList(DUMMY_ID)), eq(AippTypeEnum.getType(AippTypeEnum.NORMAL.name()).type()), @@ -227,44 +201,33 @@ void shouldDeleteWhenCallDeleteLogWithLastNotRunning() { isNull()); } - Meta GenerateInactiveMeta() { - LocalDateTime createTime = LocalDateTime.now(); - LocalDateTime modifyTime = LocalDateTime.now(); - Meta expectMeta = new Meta(); - expectMeta.setName("testName"); - expectMeta.setId(DUMMY_ID); - expectMeta.setVersion(DUMMY_META_VERSION_NEW); - expectMeta.setCreator("testUser"); - expectMeta.setCreationTime(createTime); - expectMeta.setLastModificationTime(modifyTime); - Map attribute = new HashMap<>(); - attribute.put(AippConst.ATTR_AIPP_TYPE_KEY, AippTypeEnum.getType(DUMMY_AIPP_TYPE).type()); - attribute.put(AippConst.ATTR_APP_ID_KEY, DUMMY_ID); - expectMeta.setAttributes(attribute); - return expectMeta; + AppTask generateTask() { + return AppTask.asEntity() + .setName("testName") + .setAppSuiteId(DUMMY_ID) + .setVersion(DUMMY_META_VERSION_NEW) + .setCreator("testUser") + .setCreationTime(LocalDateTime.now()) + .setLastModificationTime(LocalDateTime.now()) + .setAippType(AippTypeEnum.getType(DUMMY_AIPP_TYPE).type()) + .setAppId(DUMMY_ID) + .build(); } @Test void shouldNotDeleteWhenCallDeleteLogWithLastRunning() { final String dummyAippInstanceStatus = MetaInstStatusEnum.RUNNING.name(); final String dummyInstId = DUMMY_LOG_MSG; - this.mockMta(); - RangedResultSet metaInstanceResult = new RangedResultSet<>(); - metaInstanceResult.setRange(new RangeResult(0, 1, 1)); - metaInstanceResult.setResults(Collections.singletonList(new Instance(dummyInstId, - Collections.singletonMap(AippConst.INST_STATUS_KEY, dummyAippInstanceStatus), - null))); + this.mockTask(); when(this.aippLogMapperMock.selectNormalInstanceIdOrderByTimeDesc(eq(Collections.singletonList(DUMMY_ID)), eq(AippTypeEnum.getType(DUMMY_AIPP_TYPE).type()), eq(OperationContextDummy.DUMMY_ACCOUNT))).thenReturn(Collections.singletonList(dummyInstId)); - when(this.metaInstanceServiceMock.getMetaVersionId(any())).thenReturn(DUMMY_ID); - when(this.metaInstanceServiceMock.list(anyList(), - eq(0L), - eq(1), - argThat(OperationContextDummy::operationContextDummyMatcher))).thenReturn(metaInstanceResult); + when(this.appTaskInstanceService.getTaskId(any())).thenReturn(DUMMY_ID); + when(this.appTaskInstanceService.getInstance(any(), any(), any())).thenReturn(Optional.of( + AppTaskInstance.asEntity().setInstanceId(dummyInstId).setStatus(dummyAippInstanceStatus).build())); aippLogService.deleteAippInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()); verify(aippLogMapperMock, times(1)).selectNormalInstanceIdOrderByTimeDesc(any(), any(), any()); - verify(metaInstanceServiceMock, times(1)).list(anyList(), anyLong(), anyInt(), any()); + verify(appTaskInstanceService, times(1)).getInstance(any(), any(), any()); verify(aippLogMapperMock, times(1)).delete(eq(Collections.singletonList(DUMMY_ID)), eq(AippTypeEnum.NORMAL.name()), eq(OperationContextDummy.DUMMY_ACCOUNT), @@ -274,13 +237,13 @@ void shouldNotDeleteWhenCallDeleteLogWithLastRunning() { @Test void shouldDoNothingWhenCallDeleteLogWithNoInstId() { - this.mockMta(); + this.mockTask(); when(aippLogMapperMock.selectNormalInstanceIdOrderByTimeDesc(eq(Collections.singletonList(DUMMY_ID)), eq(AippTypeEnum.getType(DUMMY_AIPP_TYPE).type()), eq(OperationContextDummy.DUMMY_ACCOUNT))).thenReturn(Collections.emptyList()); aippLogService.deleteAippInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()); verify(aippLogMapperMock, times(1)).selectNormalInstanceIdOrderByTimeDesc(any(), any(), any()); - verify(metaInstanceServiceMock, never()).list(any(), any(), anyLong(), anyInt(), any()); + verify(appTaskInstanceService, never()).getInstance(any(), any(), any()); verify(aippLogMapperMock, never()).delete(any(), any(), any(), any()); } @@ -341,143 +304,14 @@ void shouldQueryDbWithTimeWhenCallQueryInstanceLogSinceWithTimeString() { Assertions.assertEquals(AippInstLogType.ERROR.name(), result.get(1).getLogType()); } - @Test - @Disabled - void shouldSuccessWhenQueryAippRecentInstLog() { - final String dummyFormId = "form id"; - final String dummyFormVersion = "form version"; - final String dummyFormArgs = "{\"key\": \"form args\"}"; - final String dummyFormData = "{\"key\": \"form data\"}"; - final String dummyAippInstanceStatus = MetaInstStatusEnum.ARCHIVED.name(); - Map dummyFormObject = new HashMap() {{ - put("form_args", dummyFormArgs); - put("form_data", dummyFormData); - }}; - AippLogData dummyLogData = - new AippLogData(dummyFormId, dummyFormVersion, dummyFormArgs, "", null, null, new HashMap<>()); - String dummyLogDataJson = JsonUtils.toJsonString(dummyLogData); - RangedResultSet metaInstanceResult = new RangedResultSet<>(); - metaInstanceResult.setRange(new RangeResult(0, 1, 1)); - metaInstanceResult.setResults(Collections.singletonList(new Instance(DUMMY_ID, - Collections.singletonMap(AippConst.INST_STATUS_KEY, dummyAippInstanceStatus), - null))); - - List dummyAippInstLog = Stream.of(AippInstLogType.MSG, AippInstLogType.FORM, AippInstLogType.ERROR) - .map(generateAippInstLogFunc) - .peek(logData -> logData.setAippId(DUMMY_ID)) - .peek(logData -> logData.setInstanceId(DUMMY_ID)) - .peek(logData -> logData.setLogData(dummyLogDataJson)) - .collect(Collectors.toList()); - when(aippLogMapperMock.selectRecentByAippId(eq(DUMMY_ID), - eq(DUMMY_AIPP_TYPE), - eq(OperationContextDummy.DUMMY_ACCOUNT))).thenReturn(dummyAippInstLog); - when(metaInstanceServiceMock.list(eq(DUMMY_ID), - argThat(filter -> filter.getIds().size() == 1 && filter.getIds().get(0).equals(DUMMY_ID)), - eq(0L), - eq(1), - argThat(OperationContextDummy::operationContextDummyMatcher))).thenReturn(metaInstanceResult); - - List result = - aippLogService.queryAippRecentInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()); - Assertions.assertEquals(1, result.size()); - AippInstLogDataDto logDto = result.get(0); - Assertions.assertEquals(DUMMY_ID, logDto.getAippId()); - Assertions.assertEquals(DUMMY_ID, logDto.getInstanceId()); - Assertions.assertEquals(dummyAippInstanceStatus, logDto.getStatus()); - Assertions.assertEquals(dummyAippInstLog.size(), logDto.getInstanceLogBodies().size()); - Assertions.assertTrue(logDto.getInstanceLogBodies() - .stream() - .filter(l -> l.getLogData().equals(AippInstLogType.FORM.name())) - .allMatch(l -> l.getLogData().equals(JsonUtils.toJsonString(dummyFormObject)))); - // 检查log是否成功排序 - List logIdSequence = logDto.getInstanceLogBodies() - .stream() - .map(AippInstLogDataDto.AippInstanceLogBody::getLogId) - .collect(Collectors.toList()); - Assertions.assertIterableEquals(logIdSequence.stream().sorted().collect(Collectors.toList()), logIdSequence); - verify(aippLogMapperMock, times(1)).selectRecentByAippId(eq(DUMMY_ID), - eq(DUMMY_AIPP_TYPE), - eq(OperationContextDummy.DUMMY_ACCOUNT)); - verify(metaInstanceServiceMock, times(1)).list(any(), any(), anyLong(), anyInt(), any()); - } - @Test void shouldReturnEmptyListWhenQueryAippRecentInstLogWithDbReturnNull() { - this.mockMta(); + this.mockTask(); Assertions.assertTrue(aippLogService.queryAippRecentInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()).isEmpty()); - verify(metaInstanceServiceMock, never()).list(any(), any(), anyLong(), anyInt(), any()); - } - - @Test - @Disabled - void shouldSortedWhenQueryAippRecentInstLog() { - final long instanceCount = 2L; - final long logCountPerInstance = 3L; - final AippInstLogType msgType = AippInstLogType.MSG; - - final String dummyAippInstanceStatus = MetaInstStatusEnum.ARCHIVED.name(); - final LocalDateTime createTimestamp = LocalDateTime.of(2000, 1, 1, 0, 0); - final List dummyInstIdList = - LongStream.rangeClosed(1, instanceCount).mapToObj(String::valueOf).collect(Collectors.toList()); - List dummyAippInstLog = Stream.generate(() -> msgType) - .limit(instanceCount * logCountPerInstance) - .map(generateAippInstLogFunc) - .peek(l -> l.setAippId(DUMMY_ID)) - .peek(l -> l.setCreateAt(createTimestamp)) - .peek(l -> l.setCreateUserAccount(DUMMY_ACCOUNT)) - .peek(l -> l.setInstanceId(dummyInstIdList.get((int) ((l.getLogId() - 1) / logCountPerInstance)))) - .collect(Collectors.toList()); - Collections.shuffle(dummyAippInstLog); // 打乱 - when(aippLogMapperMock.selectRecentByAippId(eq(DUMMY_ID), - eq(DUMMY_AIPP_TYPE), - eq(OperationContextDummy.DUMMY_ACCOUNT))).thenReturn(dummyAippInstLog); - RangedResultSet metaInstanceResult = - new RangedResultSet<>(Collections.singletonList(new Instance(null, - Collections.singletonMap(AippConst.INST_STATUS_KEY, dummyAippInstanceStatus), - null)), new RangeResult(0, 1, 1)); - when(metaInstanceServiceMock.list(eq(DUMMY_ID), - argThat(filter -> filter.getIds().size() == 1 && dummyInstIdList.contains(filter.getIds().get(0))), - eq(0L), - eq(1), - argThat(OperationContextDummy::operationContextDummyMatcher))).thenReturn(metaInstanceResult); - this.mockMta(); - when(this.aippLogMapperMock.selectRecentInstanceIdByAippIds(any(), any(), anyInt(), any())).thenReturn( - dummyInstIdList); - List result = - aippLogService.queryAippRecentInstLog(DUMMY_ID, DUMMY_AIPP_TYPE, OperationContextDummy.getDummy()); - Assertions.assertEquals(instanceCount, result.size()); - Assertions.assertIterableEquals(dummyInstIdList, - result.stream().map(AippInstLogDataDto::getInstanceId).collect(Collectors.toList())); - for (AippInstLogDataDto logDto : result) { - Assertions.assertEquals(DUMMY_ID, logDto.getAippId()); - Assertions.assertEquals(dummyAippInstanceStatus, logDto.getStatus()); - Assertions.assertEquals(logCountPerInstance, logDto.getInstanceLogBodies().size()); - Assertions.assertTrue(logDto.getInstanceLogBodies() - .stream() - .allMatch(l -> l.getLogType().equals(msgType.name()) && l.getCreateAt().equals(createTimestamp) - && l.getCreateUserAccount().equals(DUMMY_ACCOUNT))); - // 检查log内部是否成功排序 - List logIdSequence = logDto.getInstanceLogBodies() - .stream() - .map(AippInstLogDataDto.AippInstanceLogBody::getLogId) - .collect(Collectors.toList()); - Assertions.assertIterableEquals(logIdSequence.stream().sorted().collect(Collectors.toList()), - logIdSequence); - } - verify(aippLogMapperMock, times(1)).selectRecentByAippId(eq(DUMMY_ID), - eq(DUMMY_AIPP_TYPE), - eq(OperationContextDummy.DUMMY_ACCOUNT)); - verify(metaInstanceServiceMock, times(1)).list(any(), any(), anyLong(), anyInt(), any()); - } - - @Test - @DisplayName("测试queryLogsByInstanceIdAndLogTypes方法") - void testQueryLogsByInstanceIdAndLogTypes() { - AippParamException exception = Assertions.assertThrows(AippParamException.class, - () -> this.aippLogService.queryLogsByInstanceIdAndLogTypes("", new ArrayList<>())); - Assertions.assertEquals(AippErrCode.INPUT_PARAM_IS_INVALID.getErrorCode(), exception.getCode()); + verify(dynamicFormServiceMock, never()).queryFormDetailByPrimaryKey(any(), any(), any()); + verify(this.appTaskInstanceService, never()).getInstance(any(), any(), any()); } @Test @@ -497,43 +331,62 @@ void shouldReturnWhenCallDeleteLogWithNullParam() { @Test void shouldOkWhenCallQueryChatRecentChatLog() { - List aippInstLogList = generateAippInstLogList(); List instanceIds = new ArrayList<>(Arrays.asList("1", "2", "3")); when(this.aippChatMapperMock.selectInstanceByChat(any(), any())).thenReturn(instanceIds); - when(this.aippLogService.queryBatchAndFilterFullLogsByLogType(instanceIds, any())).thenReturn(aippInstLogList); - - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - meta.setVersionId("version1"); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - metaRangedResultSet.setRange(new RangeResult(0, 1, 1)); - - when(this.metaServiceMock.list(any(MetaFilter.class), - anyBoolean(), - anyLong(), - anyInt(), - any(OperationContext.class))).thenReturn(metaRangedResultSet); + when(this.appTaskInstanceService.getTaskId(any())).thenReturn("testMetaId"); + AppTaskInstance appTaskInstance1 = spy(AppTaskInstance.asEntity().setInstanceId("1").build()); + AppTaskInstance appTaskInstance2 = spy(AppTaskInstance.asEntity().setInstanceId("2").build()); + AppTaskInstance appTaskInstance3 = spy(AppTaskInstance.asEntity().setInstanceId("3").build()); + AppTask appTask = AppTask.asEntity().setTaskId("version1").setAppId("app1").build(); + when(this.appTaskService.getTasksByAppId(any(), any())).thenReturn(List.of(appTask)); + when(this.appTaskInstanceService.getInstance(anyString(), eq("1"), any(OperationContext.class))).thenReturn( + Optional.of(appTaskInstance1)); + when(this.appTaskInstanceService.getInstance(anyString(), eq("2"), any(OperationContext.class))).thenReturn( + Optional.of(appTaskInstance2)); + when(this.appTaskInstanceService.getInstance(anyString(), eq("3"), any(OperationContext.class))).thenReturn( + Optional.of(appTaskInstance3)); + when(this.appTaskService.getTasks(any(), any())).thenReturn(RangedResultSet.create(List.of(appTask), 0, 1, 1)); + MockedStatic mockedStatic = mockStatic(AppTaskInstance.class); + + AippInstLogDataDto logDataDto1 = getMockLogDataDto("1"); + AippInstLogDataDto logDataDto2 = getMockLogDataDto("2"); + AippInstLogDataDto logDataDto3 = getMockLogDataDto("3"); + mockedStatic.when(() -> AppTaskInstance.toLogDataDto(appTaskInstance1)).thenReturn(Optional.of(logDataDto1)); + mockedStatic.when(() -> AppTaskInstance.toLogDataDto(appTaskInstance2)).thenReturn(Optional.of(logDataDto2)); + mockedStatic.when(() -> AppTaskInstance.toLogDataDto(appTaskInstance3)).thenReturn(Optional.of(logDataDto3)); List list = this.aippLogService.queryChatRecentChatLog("1", "1", new OperationContext()); Assertions.assertEquals(list.get(0).getInstanceId(), "1"); Assertions.assertEquals(list.get(1).getInstanceId(), "2"); Assertions.assertEquals(list.get(2).getInstanceId(), "3"); + mockedStatic.close(); + } + + private AippInstLogDataDto getMockLogDataDto(String instanceId) { + AippInstLogDataDto logDataDto = mock(AippInstLogDataDto.class); + when(logDataDto.getCreateAt()).thenReturn(LocalDateTime.now()); + when(logDataDto.getInstanceId()).thenReturn(instanceId); + when(logDataDto.getAippId()).thenReturn("aippId"); + return logDataDto; } @Test void testQueryRecentLogsSinceResume() { - List aippInstLogList = generateAippInstLogList(); - List instanceIds = new ArrayList<>(Arrays.asList("1", "2", "3")); + List instanceIds = new ArrayList<>(List.of("1")); + AppTaskInstance appTaskInstance = AppTaskInstance.asEntity().setInstanceId("1").build(); when(this.aippLogMapperMock.selectRecentAfterResume(any(), any(), any())).thenReturn(instanceIds); - when(this.aippLogService.queryBatchAndFilterFullLogsByLogType(instanceIds, any())).thenReturn(aippInstLogList); - List list = - this.aippLogService.queryRecentLogsSinceResume("1", "1", OperationContextDummy.getDummy()); + when(this.appTaskInstanceService.getInstance(any(), any(), any())).thenReturn( + Optional.of(appTaskInstance)); + MockedStatic mockedStatic = mockStatic(AppTaskInstance.class); + + AippInstLogDataDto logDataDto = mock(AippInstLogDataDto.class); + when(logDataDto.getInstanceId()).thenReturn("1"); + mockedStatic.when(() -> AppTaskInstance.toLogDataDto(appTaskInstance)).thenReturn(Optional.of(logDataDto)); + + List list = this.aippLogService + .queryRecentLogsSinceResume("1", "1", OperationContextDummy.getDummy()); Assertions.assertEquals(list.get(0).getInstanceId(), "1"); - Assertions.assertEquals(list.get(1).getInstanceId(), "2"); - Assertions.assertEquals(list.get(2).getInstanceId(), "3"); + mockedStatic.close(); } @Test @@ -542,8 +395,10 @@ void testQueryAippRecentInstLogAfterSplice() { List instanceIds = new ArrayList<>(Arrays.asList("1", "2", "3")); when(this.aippLogMapperMock.selectRecentInstanceId(any(), any(), any(), any())).thenReturn(instanceIds); when(this.aippLogService.queryBatchAndFilterFullLogsByLogType(instanceIds, any())).thenReturn(aippInstLogList); - List list = - this.aippLogService.queryAippRecentInstLogAfterSplice("1", "1", 3, OperationContextDummy.getDummy()); + when(this.appTaskInstanceService.getInstance(any(), any(), any())).thenReturn( + Optional.of(AppTaskInstance.asEntity().setInstanceId("1").build())); + List list = this.aippLogService + .queryAippRecentInstLogAfterSplice("1", "1", 3, OperationContextDummy.getDummy()); Assertions.assertEquals(list.get(0).getInstanceId(), "1"); Assertions.assertEquals(list.get(1).getInstanceId(), "2"); Assertions.assertEquals(list.get(2).getInstanceId(), "3"); @@ -551,32 +406,14 @@ void testQueryAippRecentInstLogAfterSplice() { List generateAippInstLogList() { List aippInstLogList = new ArrayList<>(); - AippInstLog aippInstLog1 = AippInstLog.builder() - .aippId("1") - .logId(1L) - .instanceId("1") - .logType("MSG") - .path("/1") - .createAt(LocalDateTime.now()) - .build(); + AippInstLog aippInstLog1 = AippInstLog.builder().aippId("1").logId(1L).instanceId("1") + .logType("MSG").path("/1").createAt(LocalDateTime.now()).build(); aippInstLogList.add(aippInstLog1); - AippInstLog aippInstLog2 = AippInstLog.builder() - .aippId("1") - .logId(2L) - .instanceId("2") - .logType("MSG") - .path("/2") - .createAt(LocalDateTime.now().plusMinutes(1)) - .build(); + AippInstLog aippInstLog2 = AippInstLog.builder().aippId("1").logId(2L).instanceId("2") + .logType("MSG").path("/2").createAt(LocalDateTime.now().plusMinutes(1)).build(); aippInstLogList.add(aippInstLog2); - AippInstLog aippInstLog3 = AippInstLog.builder() - .aippId("1") - .logId(3L) - .instanceId("3") - .logType("MSG") - .path("/3") - .createAt(LocalDateTime.now().plusMinutes(2)) - .build(); + AippInstLog aippInstLog3 = AippInstLog.builder().aippId("1").logId(3L).instanceId("3") + .logType("MSG").path("/3").createAt(LocalDateTime.now().plusMinutes(2)).build(); aippInstLogList.add(aippInstLog3); return aippInstLogList; } @@ -594,8 +431,7 @@ void shouldReturnNullWhenCallInsertLogWithInvalidFormData() { void shouldThrowWhenCallInsertLogWithInvalidMsgData() { AippLogData aippLogData = AippLogData.builder().msg("你好").build(); Map businessData = MapBuilder.get(() -> new HashMap()) - .put(AippConst.BS_HTTP_CONTEXT_KEY, "{\"account\":\"123\"}") - .build(); + .put(AippConst.BS_HTTP_CONTEXT_KEY, "{\"account\":\"123\"}").build(); Assertions.assertThrows(NullPointerException.class, () -> this.aippLogService.insertLog(AippInstLogType.MSG.name(), aippLogData, businessData)); } @@ -615,6 +451,7 @@ void shouldThrowWhenUpdateLogWithNullParam() { @Test void shouldOkWhenUpdateLog() { this.aippLogService.updateLog(123L, "logType", "logData"); - verify(this.aippLogMapperMock, times(1)).updateDataAndType(123L, "logType", "logData"); + verify(this.aippLogRepository, times(1)) + .updateDataAndType(123L, "logType", "logData"); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRunTimeServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRunTimeServiceTest.java index 23a3ac7f87..9cf88786e6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRunTimeServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRunTimeServiceTest.java @@ -13,25 +13,31 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import lombok.Data; -import lombok.NoArgsConstructor; +import modelengine.fit.dynamicform.DynamicFormMetaService; +import modelengine.fit.dynamicform.DynamicFormService; import modelengine.fit.dynamicform.entity.FormMetaItem; -import modelengine.fit.http.client.HttpClassicClientFactory; -import modelengine.fit.jade.waterflow.FlowInstanceService; +import modelengine.fit.dynamicform.entity.FormMetaQueryParameter; import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.common.enums.DirectionEnum; import modelengine.fit.jane.meta.multiversion.MetaInstanceService; import modelengine.fit.jane.meta.multiversion.MetaService; import modelengine.fit.jane.meta.multiversion.definition.Meta; import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; import modelengine.fit.jane.meta.multiversion.instance.Instance; import modelengine.fit.jane.meta.multiversion.instance.InstanceDeclarationInfo; +import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jober.aipp.TestUtils; +import modelengine.fit.jober.aipp.common.PageResponse; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippForbiddenException; +import modelengine.fit.jober.aipp.condition.AippInstanceQueryCondition; +import modelengine.fit.jober.aipp.condition.PaginationCondition; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fit.jober.aipp.convertor.FormMetaConvertor; +import modelengine.fit.jober.aipp.dto.AippInstanceDto; import modelengine.fit.jober.aipp.enums.AippInstLogType; +import modelengine.fit.jober.aipp.enums.MetaInstSortKeyEnum; import modelengine.fit.jober.aipp.enums.MetaInstStatusEnum; import modelengine.fit.jober.aipp.service.impl.AippRunTimeServiceImpl; import modelengine.fit.jober.common.RangeResult; @@ -40,6 +46,10 @@ import modelengine.fit.jober.entity.FlowInstanceResult; import modelengine.fit.jober.entity.task.TaskProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import modelengine.fit.http.client.HttpClassicClientFactory; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -61,35 +71,20 @@ @Disabled public class AippRunTimeServiceTest { private static final String DUMMY_START_FORM_ID = "test_start_form_id"; - private static final String DUMMY_START_FORM_VERSION = "test_start_form_version"; - private static final String DUMMY_AIPP_ID = "main_aipp_id"; - private static final String DUMMY_AIPP_VERSION = "main_aipp_version"; - private static final String DUMMY_META_VERSION_ID = "meta_version_id"; - private static final String DUMMY_AIPP_NAME = "test_aipp_name"; - private static final String DUMMY_CREATOR = "test_aipp_creator"; - private static final String DUMMY_INST_ID = "main_inst_id"; - private static final String DUMMY_INST_NAME = "test_inst_name"; - private static final String DUMMY_FLOW_DEF_ID = "test_flow_def_id"; - private static final String DUMMY_FLOW_TRACE_ID = "test_flow_trace_id"; - private static final String DUMMY_CURR_FORM_ID = "test_cur_form_id"; - private static final String DUMMY_CURR_FORM_VERSION_ID = "test_cur_form_id"; - private static final String DUMMY_PROPS_KEY = "test_props_key"; - private static final String DUMMY_PROPS_VALUE = "test_props_value"; - private static final String DUMMY_FLOW_INST_ID = "test_flow_inst_id"; @InjectMocks @@ -97,19 +92,18 @@ public class AippRunTimeServiceTest { @Mock private AopAippLogService aopAippLogServiceMock; - + @Mock + private DynamicFormMetaService dynamicFormMetaServiceMock; @Mock private MetaService metaServiceMock; - + @Mock + private DynamicFormService dynamicFormServiceMock; @Mock private MetaInstanceService metaInstanceServiceMock; - @Mock private FlowInstanceService flowInstanceServiceMock; - @Mock private UploadedFileManageService uploadedFileManageServiceMock; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private HttpClassicClientFactory httpClientFactoryMock; @@ -136,9 +130,14 @@ void shouldOkWhenCreateInstance() { Mockito.doReturn(inst).when(metaInstanceServiceMock).createMetaInstance(any(), any(), any()); Meta expectMeta = MetaBuilder.custom().putAttr(AippConst.ATTR_FLOW_DEF_ID_KEY, DUMMY_FLOW_DEF_ID).build(); - when(metaServiceMock.list(any(MetaFilter.class), eq(true), eq(0L), eq(1), - any(OperationContext.class))).thenReturn( - RangedResultSet.create(Collections.singletonList(expectMeta), 0L, 1, 1L)); + when(metaServiceMock.list(any(MetaFilter.class), + eq(true), + eq(0L), + eq(1), + any(OperationContext.class))).thenReturn(RangedResultSet.create(Collections.singletonList(expectMeta), + 0L, + 1, + 1L)); Mockito.doAnswer(var -> { String flowDefId = var.getArgument(0); @@ -153,9 +152,11 @@ void shouldOkWhenCreateInstance() { return null; }).when(metaInstanceServiceMock).patchMetaInstance(any(), any(), any(), any()); + Mockito.doReturn(Collections.emptyList()).when(dynamicFormMetaServiceMock).query(any()); + Map initContext = Collections.singletonMap(AippConst.BS_INIT_CONTEXT_KEY, new HashMap<>()); - String instId = runTimeService.createAippInstance(DUMMY_AIPP_ID, DUMMY_AIPP_VERSION, initContext, - genTestOpContext()); + String instId = + runTimeService.createAippInstance(DUMMY_AIPP_ID, DUMMY_AIPP_VERSION, initContext, genTestOpContext()); Assertions.assertEquals(DUMMY_INST_ID, instId); verify(uploadedFileManageServiceMock, times(1)).addFileRecord(any(), any(), any(), any()); } @@ -165,20 +166,25 @@ void shouldFailedWhenDeleteInvalidAipp() { Meta meta = MetaBuilder.custom().build(); TestUtils.mockMetaListReturnSingleItem5(meta, metaServiceMock); - RangedResultSet res = RangedResultSet.create(Collections.singletonList(new Instance()), - new RangeResult(0, 0, 0)); + RangedResultSet res = + RangedResultSet.create(Collections.singletonList(new Instance()), new RangeResult(0, 0, 0)); Mockito.doReturn(res).when(metaInstanceServiceMock).list(any(), any(), eq(0L), eq(1), any()); Assertions.assertThrows(JobberException.class, - () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, DUMMY_AIPP_VERSION, DUMMY_INST_ID, + () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, + DUMMY_AIPP_VERSION, + DUMMY_INST_ID, genTestOpContext())); Instance inst = new Instance(DUMMY_INST_ID, - Collections.singletonMap("invalid_key", MetaInstStatusEnum.ARCHIVED.name()), null); + Collections.singletonMap("invalid_key", MetaInstStatusEnum.ARCHIVED.name()), + null); res.setResults(Collections.singletonList(inst)); res.setRange(new RangeResult(0, 1, 1)); Assertions.assertThrows(AippException.class, - () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, DUMMY_AIPP_VERSION, DUMMY_INST_ID, + () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, + DUMMY_AIPP_VERSION, + DUMMY_INST_ID, genTestOpContext())); } @@ -190,7 +196,9 @@ void shouldFailedWhenDeleteRunningAipp() { Mockito.doReturn(res).when(metaInstanceServiceMock).list(any(), any(), eq(0L), eq(1), any()); AippForbiddenException exception = Assertions.assertThrows(AippForbiddenException.class, - () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, DUMMY_AIPP_VERSION, DUMMY_INST_ID, + () -> runTimeService.deleteAippInstance(DUMMY_AIPP_ID, + DUMMY_AIPP_VERSION, + DUMMY_INST_ID, genTestOpContext())); Assertions.assertEquals(AippErrCode.DELETE_INSTANCE_FORBIDDEN.getErrorCode(), exception.getCode()); } @@ -217,8 +225,10 @@ void shouldOkWhenResumeInstance() { Mockito.doNothing() .when(metaInstanceServiceMock) - .patchMetaInstance(any(), eq(DUMMY_INST_ID), - argThat(info -> info.getInfo().getValue().containsKey(DUMMY_PROPS_KEY)), any()); + .patchMetaInstance(any(), + eq(DUMMY_INST_ID), + argThat(info -> info.getInfo().getValue().containsKey(DUMMY_PROPS_KEY)), + any()); RangedResultSet res = genTestRangedResultSet(MetaInstStatusEnum.ARCHIVED.name()); Mockito.doReturn(res).when(metaInstanceServiceMock).list(any(), any(), eq(0L), eq(1), any()); @@ -229,6 +239,7 @@ void shouldOkWhenResumeInstance() { Mockito.doNothing() .when(aopAippLogServiceMock) .insertLog(argThat(dto -> AippInstLogType.FORM.name().equals(dto.getLogType()))); + Mockito.doReturn(Collections.emptyList()).when(dynamicFormMetaServiceMock).query(any()); Map businessData = new HashMap() { { @@ -236,9 +247,8 @@ void shouldOkWhenResumeInstance() { } }; Map formArgs = Collections.singletonMap(AippConst.BS_DATA_KEY, businessData); - Assertions.assertDoesNotThrow( - () -> runTimeService.resumeAndUpdateAippInstance(DUMMY_INST_ID, formArgs, 1L, genTestOpContext(), - true)); + Assertions.assertDoesNotThrow(() -> + runTimeService.resumeAndUpdateAippInstance(DUMMY_INST_ID, formArgs, 1L, genTestOpContext(), true)); } @Test @@ -248,8 +258,10 @@ void shouldOkWhenTerminateInstance() { Mockito.doNothing() .when(metaInstanceServiceMock) - .patchMetaInstance(eq(DUMMY_META_VERSION_ID), eq(DUMMY_INST_ID), - argThat(info -> info.getInfo().getValue().containsKey(AippConst.INST_STATUS_KEY)), any()); + .patchMetaInstance(eq(DUMMY_META_VERSION_ID), + eq(DUMMY_INST_ID), + argThat(info -> info.getInfo().getValue().containsKey(AippConst.INST_STATUS_KEY)), + any()); RangedResultSet res = genTestRangedResultSet(MetaInstStatusEnum.RUNNING.name()); Mockito.doReturn(res).when(metaInstanceServiceMock).list(any(), any(), eq(0L), eq(1), any()); @@ -272,7 +284,9 @@ void shouldThrowExceptionWhenTerminateFlowsFailed() { Mockito.doReturn(meta).when(metaServiceMock).retrieve(any(), any()); - Mockito.doThrow(JobberException.class).when(flowInstanceServiceMock).terminateFlows(any(), any(), any(), any()); + Mockito.doThrow(JobberException.class) + .when(flowInstanceServiceMock) + .terminateFlows(any(), any(), any(), any()); Map msgArgs = new HashMap<>(); Assertions.assertThrows(AippException.class, () -> { @@ -284,27 +298,16 @@ void shouldThrowExceptionWhenTerminateFlowsFailed() { @Data private static class MetaBuilder { private String id = DUMMY_AIPP_ID; - private String name; - private String category; - private List properties = new ArrayList<>(); - private String creator; - private LocalDateTime creationTime; - private String lastModifier; - private LocalDateTime lastModificationTime; - private String tenant; - private Map attributes = new HashMap<>(); - private String version = DUMMY_AIPP_VERSION; - private String versionId = DUMMY_META_VERSION_ID; public static MetaBuilder custom() { @@ -328,8 +331,18 @@ public MetaBuilder addProps(String key, String name, String type) { } public Meta build() { - return new Meta(id, name, category, properties, creator, creationTime, lastModifier, lastModificationTime, - tenant, attributes, version, versionId); + return new Meta(id, + name, + category, + properties, + creator, + creationTime, + lastModifier, + lastModificationTime, + tenant, + attributes, + version, + versionId); } } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRuntimeServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRuntimeServiceImplTest.java index 1a184ea199..9889491b8e 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRuntimeServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AippRuntimeServiceImplTest.java @@ -6,50 +6,33 @@ package modelengine.fit.jober.aipp.service; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import modelengine.fit.dynamicform.DynamicFormMetaService; +import modelengine.fit.dynamicform.DynamicFormService; import modelengine.fit.http.client.HttpClassicClientFactory; import modelengine.fit.jade.waterflow.FlowInstanceService; import modelengine.fit.jade.waterflow.FlowsService; -import modelengine.fit.jade.waterflow.dto.FlowInfo; -import modelengine.fit.jade.waterflow.entity.FlowNodeInfo; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; -import modelengine.fit.jane.meta.multiversion.instance.Instance; -import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.dto.chat.AppChatRsp; -import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.domains.taskinstance.AppTaskInstance; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fit.jober.aipp.service.impl.AippRunTimeServiceImpl; -import modelengine.fit.jober.aipp.util.AppUtils; -import modelengine.fit.jober.common.RangeResult; -import modelengine.fit.jober.common.RangedResultSet; -import modelengine.fit.jober.common.exceptions.JobberException; -import modelengine.fit.waterflow.domain.enums.FlowTraceStatus; import modelengine.fitframework.broker.client.BrokerClient; import modelengine.fitframework.flowable.Emitter; -import modelengine.fitframework.flowable.emitter.DefaultEmitter; import modelengine.fitframework.util.MapBuilder; -import modelengine.fitframework.util.ObjectUtils; import modelengine.jade.authentication.context.UserContext; import modelengine.jade.authentication.context.UserContextHolder; import modelengine.jade.common.globalization.LocaleService; @@ -68,15 +51,8 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; import java.util.Map; +import java.util.Optional; /** * 为{@link modelengine.fit.jober.aipp.service.impl.AippRunTimeServiceImpl} 提供测试 @@ -91,62 +67,49 @@ public class AippRuntimeServiceImplTest { @Mock private AopAippLogService aopAippLogServiceMock; - @Mock - private MetaService metaService; - + private DynamicFormMetaService dynamicFormMetaServiceMock; @Mock - private MetaInstanceService metaInstanceService; - + private DynamicFormService dynamicFormService; @Mock private FlowInstanceService flowInstanceService; - @Mock private UploadedFileManageService uploadedFileManageService; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private HttpClassicClientFactory httpClientFactory; - @Mock private AippLogService aippLogService; - @Mock private BrokerClient client; - @Mock private AppBuilderFormRepository formRepository; - @Mock private AppBuilderFormPropertyRepository formPropertyRepository; - @Mock private FlowsService flowsService; - @Mock private AippStreamService aippStreamService; - @Mock private AopAippLogService aopAippLogService; - @Mock private AppChatSseService appChatSSEService; - @Mock private AippLogService logService; - @Mock private AppBuilderAppFactory appFactory; - @Mock private LocaleService localeService; - private MockedStatic opContextHolderMock; - @Mock private AppChatSessionService appChatSessionService; - @Mock private Emitter emitter; + @Mock + private AppTaskService appTaskService; + @Mock + private AppTaskInstanceService appTaskInstanceService; + @Mock + private AppVersionService appVersionService; @BeforeEach void setUp() { @@ -165,14 +128,11 @@ void testStartFlowWithUserSelectMemory() { Map businessData = MapBuilder.get().build(); Map initContext = MapBuilder.get().put(AippConst.BS_INIT_CONTEXT_KEY, businessData).build(); - Meta meta = new Meta(); - meta.setAttributes(MapBuilder.get() - .put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_id") - .put(AippConst.ATTR_META_STATUS_KEY, "active") - .put(AippConst.ATTR_APP_ID_KEY, "123") - .build()); - Mockito.when(metaService.retrieve(Mockito.eq("versionId"), Mockito.any())).thenReturn(meta); - Mockito.when(metaInstanceService.getMetaVersionId(Mockito.eq("instanceId"))).thenReturn("versionId"); + AppTaskInstance appTaskInstance = Mockito.mock(AppTaskInstance.class); + AppTask appTask = Mockito.mock(AppTask.class); + when(appTaskService.getTaskById(any(), any())).thenReturn(Optional.of(appTask)); + when(this.appTaskInstanceService.getTaskId(eq("instanceId"))).thenReturn("versionId"); + when(this.appTaskInstanceService.getInstance(any(), any(), any())).thenReturn(Optional.of(appTaskInstance)); Assertions.assertDoesNotThrow(() -> this.aippRunTimeService.startFlowWithUserSelectMemory("instanceId", initContext, @@ -187,8 +147,9 @@ void testCreateAippInstance() { OperationContext context = new OperationContext(); context.setTenantId("testTenantId"); context.setOperator("UT 123456"); - this.mockMetaQuery("UserSelect"); Map initContext = this.genInitContext(); + AppTask appTask = Mockito.mock(AppTask.class); + when(this.appTaskService.getLatest(any(), any(), any())).thenReturn(Optional.of(appTask)); Assertions.assertDoesNotThrow(() -> this.aippRunTimeService.createAippInstance("aipp_id", "version", initContext, @@ -201,336 +162,19 @@ void TestCreateLatestAippInstanceByAppId() { OperationContext context = new OperationContext(); context.setTenantId("testTenantId"); context.setOperator("UT 123456"); - - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - attributes.put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id1"); - Meta meta = new Meta(); - meta.setAttributes(attributes); - meta.setVersion("version1"); - meta.setVersionId("versionId1"); - meta.setId("id1"); - - Instance metaInst = new Instance(); - metaInst.setId("instId"); - metaInst.setInfo(MapBuilder.get().put("flow_trans_id", "flowTransId1").build()); - - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - metaRangedResultSet.setRange(new RangeResult(0, 1, 1)); - - RangedResultSet metaInstanceResultSet = new RangedResultSet<>(); - metaInstanceResultSet.setResults(Collections.singletonList(metaInst)); - Map businessData = MapBuilder.get().build(); Map initContext = MapBuilder.get().put(AippConst.BS_INIT_CONTEXT_KEY, businessData).build(); - when(this.metaInstanceService.createMetaInstance(any(), any(), any())).thenReturn(metaInst); - when(this.metaService.list(any(MetaFilter.class), - anyBoolean(), - anyLong(), - anyInt(), - any(OperationContext.class))).thenReturn(metaRangedResultSet); - when(flowsService.getFlows(any(), Mockito.any())).thenReturn(mockFlowInfo("UserSelect")); - when(this.metaInstanceService.list(anyList(), anyLong(), anyInt(), any())).thenReturn( - metaInstanceResultSet); - - assertThat(this.aippRunTimeService.createLatestAippInstanceByAppId("app_id", - true, - initContext, - context)).isEqualTo("flowTransId1"); - } - - @Test - @DisplayName("测试createInstanceByApp方法") - void testCreateInstanceByApp() { - OperationContext context = new OperationContext(); - context.setTenantId("testTenantId"); - context.setOperator("UT 123456"); - - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - Meta meta = new Meta(); - meta.setId("aipp_id"); - meta.setAttributes(attributes); - meta.setVersionId("version1"); - RangedResultSet metaRangedResultSet = new RangedResultSet<>(); - metaRangedResultSet.setResults(Collections.singletonList(meta)); - metaRangedResultSet.setRange(new RangeResult(0, 1, 1)); + AppTask appTask = Mockito.mock(AppTask.class); - AppUtils.setAppChatInfo("123", false); + AppVersion appVersion = mock(AppVersion.class); + when(appVersion.getLatestTask(any())).thenReturn(appTask); - Instance metaInst = new Instance(); - metaInst.setId("instId"); - when(this.metaInstanceService.createMetaInstance(any(), any(), any())).thenReturn(metaInst); - - when(this.metaService.list(any(MetaFilter.class), - anyBoolean(), - anyLong(), - anyInt(), - any(OperationContext.class))).thenReturn(metaRangedResultSet); - - Map initContext = this.genInitContext(); - Assertions.assertDoesNotThrow(() -> this.aippRunTimeService.createInstanceByApp("aipp_id", - "question", - initContext, - context, - false)); - } - - @Test - @DisplayName("测试startChat方法") - @Disabled - void testStartChatSuccess() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // 获取要测试的私有方法 - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("startChat", - Map.class, - Meta.class, - OperationContext.class, - Instance.class, - ChatSession.class); - method.setAccessible(true); // 设置访问权限为 true - Map businessData = new HashMap<>(); - Map attributes = new HashMap<>(); - Meta meta = new Meta(); - meta.setId("meta_id"); - meta.setAttributes(attributes); - OperationContext context = new OperationContext(); - Instance metaInst = new Instance(); - metaInst.setId("instance_id"); - ChatSession chatSession = new ChatSession<>(new DefaultEmitter<>(), "123", true, Locale.ENGLISH); - Mockito.when(flowsService.getFlows(any(), Mockito.any())).thenReturn(mockFlowInfo("UserSelect")); - // 调用私有方法 - Assertions.assertDoesNotThrow(() -> method.invoke(aippRunTimeService, - businessData, - meta, - context, - metaInst, - chatSession)); - } - - @Test - @DisplayName("测试recordContext方法") - void testRecordContextSuccess() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // 获取要测试的私有方法 - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("recordContext", - OperationContext.class, - Meta.class, - Map.class, - Instance.class); - method.setAccessible(true); // 设置访问权限为 true - OperationContext operationContext = new OperationContext(); - operationContext.setOperator("user1"); - Meta meta = new Meta(); - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_ID_KEY, "app1"); - meta.setAttributes(attributes); - Instance metaInst = new Instance(); - metaInst.setId("instance_id"); - Map businessData = new HashMap<>(); - List> files = new ArrayList<>(); - files.add(MapBuilder.get() - .put("file_name", "1.docx") - .put("file_url", "/path/to/1.docx") - .put("file_type", "docx") - .build()); - businessData.put(AippConst.BS_AIPP_FILE_DESC_KEY, files); - method.invoke(aippRunTimeService, operationContext, meta, businessData, metaInst); - assertThat(businessData.get(AippConst.BS_AIPP_FILES_DOWNLOAD_KEY)).isEqualTo(Collections.singletonList( - "/path/to/1.docx")); - } - - @Test - @DisplayName("测试persistAippLog方法") - void testPersistAippLogSuccess() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // 获取要测试的私有方法 - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("persistAippLog", - Map.class, - String.class, - OperationContext.class); - method.setAccessible(true); // 设置访问权限为 true - Map businessData = new HashMap<>(); - businessData.put(AippConst.BS_AIPP_QUESTION_KEY, "question"); - List> files = new ArrayList<>(); - files.add(MapBuilder.get() - .put("file_name", "1.docx") - .put("file_url", "/path/to/1.docx") - .put("file_type", "docx") - .build()); - businessData.put(AippConst.BS_AIPP_FILE_DESC_KEY, files); - method.invoke(aippRunTimeService, businessData, "flow_definition_id1", null); - assertThat(businessData.get(AippConst.BS_AIPP_QUESTION_KEY)).isEqualTo("question"); - } - - @Test - @DisplayName("测试startChat方法") - @Disabled - void testStartChatFail() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - // 获取要测试的私有方法 - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("startChat", - Map.class, - Meta.class, - OperationContext.class, - Instance.class, - String.class, - ChatSession.class); - method.setAccessible(true); // 设置访问权限为 true - String word = "aipp.service.impl.AippRunTimeServiceImpl"; - when(localeService.localize(anyString())).thenReturn("test"); - FlowInfo flowInfo = new FlowInfo(); - when(this.flowsService.getFlows(anyString(), any())).thenReturn(flowInfo); - method.invoke(aippRunTimeService, null, mock(Meta.class), null, mock(Instance.class), null); - verify(localeService).localize(Mockito.eq(word)); - } - - @Test - @DisplayName("测试getMemorySwitch方法:当不存在memoryConfig配置项时,返回false") - void testGetMemorySwitchWithNotExistMemoryConfig() - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("getMemorySwitch", List.class, Map.class); - method.setAccessible(true); - assertThat(method.invoke(this.aippRunTimeService, null, null)).isEqualTo(false); - } - - @Test - @DisplayName("测试getMemorySwitch方法:当不存在memorySwitch配置项时,返回为false") - void testGetMemorySwitchWithNotExistMemorySwitch() - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("getMemorySwitch", List.class, Map.class); - method.setAccessible(true); - List> configs = new ArrayList<>(); - Map config1 = new HashMap<>(); - config1.put("name1", "configValue1"); - configs.add(config1); - assertThat(method.invoke(this.aippRunTimeService, configs, null)).isEqualTo(false); - } - - @Test - @DisplayName("测试getMemoryConfig方法,当流程getFlows抛异常时,会抛出Aipp异常") - void testGetMemoryConfigWithGetFlowsError() throws NoSuchMethodException, IllegalAccessException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("getMemoryConfigs", - Meta.class, - String.class, - OperationContext.class); - method.setAccessible(true); - Mockito.when(flowsService.getFlows(Mockito.anyString(), Mockito.any())).thenThrow(JobberException.class); - try { - method.invoke(this.aippRunTimeService, mock(Meta.class), "hello", new OperationContext()); - } catch (InvocationTargetException e) { - assertTrue(e.getCause() instanceof AippException); - assertThat((e.getCause()).getMessage()).isEqualTo("系统错误,获取应用编排信息失败,请联系管理员。"); - } - } - - @Test - @DisplayName("测试getMemoryConfig方法") - void testGetMemoryConfigWithGetFlowsOK() - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("getMemoryConfigs", - Meta.class, - String.class, - OperationContext.class); - method.setAccessible(true); - FlowInfo flowInfo = this.mockFlowInfo("UserSelect"); - Mockito.when(flowsService.getFlows(Mockito.anyString(), Mockito.any())).thenReturn(flowInfo); - Assertions.assertDoesNotThrow(() -> method.invoke(this.aippRunTimeService, - mock(Meta.class), - "hello", - new OperationContext())); - } - - @Test - @DisplayName("测试startFlow方法,当流程startFlow抛异常时,会抛出Aipp异常") - void testStartFlowsWithStartFlowError() throws NoSuchMethodException, IllegalAccessException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("startFlow", - String.class, - String.class, - String.class, - Map.class, - OperationContext.class); - method.setAccessible(true); - Mockito.when(flowInstanceService.startFlow(Mockito.anyString(), Mockito.any(), Mockito.any())) - .thenThrow(JobberException.class); - try { - method.invoke(this.aippRunTimeService, "hello", "hello", "hello", new HashMap<>(), new OperationContext()); - } catch (InvocationTargetException e) { - assertTrue(e.getCause() instanceof AippException); - assertThat((e.getCause()).getMessage()).isEqualTo("会话响应出错,请重试。"); - } - } - - @Test - @DisplayName("测试sendReadyStatus方法") - void testSendReadyStatus() throws NoSuchMethodException { - Method method = AippRunTimeServiceImpl.class.getDeclaredMethod("sendReadyStatus", String.class, Map.class); - method.setAccessible(true); - String metaInstId = "123"; - Map businessData = new HashMap<>(); - businessData.put(AippConst.BS_AT_CHAT_ID, "at_chat"); - businessData.put(AippConst.BS_CHAT_ID, "chat"); - AppChatRsp appChatRsp = AppChatRsp.builder() - .instanceId(metaInstId) - .status(FlowTraceStatus.READY.name()) - .atChatId(ObjectUtils.cast(businessData.get(AippConst.BS_AT_CHAT_ID))) - .chatId(ObjectUtils.cast(businessData.get(AippConst.BS_CHAT_ID))) - .build(); - doNothing().when(this.appChatSSEService).send(eq(metaInstId), eq(appChatRsp)); - - Assertions.assertDoesNotThrow(() -> method.invoke(this.aippRunTimeService, metaInstId, businessData)); - } - - private void mockMetaQuery(String type) { - Meta meta = new Meta(); - meta.setId("meta_id"); - meta.setVersionId("meta_version_id"); - meta.setVersion("version"); - meta.setAttributes(MapBuilder.get() - .put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id") - .build()); - FlowInfo flowInfo = mockFlowInfo(type); - Mockito.when(flowsService.getFlows(Mockito.eq("flow_definition_id"), Mockito.any())).thenReturn(flowInfo); - Mockito.when(metaService.list(Mockito.any(), Mockito.eq(true), Mockito.eq(0L), Mockito.eq(1), Mockito.any())) - .thenReturn(RangedResultSet.create(Collections.singletonList(meta), 0, 1, 1)); - Instance metaInst = new Instance(); - metaInst.setId("instId"); - Mockito.when(this.metaInstanceService.createMetaInstance(Mockito.eq("meta_version_id"), - Mockito.any(), - Mockito.any())).thenReturn(metaInst); - } - - private FlowInfo mockFlowInfo(String type) { - FlowNodeInfo flowNodeInfo = new FlowNodeInfo(); - flowNodeInfo.setType("start"); - flowNodeInfo.setProperties(MapBuilder.get() - .put("inputParams", Arrays.asList(this.buildInput(), this.buildMemory(type))) - .build()); - FlowInfo flowInfo = new FlowInfo(); - flowInfo.setFlowNodes(Collections.singletonList(flowNodeInfo)); - return flowInfo; - } - - private Object buildInput() { - return MapBuilder.get() - .put("name", AippConst.BUSINESS_INPUT_KEY) - .put("value", - Collections.singletonList(MapBuilder.get() - .put("name", AippConst.BS_AIPP_QUESTION_KEY) - .put("value", "question_1") - .build())) - .build(); - } + when(this.appVersionService.retrieval(anyString())).thenReturn(appVersion); - private Map buildMemory(String type) { - return MapBuilder.get() - .put("name", AippConst.MEMORY_CONFIG_KEY) - .put("value", - Arrays.asList(MapBuilder.get() - .put("name", AippConst.MEMORY_SWITCH_KEY) - .put("value", true) - .build(), - MapBuilder.get().put("name", "type").put("value", type).build())) - .build(); + Assertions.assertDoesNotThrow( + () -> this.aippRunTimeService.createLatestAippInstanceByAppId("app_id", true, initContext, context)); } private Map genInitContext() { diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderAppServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderAppServiceImplTest.java index b1131592b9..4c2a82b683 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderAppServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderAppServiceImplTest.java @@ -6,20 +6,12 @@ package modelengine.fit.jober.aipp.service; -import static modelengine.fit.jober.aipp.constants.AippConst.ATTR_APP_ID_KEY; -import static modelengine.fit.jober.aipp.service.impl.UploadedFileMangeServiceImpl.IRREMOVABLE; -import static modelengine.fitframework.util.ObjectUtils.cast; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; @@ -27,82 +19,43 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.alibaba.fastjson.JSONObject; - -import modelengine.fit.jade.aipp.model.service.AippModelCenter; import modelengine.fit.jade.waterflow.AippFlowDefinitionService; -import modelengine.fit.jade.waterflow.FlowsService; import modelengine.fit.jade.waterflow.service.FlowDefinitionService; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jane.meta.multiversion.MetaInstanceService; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.instance.Instance; import modelengine.fit.jober.aipp.common.exception.AippErrCode; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippTaskNotFoundException; import modelengine.fit.jober.aipp.condition.AppQueryCondition; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; -import modelengine.fit.jober.aipp.domain.AppBuilderConfig; -import modelengine.fit.jober.aipp.domain.AppBuilderConfigProperty; -import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; -import modelengine.fit.jober.aipp.domain.AppBuilderForm; -import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; -import modelengine.fit.jober.aipp.dto.AippCreateDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppCreateDto; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.app.AppFactory; +import modelengine.fit.jober.aipp.domains.app.service.AppDomainService; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.AppVersionFactory; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.task.AppTask; +import modelengine.fit.jober.aipp.domains.task.service.AppTaskService; +import modelengine.fit.jober.aipp.dto.AippDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.dto.AppBuilderAppMetadataDto; import modelengine.fit.jober.aipp.dto.AppBuilderConfigDto; -import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormDto; -import modelengine.fit.jober.aipp.dto.AppBuilderConfigFormPropertyDto; import modelengine.fit.jober.aipp.dto.AppBuilderSaveConfigDto; -import modelengine.fit.jober.aipp.dto.AppTypeDto; import modelengine.fit.jober.aipp.dto.check.AppCheckDto; import modelengine.fit.jober.aipp.dto.check.CheckResult; -import modelengine.fit.jober.aipp.dto.export.AppExportApp; -import modelengine.fit.jober.aipp.dto.export.AppExportConfigProperty; -import modelengine.fit.jober.aipp.dto.export.AppExportDto; -import modelengine.fit.jober.aipp.dto.export.AppExportFlowGraph; -import modelengine.fit.jober.aipp.dto.export.AppExportFormProperty; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; +import modelengine.fit.jober.aipp.enums.AppState; +import modelengine.fit.jober.aipp.factory.AppTemplateFactory; import modelengine.fit.jober.aipp.factory.CheckerFactory; import modelengine.fit.jober.aipp.genericable.entity.AippCreate; -import modelengine.fit.jober.aipp.mapper.AippChatMapper; -import modelengine.fit.jober.aipp.mapper.AippLogMapper; -import modelengine.fit.jober.aipp.mapper.AippUploadedFileMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderAppMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderConfigMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderConfigPropertyMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderFlowGraphMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderFormMapper; -import modelengine.fit.jober.aipp.mapper.AppBuilderFormPropertyMapper; import modelengine.fit.jober.aipp.po.AppBuilderAppPo; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; -import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderAppRepositoryImpl; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderConfigPropertyRepositoryImpl; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderConfigRepositoryImpl; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderFlowGraphRepositoryImpl; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderFormPropertyRepositoryImpl; -import modelengine.fit.jober.aipp.repository.impl.AppBuilderFormRepositoryImpl; import modelengine.fit.jober.aipp.service.impl.AppBuilderAppServiceImpl; import modelengine.fit.jober.aipp.service.impl.RetrievalNodeChecker; +import modelengine.fit.jober.aipp.util.ConvertUtils; import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.aipp.validation.AppUpdateValidator; import modelengine.fit.jober.common.RangedResultSet; -import modelengine.fitframework.util.IoUtils; -import modelengine.fitframework.util.MapBuilder; -import modelengine.jade.app.engine.base.service.UsrAppCollectionService; + +import modelengine.fitframework.util.StringUtils; import modelengine.jade.knowledge.KnowledgeCenterService; -import modelengine.jade.knowledge.dto.KnowledgeDto; -import modelengine.jade.store.service.AppService; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -111,20 +64,13 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.Optional; /** * @author 姚江 @@ -133,43 +79,19 @@ @ExtendWith(MockitoExtension.class) public class AppBuilderAppServiceImplTest { @Mock - private AippFlowService aippFlowService; - - @Mock - private AppBuilderAppRepository appRepository; - - @Mock - private AippChatMapper aippChatMapper; - - @Mock - private AppBuilderFlowGraphRepository flowGraphRepository; - - @Mock - private AppBuilderConfigRepository configRepository; - - @Mock - private AppBuilderFormRepository formRepository; - - @Mock - private AppBuilderConfigPropertyRepository configPropertyRepository; - + private UploadedFileManageService uploadedFileService; @Mock - private AppBuilderFormPropertyRepository formPropertyRepository; - + private AppTaskService appTaskService; @Mock - private MetaService metaService; - + private AppVersionService appVersionService; @Mock - private UsrAppCollectionService usrAppCollectionService; - + private AppDomainService appDomainService; @Mock - private UploadedFileManageService uploadedFileService; - + private AppFactory appDomainFactory; @Mock - private AippModelCenter aippModelCenter; + private AppTemplateFactory templateFactory; - @Mock - private AppTypeService appTypeService; + private ConverterFactory converterFactory; @Mock private AippFlowDefinitionService aippFlowDefinitionService; @@ -179,278 +101,27 @@ public class AppBuilderAppServiceImplTest { private AppBuilderAppServiceImpl appBuilderAppService; + private MockedStatic mockConvertUtils; + @Mock private KnowledgeCenterService knowledgeCenterService; - private static final LocalDateTime TIME = LocalDateTime.of(2024, 5, 6, 15, 15, 15); - - private static final String IMPORT_CONFIG = "component/import_config.json"; - @BeforeEach public void before() { - AppBuilderAppFactory factory = new AppBuilderAppFactory(flowGraphRepository, configRepository, formRepository, - configPropertyRepository, formPropertyRepository, appRepository); - appBuilderAppService = new AppBuilderAppServiceImpl(factory, aippFlowService, appRepository, null, 64, - metaService, usrAppCollectionService, null, null, uploadedFileService, null, null, null, null, - this.aippModelCenter, null, MapBuilder.get() - .put("version", "1.0.1") - .put("hash-template", "123") - .put("digest", "MD5") - .build(), appTypeService, null, null, flowDefinitionService, aippFlowDefinitionService, "", knowledgeCenterService); - } + this.converterFactory = mock(ConverterFactory.class); + appBuilderAppService = new AppBuilderAppServiceImpl(this.templateFactory, + this.uploadedFileService, + this.appTaskService, + this.appVersionService, + this.appDomainService, + this.appDomainFactory, this.converterFactory, this.knowledgeCenterService); + mockConvertUtils = mockStatic(ConvertUtils.class); - private AppBuilderApp mockApp() { - AppBuilderApp appBuilderApp = new AppBuilderApp(flowGraphRepository, configRepository, formRepository, - configPropertyRepository, formPropertyRepository); - appBuilderApp.setType("template"); - appBuilderApp.setAppBuiltType("basic"); - appBuilderApp.setAppCategory("chatbot"); - appBuilderApp.setName("Unit Test App"); - appBuilderApp.setTenantId("727d7157b3d24209aefd59eb7d1c49ff"); - appBuilderApp.setId("45698235b3d24209aefd59eb7d1c3322"); - appBuilderApp.setAttributes(new HashMap<>()); - appBuilderApp.getAttributes() - .put("icon", "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/var/share/test_old" - + ".jpg&fileName=test_old.jpg"); - appBuilderApp.getAttributes().put("app_type", "编程开发"); - appBuilderApp.getAttributes().put("greeting", "1"); - appBuilderApp.getAttributes().put("description", "1"); - appBuilderApp.setState("inactive"); - appBuilderApp.setCreateBy("yaojiang"); - appBuilderApp.setUpdateBy("yaojiang"); - appBuilderApp.setUpdateAt(TIME); - appBuilderApp.setCreateAt(TIME); - appBuilderApp.setVersion("1.0.0"); - appBuilderApp.setPath("YGHmQFJE5ZaFW4wl"); - appBuilderApp.setConfig(this.mockConfig()); - appBuilderApp.getConfig().setApp(appBuilderApp); - appBuilderApp.setConfigId(appBuilderApp.getConfig().getId()); - appBuilderApp.setFlowGraph(this.mockGraph()); - appBuilderApp.setFlowGraphId(appBuilderApp.getFlowGraph().getId()); - appBuilderApp.setFormProperties(mockFormProperties()); - return appBuilderApp; } - private AppBuilderConfig mockConfig() { - AppBuilderConfig config = new AppBuilderConfig(this.formRepository, this.formPropertyRepository, - this.configPropertyRepository, this.appRepository); - - config.setAppId("45698235b3d24209aefd59eb7d1c3322"); - config.setId("24581235b3d24209aefd59eb7d1c3322"); - - config.setUpdateAt(TIME); - config.setCreateAt(TIME); - config.setCreateBy("yaojiang"); - config.setUpdateBy("yaojiang"); - config.setTenantId("727d7157b3d24209aefd59eb7d1c49ff"); - - config.setForm(mockForm()); - config.setFormId(config.getForm().getId()); - config.setConfigProperties(mockConfigProperties()); - List formProperties = mockFormProperties(); - for (int i = 0; i < 8; i++) { - AppBuilderConfigProperty configProperty = config.getConfigProperties().get(i); - configProperty.setConfig(config); - configProperty.setFormProperty(formProperties.get(i)); - } - return config; - } - - private AppBuilderForm mockForm() { - AppBuilderForm form = new AppBuilderForm(this.formPropertyRepository); - form.setId("cc65e235b3d24209aefd59eb7d1a5499"); - form.setName("表单"); - form.setTenantId("727d7157b3d24209aefd59eb7d1c49ff"); - form.setUpdateAt(TIME); - form.setCreateAt(TIME); - form.setCreateBy("yaojiang"); - form.setUpdateBy("yaojiang"); - form.setType("component"); - form.setAppearance(null); - return form; - } - - private List mockFormProperties() { - Object[] values = new Object[] { - "null", "null", Collections.singletonList("jadewdnjbq"), - Arrays.asList(Arrays.asList("jadewdnjbq", "tools"), Arrays.asList("jadewdnjbq", "workflows")), - Arrays.asList("jade0pg2ag", "knowledge"), "null", Arrays.asList("jade6qm5eg", "memory"), - JSONObject.parseObject( - "{\"category\":[{\"title\":\"root\",\"id\":\"root\",\"children\":[]}],\"inspirations\":[]}"), - JSONObject.parseObject("{\"showRecommend\":false, \"list\":[]}"), - "i18n_appBuilder_{form_property_opening_content}" - }; - String[] names = new String[] { - "basic", "ability", "model", "tools", "knowledge", "chat", "memory", "inspiration", "recommend", - "opening" - }; - String[] dataTypes = new String[] { - "String", "String", "List", "List>", "List", "String", "List", - "object", "object", "String" - }; - String[] from = new String[] { - "none", "none", "graph", "graph", "graph", "none", "graph", "input", "input", "input" - }; - String[] group = new String[] { - "null", "basic", "ability", "ability", "ability", "basic", "chat", "chat", "chat", "chat" - }; - String[] description = new String[] { - "i18n_appBuilder_{form_property_basic}", "i18n_appBuilder_{form_property_ability}", - "i18n_appBuilder_{form_property_model}", "i18n_appBuilder_{form_property_tools}", - "i18n_appBuilder_{form_property_knowledge}", "i18n_appBuilder_{form_property_chat}", - "i18n_appBuilder_{form_property_memory}", "i18n_appBuilder_{form_property_inspiration}", - "i18n_appBuilder_{form_property_recommend}", "i18n_appBuilder_{form_property_opening}" - }; - List formProperties = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - AppBuilderFormProperty formProperty = new AppBuilderFormProperty(); - formProperty.setFormId("cc65e235b3d24209aefd59eb7d1a5499"); - formProperty.setName(names[i]); - formProperty.setId(i + "c65e235b3d24209aefd59eb7d1a549" + i); - formProperty.setDataType(dataTypes[i]); - formProperty.setDefaultValue(values[i]); - formProperty.setFrom(from[i]); - formProperty.setGroup(group[i]); - formProperty.setDescription(description[i]); - formProperty.setFormRepository(this.formRepository); - formProperties.add(formProperty); - } - return formProperties; - } - - private List mockConfigProperties() { - String[] nodeIds = new String[] {"4agtId", "4agtId", "4agtId", "4agtId", "sciinj", null, null, "4agtId"}; - - List configProperties = new ArrayList<>(); - for (int i = 0; i < 8; i++) { - AppBuilderConfigProperty configProperty = new AppBuilderConfigProperty(this.configRepository, - this.formRepository, this.formPropertyRepository); - configProperty.setConfigId(i + "275e235b3d24209aefd59eb8541a549" + i); - configProperty.setFormPropertyId(i + "c65e235b3d24209aefd59eb7d1a549" + i); - configProperty.setNodeId(nodeIds[i]); - configProperty.setConfigId("24581235b3d24209aefd59eb7d1c3322"); - configProperties.add(configProperty); - } - return configProperties; - } - - private AppBuilderFlowGraph mockGraph() { - String appearance = - "{\"id\": \"69e9dec999384b1791e24a3032010e77\", \"type\": \"jadeFlowGraph\", \"pages\": [{\"x\": 0, " - + "\"y\": 0, \"id\": \"elsa-page:t1qrig\", \"bold\": false, \"mode\": \"configuration\", " - + "\"text\": \"newFlowPage\", \"type\": \"jadeFlowPage\", \"dirty\": false, \"index\": 0, " - + "\"width\": 2000, \"hAlign\": \"left\", \"height\": 1600, \"isPage\": true, \"italic\": " - + "false, \"shapes\": [{\"x\": 194, \"y\": 134, \"id\": \"a6lgso\", \"pad\": 6, \"bold\": " - + "false, \"name\": \"被监听者\", \"text\": \"\", \"type\": \"jadeInputNode\", \"dirty\": true, " - + "\"index\": 100, \"jober\": {\"name\": \"\", \"type\": \"general_jober\", \"fitables\": []," - + " \"converter\": {\"type\": \"mapping_converter\", \"entity\": []}}, \"width\": 500, " - + "\"height\": 180, \"italic\": false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", " - + "\"hideText\": true, \"backColor\": \"white\", \"container\": \"elsa-page:t1qrig\", " - + "\"dashWidth\": 0, \"namespace\": \"flowable\", \"autoHeight\": true, \"rotateAble\": " - + "false, \"borderColor\": \"rgba(28,31,35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0" - + " 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": " - + "\"auto\", \"warningTask\": 0, \"completedTask\": 0, \"componentName\": " - + "\"jadeInputComponent\", \"focusBackColor\": \"white\", \"enableAnimation\": true, " - + "\"focusBorderColor\": \"#4d53e8\", \"focusBorderWidth\": 2, \"mouseInBorderColor\": " - + "\"#4d53e8\"}, {\"x\": 120, \"y\": 80, \"id\": \"8ka4p9\", \"pad\": 6, \"bold\": false, " - + "\"name\": \"大模型\", \"text\": \"\", \"type\": \"llmNode\", \"dirty\": true, \"index\": 101," - + " \"jober\": {\"name\": \"\", \"type\": \"general_jober\", \"fitables\": [], \"converter\":" - + " {\"type\": \"mapping_converter\", \"entity\": [{\"id\": " - + "\"c4aa80b5-70ef-4fb8-91f6-c9a832d5d6ec\", \"from\": \"value\", \"name\": \"model\", " - + "\"type\": \"String\", \"value\": \"Qwen\"}, {\"id\": " - + "\"96282fba-d75e-464c-b021-f390beccc942\", \"from\": \"value\", \"name\": \"temperature\", " - + "\"type\": \"Number\", \"value\": \"0.3\"}, {\"id\": " - + "\"b6440db6-f8c7-4775-870e-ba32623b8945\", \"from\": \"value\", \"name\": \"prompt\", " - + "\"type\": \"Object\", \"value\": [{\"id\": \"70f63499-8a4a-48c4-a5e0-e616d9ac2c08\", " - + "\"from\": \"value\", \"name\": \"template\", \"type\": \"String\", \"value\": \"\"}, " - + "{\"id\": \"15402f32-0ca9-46db-868c-69be7df334c0\", \"from\": \"value\", \"name\": " - + "\"variables\", \"type\": \"Object\", \"value\": [{\"id\": " - + "\"17832beb-c237-459e-8432-f0457a858413\", \"from\": \"reference\", \"name\": \"\", " - + "\"type\": \"String\", \"value\": \"\", \"referenceId\": \"\", \"referenceKey\": \"\", " - + "\"referenceNode\": \"\"}]}]}, {\"id\": \"5ec6d065-c6b9-4685-ad1b-ea5459d03069\", \"from\":" - + " \"value\", \"name\": \"tools\", \"type\": \"array\", \"value\": []}, {\"id\": " - + "\"bf1721b7-a88a-43e8-9ee1-8e6938c589b6\", \"from\": \"value\", \"name\": \"workflows\", " - + "\"type\": \"array\", \"value\": []}, {\"id\": \"b0215ced-f97f-4f87-883d-a76adb31abd7\", " - + "\"from\": \"value\", \"name\": \"output\", \"type\": \"Object\", \"value\": [{\"id\": " - + "\"24e24707-19ff-424c-a007-6c2d4a5abf05\", \"from\": \"value\", \"name\": \"llmOutput\", " - + "\"type\": \"string\", \"value\": \"\"}]}]}}, \"width\": 500, \"height\": 836, \"italic\": " - + "false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", \"hideText\": true, \"backColor\": " - + "\"white\", \"container\": \"elsa-page:t1qrig\", \"dashWidth\": 0, \"namespace\": " - + "\"flowable\", \"autoHeight\": true, \"rotateAble\": false, \"borderColor\": \"rgba(28,31," - + "35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba" - + "(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": \"auto\", \"warningTask\": 0, " - + "\"completedTask\": 0, \"componentName\": \"llmComponent\", \"focusBackColor\": \"white\", " - + "\"enableAnimation\": true, \"focusBorderColor\": \"#4d53e8\", \"focusBorderWidth\": 2, " - + "\"mouseInBorderColor\": \"#4d53e8\"}], \"vAlign\": \"top\", \"itemPad\": [0, 0, 0, 0], " - + "\"division\": -1, \"dockMode\": \"none\", \"fontFace\": \"arial\", \"fontSize\": 18, " - + "\"hideText\": true, \"moveable\": true, \"shapesAs\": {}, \"backColor\": \"#f2f3f5\", " - + "\"container\": \"elsa-page:t1qrig\", \"dockAlign\": \"top\", \"fontColor\": \"#ECD0A7\", " - + "\"fontStyle\": \"normal\", \"itemSpace\": 5, \"namespace\": \"jadeFlow\", \"fontWeight\": " - + "\"bold\", \"itemScroll\": {\"x\": 0, \"y\": 0}, \"borderColor\": \"white\", " - + "\"focusBackColor\": \"#f2f3f5\"}], \"title\": \"jadeFlow\", \"source\": \"elsa\", " - + "\"tenant\": \"default\", \"setting\": {\"pad\": 10, \"tag\": {}, \"code\": \"\", " - + "\"pDock\": \"none\", \"hAlign\": \"center\", \"margin\": 25, \"shadow\": \"\", \"shared\":" - + " false, \"vAlign\": \"top\", \"itemPad\": [5, 5, 5, 5], \"visible\": true, \"autoText\": " - + "false, \"dockMode\": \"none\", \"dragable\": true, \"editable\": true, \"fontFace\": " - + "\"arial\", \"fontSize\": 12, \"infoType\": {\"name\": \"none\", \"next\": " - + "\"INFORMATION\"}, \"moveable\": true, \"priority\": 0, \"allowLink\": true, \"autoWidth\":" - + " false, \"backAlpha\": 0.15, \"backColor\": \"whitesmoke\", \"dashWidth\": 0, " - + "\"deletable\": true, \"fontColor\": \"steelblue\", \"fontStyle\": \"normal\", " - + "\"headColor\": \"steelblue\", \"lineWidth\": 2, \"underline\": false, \"autoHeight\": " - + "false, \"emphasized\": false, \"fontWeight\": \"lighter\", \"itemScroll\": {\"x\": 0, " - + "\"y\": 0}, \"lineHeight\": 1.5, \"resizeable\": true, \"rotateAble\": true, " - + "\"scrollLock\": {\"x\": false, \"y\": false}, \"selectable\": true, \"shadowData\": \"2px " - + "2px 4px\", \"borderColor\": \"steelblue\", \"borderWidth\": 1, \"bulletSpeed\": 1, " - + "\"focusMargin\": 0, \"focusShadow\": \"\", \"globalAlpha\": 1, \"outstanding\": false, " - + "\"bulletedList\": false, \"cornerRadius\": 4, \"enableSocial\": true, \"mouseInColor\": " - + "\"orange\", \"numberedList\": false, \"rotateDegree\": 0, \"captionhAlign\": \"center\", " - + "\"strikethrough\": false, \"focusBackColor\": \"whitesmoke\", \"focusFontColor\": " - + "\"darkorange\", \"progressStatus\": {\"name\": \"NONE\", \"next\": \"UNKNOWN\", \"color\":" - + " \"gray\"}, \"showedProgress\": false, \"captionfontFace\": \"arial black\", " - + "\"captionfontSize\": 14, \"enableAnimation\": false, \"progressPercent\": 0.65, " - + "\"captionfontColor\": \"whitesmoke\", \"captionfontStyle\": \"normal\", " - + "\"focusBorderColor\": \"darkorange\", \"focusBorderWidth\": 1, \"mouseInBackColor\": " - + "\"whitesmoke\", \"mouseInFontColor\": \"orange\", \"captionfontWeight\": \"lighter\", " - + "\"captionlineHeight\": 1, \"mouseInBorderColor\": \"orange\"}}"; - AppBuilderFlowGraph graph = new AppBuilderFlowGraph("69e9dec999384b1791e24a3032010e77", "graph", appearance); - graph.setUpdateAt(TIME); - graph.setCreateAt(TIME); - graph.setCreateBy("yaojiang"); - graph.setUpdateBy("yaojiang"); - return graph; - } - - /** - * 为 {@link AppBuilderAppServiceImpl#create(String, AppBuilderAppCreateDto, OperationContext, boolean)} 提供测试 - */ - @Nested - @DisplayName("创建测试") - class TestCreate { - @Test - @DisplayName("测试创建基础编排") - public void testBuildBasicApp() { - OperationContext context = new OperationContext(); - AppBuilderAppCreateDto appCreateDto = new AppBuilderAppCreateDto(); - appCreateDto.setAppType("app"); - appCreateDto.setAppBuiltType("basic"); - String appName = "appName"; - String appId = "appId1"; - appCreateDto.setName(appName); - appCreateDto.setAppCategory("chatbot"); - appCreateDto.setDescription(""); - AppBuilderApp appTemplate = mockApp(); - AippCreateDto aippCreateDto = new AippCreateDto(); - aippCreateDto.setAippId("aippId1"); - List knowledgeDtos = new ArrayList<>(); - knowledgeDtos.add(KnowledgeDto.builder().groupId("default").build()); - when(appRepository.selectWithId(appId)).thenReturn(appTemplate); - when(aippFlowService.previewAipp(anyString(), any(), any())).thenReturn(aippCreateDto); - when(aippModelCenter.fetchModelList(any(), any(), any())).thenReturn(null); - when(knowledgeCenterService.getSupportKnowledges(any())).thenReturn(knowledgeDtos); - AppBuilderAppDto appBuilderAppDto = appBuilderAppService.create(appId, appCreateDto, context, false); - assertThat(appBuilderAppDto.getName()).isEqualTo(appName); - assertThat(appBuilderAppDto.getAppCategory()).isEqualTo("chatbot"); - } + @AfterEach + void tearDown() { + mockConvertUtils.close(); } /** @@ -462,240 +133,82 @@ class TestUpdateFlow { @Test @DisplayName("测试需要更新") public void testTrue() { - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_APP_IS_UPDATE, true); - Mockito.when(appRepository.selectWithId("hello")) - .thenReturn(AppBuilderApp.builder() - .id("hello") - .appBuiltType("basic") - .attributes(attributes) - .flowGraph(AppBuilderFlowGraph.builder().appearance("{}").build()) - .config(AppBuilderConfig.builder() - .form(AppBuilderForm.builder().build()) - .configProperties(new ArrayList<>()) - .build()) - .build()); - Assertions.assertDoesNotThrow(() -> appBuilderAppService.updateFlow("hello", new OperationContext())); + AppVersion appVersion = mock(AppVersion.class); + when(appVersion.getData()).thenReturn(new AppBuilderAppPo()); + when(appVersionService.retrieval("hello")).thenReturn(appVersion); + when(appVersion.isUpdated()).thenReturn(true); + AppBuilderAppDto appDto = AppBuilderAppDto.builder().attributes(new HashMap<>()).build(); + AippDto aippDto = AippDto.builder().flowViewData(new HashMap<>()).build(); + mockConvertUtils.when(() -> ConvertUtils.convertToAippDtoFromAppBuilderAppDto(appDto)).thenReturn(aippDto); + + appBuilderAppService.updateFlow("hello", new OperationContext()); + + verify(appVersion, times(1)).preview(any(), any(), any()); + verify(appVersionService, times(1)).update(appVersion); } } - /** - * 为 {@link AppBuilderAppServiceImpl#delete(String, OperationContext)} 提供测试 - */ - @Nested - class TestDelete extends DatabaseBaseTest { - private final AppBuilderAppMapper appBuilderAppMapper = sqlSessionManager.openSession(true) - .getMapper(AppBuilderAppMapper.class); - - private final AppBuilderConfigMapper appBuilderConfigMapper = sqlSessionManager.openSession(true) - .getMapper(AppBuilderConfigMapper.class); - - private final AppBuilderConfigPropertyMapper appBuilderConfigPropertyMapper = sqlSessionManager.openSession( - true).getMapper(AppBuilderConfigPropertyMapper.class); - - private final AppBuilderFlowGraphMapper appBuilderFlowGraphMapper = sqlSessionManager.openSession(true) - .getMapper(AppBuilderFlowGraphMapper.class); - - private final AppBuilderFormMapper appBuilderFormMapper = sqlSessionManager.openSession(true) - .getMapper(AppBuilderFormMapper.class); - - private final AppBuilderFormPropertyMapper appBuilderFormPropertyMapper = sqlSessionManager.openSession(true) - .getMapper(AppBuilderFormPropertyMapper.class); - - private final AppBuilderFormPropertyRepository formPropertyRepository - = new AppBuilderFormPropertyRepositoryImpl(this.appBuilderFormPropertyMapper); - - private final AppBuilderFormRepository formRepository = new AppBuilderFormRepositoryImpl( - this.appBuilderFormMapper, this.formPropertyRepository); - - private final AppBuilderConfigPropertyRepository configPropertyRepository - = new AppBuilderConfigPropertyRepositoryImpl(this.appBuilderConfigPropertyMapper); - - private final AppBuilderConfigRepository configRepository = new AppBuilderConfigRepositoryImpl( - this.appBuilderConfigMapper, this.configPropertyRepository); - - private final AppBuilderAppRepository appRepository = new AppBuilderAppRepositoryImpl(this.appBuilderAppMapper); - - private final AppBuilderFlowGraphRepository flowGraphRepository = new AppBuilderFlowGraphRepositoryImpl( - this.appBuilderFlowGraphMapper); - - private AippFlowService aippFlowService; - - private final MetaService metaService = mock(MetaService.class); - - private final UsrAppCollectionService usrAppCollectionService = mock(UsrAppCollectionService.class); - - private final MetaInstanceService metaInstanceService = mock(MetaInstanceService.class); - - private final UploadedFileManageService uploadedFileManageService = Mockito.mock( - UploadedFileManageService.class); - - private final AippUploadedFileMapper aippUploadedFileMapper = mock(AippUploadedFileMapper.class); - - private final AippLogMapper aippLogMapper = mock(AippLogMapper.class); - - private final FlowsService flowsService = mock(FlowsService.class); - - private final AppService appService = mock(AppService.class); - - private final AippChatService aippChatService = mock(AippChatService.class); - - private final AippChatMapper aippChatMapper = Mockito.mock(AippChatMapper.class); - - private final AippModelCenter aippModelCenter = mock(AippModelCenter.class); - - private final AppUpdateValidator appUpdateValidator = mock(AppUpdateValidator.class); - - private final KnowledgeCenterService knowledgeCenterService = mock(KnowledgeCenterService.class); - - private final AppBuilderAppFactory factory = new AppBuilderAppFactory(this.flowGraphRepository, - this.configRepository, this.formRepository, this.configPropertyRepository, this.formPropertyRepository, - this.appRepository); + @Test + @DisplayName("更新 config") + void testUpdateConfig() { + AppBuilderAppPo appPo = AppBuilderAppPo.builder() + .name("oldName") + .status(AppState.PUBLISHED.getName()) + .build(); + AppVersion appVersion = spy(mockAppVersion(appPo)); + when(appVersionService.retrieval(anyString())).thenReturn(appVersion); + doNothing().when(appVersion).updateConfig(any(), any(), any()); + doNothing().when(appVersion).updateGraph(any(), any()); + OperationContext context = new OperationContext(); + context.setOperator("tester"); - private final AppBuilderAppService appBuilderAppService = new AppBuilderAppServiceImpl(this.factory, - this.aippFlowService, this.appRepository, null, 64, this.metaService, this.usrAppCollectionService, - this.appUpdateValidator, this.metaInstanceService, this.uploadedFileManageService, this.aippLogMapper, - this.flowsService, this.appService, this.aippChatService, this.aippModelCenter, this.aippChatMapper, - null, null, null, null, null, null, - "", knowledgeCenterService); + this.appBuilderAppService.updateConfig("testId", new AppBuilderConfigDto(), new ArrayList<>(), context); - @Test - @DisplayName("更新 config") - void testUpdateConfig() { - AppBuilderAppPo appPo = this.appBuilderAppMapper.selectWithId("3a617d8aeb1d41a9ad7453f2f0f70d61"); - doNothing().when(appUpdateValidator).validate(anyString()); - String configId = appPo.getConfigId(); - AppBuilderConfig config = this.configRepository.selectWithId(configId); - String formId = config.getFormId(); - AppBuilderForm appBuilderForm = this.formRepository.selectWithId(formId); - List appBuilderFormProperties = this.formPropertyRepository.selectWithFormId( - formId); - AppBuilderConfigFormDto newFormDto = AppBuilderConfigFormDto.builder() - .appearance(appBuilderForm.getAppearance()) - .id(appBuilderForm.getId()) - .name(appBuilderForm.getName()) - .build(); - AppBuilderFormProperty newFormProperty = AppBuilderFormProperty.builder() - .id("789") - .formId("369") - .name("test") - .dataType("String") - .defaultValue("test") - .build(); - appBuilderFormProperties.add(newFormProperty); - AppBuilderConfigDto newConfigDto = AppBuilderConfigDto.builder().form(newFormDto).build(); - List formPropertyDtos = appBuilderFormProperties.stream() - .map(AppBuilderFormProperty::toAppBuilderConfigFormPropertyDto) - .peek(dto -> dto.setDefaultValue("newValue")) - .collect(Collectors.toList()); - OperationContext context = new OperationContext(); - context.setOperator("test"); - Rsp newAppDto = this.appBuilderAppService.updateConfig("3a617d8aeb1d41a9ad7453f2f0f70d61", - newConfigDto, formPropertyDtos, context); - assertEquals(newAppDto.getData().getConfigFormProperties().get(0).getDefaultValue(), "newValue"); - } + verify(appVersionService, times(1)).update(appVersion); + Assertions.assertEquals(appVersion.getData().getUpdateBy(), "tester"); + } - @Test - @DisplayName("测试删除 app 所有信息") - void testDeleteAppBasicData() { - Meta meta = this.buildMeta(); - RangedResultSet metas = RangedResultSet.create(Collections.singletonList(meta), 0, 1, 1); - Mockito.when(this.metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())) - .thenReturn(metas); - RangedResultSet instances = this.buildInstances(); - Mockito.when(this.metaInstanceService.list(eq("vid1"), any(), anyLong(), anyInt(), any())) - .thenReturn(instances); - Mockito.when(this.metaInstanceService.list(eq("vid1"), anyLong(), anyInt(), any())) - .thenReturn(instances); - Mockito.when(this.aippChatMapper.deleteAppByAippId(anyString())).thenReturn(1); - AppBuilderAppPo appPo = this.appBuilderAppMapper.selectWithId("df87073b9bc85a48a9b01eccc9afccc4"); - String configId = appPo.getConfigId(); - AppBuilderConfig config = this.configRepository.selectWithId(configId); - List configProperties = this.configPropertyRepository.selectWithConfigId( - configId); - String formId = config.getFormId(); - assertEquals(appPo.getId(), "df87073b9bc85a48a9b01eccc9afccc4"); - AppBuilderForm appBuilderForm = this.formRepository.selectWithId(formId); - assertEquals(configProperties.size(), 8); - assertEquals(appBuilderForm.getId(), "b8986770a6ffef44bbf2a9f26d6fc1bc"); - List appBuilderFormProperties = this.formPropertyRepository.selectWithFormId( - formId); - assertEquals(appBuilderFormProperties.size(), 10); - - this.appBuilderAppService.delete("df87073b9bc85a48a9b01eccc9afccc4", new OperationContext()); - - AppBuilderAppPo newAppPo = this.appBuilderAppMapper.selectWithId("df87073b9bc85a48a9b01eccc9afccc4"); - assertNull(newAppPo); - AppBuilderForm newAppBuilderForm = this.formRepository.selectWithId(formId); - List newConfigProperties = this.configPropertyRepository.selectWithConfigId( - configId); - assertEquals(newConfigProperties.size(), 0); - List newFormProperties = this.formPropertyRepository.selectWithFormId(formId); - assertEquals(newFormProperties.size(), 0); - } + @Test + @DisplayName("测试删除app成功") + public void testDeleteApp() { + OperationContext context = new OperationContext(); + String appId = "testId"; - private RangedResultSet buildInstances() { - Instance instance1 = new Instance(); - instance1.setId("instanceId1"); - Instance instance2 = new Instance(); - instance2.setId("instanceId2"); - return RangedResultSet.create(Arrays.asList(instance1, instance2), 0, 2, 2); - } + this.appBuilderAppService.delete(appId, context); - private Meta buildMeta() { - Meta meta = new Meta(); - meta.setId("mid1"); - meta.setVersionId("vid1"); - meta.setVersion("v1"); - Map attr = new HashMap<>(); - attr.put(ATTR_APP_ID_KEY, "df87073b9bc85a48a9b01eccc9afccc4"); - meta.setAttributes(attr); - return meta; - } + verify(appDomainService, times(1)).deleteByAppId(appId, context); } @Test - @DisplayName("测试生成版本号符合预期") - public void testGenerateVersion() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method method = AppBuilderAppServiceImpl.class.getDeclaredMethod("buildVersion", AppBuilderApp.class, - boolean.class); - method.setAccessible(true); - AppBuilderApp appBuilderApp = mockApp(); - appBuilderApp.setVersion("10.99.99"); - Object result = method.invoke(appBuilderAppService, appBuilderApp, true); - assertThat(result).isInstanceOf(String.class); - String resAfterCast = cast(result); - assertThat(resAfterCast).isEqualTo("10.99.99"); + @DisplayName("测试查询单个app成功") + public void testQuerySucceed() { + AppVersion appVersion = mock(AppVersion.class); + when(appVersionService.getByAppId(anyString())).thenReturn(Optional.of(appVersion)); + when(appVersion.getData()).thenReturn(AppBuilderAppPo.builder().appSuiteId("id1").build()); + when(appVersionService.getFirstCreatedByAppSuiteId(anyString())).thenReturn(Optional.of(appVersion)); + when(converterFactory.convert(any(), any())).thenReturn(AppBuilderAppDto.builder().aippId("id1").build()); + Assertions.assertDoesNotThrow(() -> appBuilderAppService.query("testId", new OperationContext())); } @Test - @DisplayName("测试查询app能得到最初创建的app的创建时间") - public void testQuery() { - Meta mockMeta = new Meta(); - mockMeta.setId("test"); - mockMeta.setAttributes(MapBuilder.get().put(ATTR_APP_ID_KEY, "test-appid").build()); - LocalDateTime mockCreateAt = LocalDateTime.of(2024, 12, 21, 12, 0, 0); - mockMeta.setCreationTime(mockCreateAt); - List mockMetas = Collections.singletonList(mockMeta); - when(appRepository.selectWithId(any())).thenReturn(mockApp()); - when(metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( - RangedResultSet.create(mockMetas, 0, 1, 1)); - AppBuilderAppDto appBuilderAppDto = appBuilderAppService.query("test-appid", new OperationContext()); - assertThat(appBuilderAppDto.getBaselineCreateAt()).isEqualTo(mockCreateAt); - - appBuilderAppDto = appBuilderAppService.queryLatestOrchestration("test-appid", new OperationContext()); - assertThat(appBuilderAppDto.getBaselineCreateAt()).isEqualTo(mockCreateAt); + @DisplayName("测试查询单个app失败,抛出对应异常") + public void testQueryFailed() { + when(appVersionService.getByAppId(anyString())).thenReturn(Optional.empty()); + + AippException exception = Assertions.assertThrows(AippException.class, + () -> appBuilderAppService.query("testId", new OperationContext())); + + Assertions.assertEquals(AippErrCode.APP_NOT_FOUND.getCode(), exception.getCode()); } @Test @DisplayName("测试查询最新编排流程图") void testQueryByPathValidPath() { String validPath = "YGHmQFJE5ZaFW4wl"; - when(appRepository.selectWithPath(any())).thenReturn(mockApp()); - AppBuilderAppDto result = appBuilderAppService.queryByPath(validPath); - assertEquals("45698235b3d24209aefd59eb7d1c3322", result.getId()); - assertEquals("Unit Test App", result.getName()); - assertEquals("/chat/YGHmQFJE5ZaFW4wl", result.getChatUrl()); + AppVersion appVersion = mock(AppVersion.class); + when(appVersionService.getByPath(anyString())).thenReturn(Optional.of(appVersion)); + when(converterFactory.convert(any(), any())).thenReturn(AppBuilderAppDto.builder().build()); + Assertions.assertDoesNotThrow(() -> appBuilderAppService.queryByPath(validPath)); } @Test @@ -709,19 +222,22 @@ void testQueryByPathInvalidPath() { } @Test - @DisplayName("测试查询最新编排流程图") - void testQueryLatestOrchestration() { - OperationContext context = new OperationContext(); - Meta mockMeta = new Meta(); - mockMeta.setId("test"); - mockMeta.setAttributes(MapBuilder.get().put(ATTR_APP_ID_KEY, "test-appid").build()); - LocalDateTime mockCreateAt = LocalDateTime.of(2024, 12, 21, 12, 0, 0); - mockMeta.setCreationTime(mockCreateAt); - List mockMetas = Collections.singletonList(mockMeta); - when(metaService.list(any(), anyBoolean(), anyLong(), anyInt(), any())).thenReturn( - RangedResultSet.create(mockMetas, 0, 1, 1)); - AippCreate aippCreate = appBuilderAppService.queryLatestPublished("123", context); - Assertions.assertEquals(aippCreate.getAippId(), "test"); + @DisplayName("测试查询app最新发布版本") + void testQueryLatestPublish() { + String appId = "appId"; + String suiteId = "suiteId"; + String version = "1.0.0"; + AppBuilderAppPo appPo = AppBuilderAppPo.builder().appSuiteId(suiteId).appId(appId).build(); + AppVersion appVersion = spy(mockAppVersion(appPo)); + AppTask appTask = AppTask.asEntity().setVersion(version).setAppId(appId).build(); + when(appVersionService.retrieval(any())).thenReturn(appVersion); + doReturn(appTask).when(appVersion).getLatestPublishedTask(any()); + + AippCreate aippCreate = this.appBuilderAppService.queryLatestPublished(appId, new OperationContext()); + + Assertions.assertEquals(version, aippCreate.getVersion()); + Assertions.assertEquals(suiteId, aippCreate.getAippId()); + Assertions.assertEquals(appId, aippCreate.getAppId()); } @Nested @@ -736,178 +252,31 @@ void testUpdateWhenDtoIsNull() { } @Test - @DisplayName("测试icon变化时更新成功") - void testUpdateAppWhenIconChanges() throws AippTaskNotFoundException { + @DisplayName("测试Dto不为空时更新成功") + void testUpdateAppWhenAppChanges() throws AippTaskNotFoundException { String appId = "45698235b3d24209aefd59eb7d1c3322"; - AppBuilderAppFactory appFactory = mock(AppBuilderAppFactory.class); - UploadedFileManageService uploadedFileManageService = mock(UploadedFileManageService.class); - AppUpdateValidator appUpdateValidator = mock(AppUpdateValidator.class); - AppBuilderAppServiceImpl service = spy(new AppBuilderAppServiceImpl(appFactory, - null, - null, - null, - 100, - null, - null, - appUpdateValidator, - null, - uploadedFileManageService, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "", - null)); - doNothing().when(service).validateUpdateApp(any(), any(), any()); - doNothing().when(appUpdateValidator).validate(anyString()); - when(appFactory.create(anyString())).thenReturn(mockApp()); - doNothing().when(appFactory).update(any()); - Assertions.assertDoesNotThrow(() -> service.updateApp(appId, mockAppDto(), new OperationContext())); - } - - private AppBuilderAppDto mockAppDto() { - AppBuilderAppDto appDto = new AppBuilderAppDto(); - appDto.setAttributes(new HashMap<>()); - appDto.getAttributes() - .put("icon", "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/var/share/test_new" - + ".jpg&fileName=test_new.jpg"); - appDto.setId("45698235b3d24209aefd59eb7d1c3322"); - appDto.setName("1"); - appDto.setType("app"); - return appDto; - } - } - - @Test - @DisplayName("测试生成独特path方法") - public void testGenerateUniquePathWithSuccessfulGeneration() throws Exception { - when(appRepository.checkPathExists(anyString())).thenReturn(false); - Method method = AppBuilderAppServiceImpl.class.getDeclaredMethod("generateUniquePath"); - method.setAccessible(true); // 允许访问私有方法 - Object result = method.invoke(appBuilderAppService); - if (result instanceof String) { - String path = (String) result; // 安全地进行类型转换 - verify(appRepository, times(1)).checkPathExists(anyString()); + AppBuilderAppDto appDto = AppBuilderAppDto.builder().name("newName").build(); + OperationContext context = new OperationContext(); + when(appVersionService.update(anyString(), any(AppBuilderAppDto.class), + any(OperationContext.class))).thenReturn(mock(AppVersion.class)); + appBuilderAppService.updateApp(appId, appDto, context); + verify(appVersionService, times(1)).update(appId, appDto, context); } } - @Test - @DisplayName("测试生成独特path方法报错") - void testGenerateUniquePathWithFailureAfterRetries() throws Exception { - // 模拟 checkPathExists 总是返回 true,即路径总是存在 - when(appRepository.checkPathExists(anyString())).thenReturn(true); - - // 使用反射调用 generateUniquePath 方法 - Method method = AppBuilderAppServiceImpl.class.getDeclaredMethod("generateUniquePath"); - method.setAccessible(true); // 允许访问私有方法 - - // 捕获反射调用时抛出的异常 - InvocationTargetException exception = assertThrows(InvocationTargetException.class, () -> { - method.invoke(appBuilderAppService); - }); - - // 获取原始异常,即被反射方法抛出的异常 - Throwable cause = exception.getCause(); - - // 验证异常类型是 AippException - assertEquals(AippException.class, cause.getClass()); - assertEquals("系统错误,更新应用配置失败,请联系管理员。", cause.getMessage()); - } - - @Test - @DisplayName("测试保存新app方法") - void testSaveNewApp() throws NoSuchMethodException { - Method saveNewAppBuilderApp = AppBuilderAppServiceImpl.class.getDeclaredMethod("saveNewAppBuilderApp", - AppBuilderApp.class); - saveNewAppBuilderApp.setAccessible(true); - Assertions.assertDoesNotThrow(() -> saveNewAppBuilderApp.invoke(appBuilderAppService, mockApp())); - verify(uploadedFileService).updateRecord(eq("45698235b3d24209aefd59eb7d1c3322"), eq("/var/share/test_old.jpg"), - eq(IRREMOVABLE)); - } - @Test @DisplayName("测试保存应用配置") void testSaveConfig() { String appId = "45698235b3d24209aefd59eb7d1c3322"; - AppBuilderSaveConfigDto appBuilderSaveConfigDto = AppBuilderSaveConfigDto.builder() - .input(Collections.singletonList(AppBuilderConfigFormPropertyDto.builder().build())) - .graph("{\"graph\":\"abc\"}") - .build(); - Mockito.when(this.appRepository.selectWithId(appId)).thenReturn(mockApp()); - doNothing().when(this.formPropertyRepository).updateMany(any()); - doNothing().when(this.flowGraphRepository).updateOne(any()); - Rsp res = this.appBuilderAppService.saveConfig(appId, appBuilderSaveConfigDto, - new OperationContext()); - Assertions.assertEquals(res.getData().getId(), "45698235b3d24209aefd59eb7d1c3322"); - } + AppBuilderSaveConfigDto appBuilderSaveConfigDto = new AppBuilderSaveConfigDto(); + OperationContext context = new OperationContext(); + AppVersion appVersion = mock(AppVersion.class); + when(appVersionService.update(anyString(), any(AppBuilderSaveConfigDto.class), + any(OperationContext.class))).thenReturn(appVersion); - @Test - @DisplayName("测试应用导出") - void testExportAppConfig() throws NoSuchMethodException { - AppBuilderApp mockApp = AppBuilderApp.builder() - .id("123456") - .configId("123456") - .flowGraphId("123456") - .name("testApp") - .tenantId("tenant123") - .type("testType") - .version("1.0.0") - .createBy("admin") - .attributes(new HashMap<>()) - .formPropertyRepository(this.formPropertyRepository) - .build(); - List mockConfigProperties = Collections.singletonList( - AppBuilderConfigProperty.builder().id("123").nodeId("456").formPropertyId("789").build()); - List mockFormProperties = Collections.singletonList(AppBuilderFormProperty.builder() - .id("789") - .formId("369") - .name("test") - .dataType("String") - .defaultValue("test") - .group("null") - .build()); - AppBuilderForm mockForm = mock(AppBuilderForm.class); - AppBuilderConfig mockConfig = AppBuilderConfig.builder() - .id("258") - .form(mockForm) - .configProperties(mockConfigProperties) - .app(mockApp) - .build(); - AppBuilderFlowGraph mockFlowGraph = AppBuilderFlowGraph.builder() - .id("123") - .name("testFlowGraph") - .appearance("{\"id\": \"testGraphId\"}") - .build(); - when(this.appRepository.selectWithId(anyString())).thenReturn(mockApp); - when(this.configRepository.selectWithId(anyString())).thenReturn(mockConfig); - when(this.flowGraphRepository.selectWithId(anyString())).thenReturn(mockFlowGraph); - when(this.formPropertyRepository.selectWithAppId(anyString())).thenReturn(mockFormProperties); - when(this.appTypeService.query(any(), any())).thenReturn(AppTypeDto.builder().name("单元测试").build()); - when(this.aippFlowDefinitionService.getParsedGraphData(any(), any())).thenReturn("testFlowDefinition"); - doNothing().when(this.flowDefinitionService).validateDefinitionData(any()); - - OperationContext operationContext = new OperationContext(); - operationContext.setName("admin"); - AppExportDto exportConfig = this.appBuilderAppService.export("123", operationContext); - - assertThat(exportConfig.getApp()).extracting(AppExportApp::getName, AppExportApp::getVersion, - AppExportApp::getAppType).containsExactly("testApp", "1.0.0", "单元测试"); - assertThat(exportConfig.getConfig().getConfigProperties()).hasSize(1) - .map(AppExportConfigProperty::getFormProperty) - .element(0) - .extracting(AppExportFormProperty::getName, AppExportFormProperty::getDataType, - AppExportFormProperty::getDefaultValue) - .containsExactly("test", "String", "\"test\""); - assertThat(exportConfig.getFlowGraph()).extracting(AppExportFlowGraph::getName, - AppExportFlowGraph::getAppearance).containsExactly("testFlowGraph", "{\"id\": \"testGraphId\"}"); + this.appBuilderAppService.saveConfig(appId, appBuilderSaveConfigDto, context); + + verify(appVersionService, times(1)).update(appId, appBuilderSaveConfigDto, context); } @Test @@ -921,45 +290,74 @@ void testModelNode() { AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); try (MockedStatic mockedStatic = mockStatic(CheckerFactory.class)) { mockedStatic.when(() -> CheckerFactory.getChecker(anyString())).thenReturn(new RetrievalNodeChecker()); - List results = this.appBuilderAppService.checkAvailable( - Collections.singletonList(appCheckDto), null); + List results = + this.appBuilderAppService.checkAvailable(Collections.singletonList(appCheckDto), null); Assertions.assertFalse(results.get(0).isValid()); Assertions.assertEquals(results.get(0).getConfigChecks().size(), 2); } } @Test - @DisplayName("测试应用导入") - void testImportAppConfig() throws IOException { - when(this.aippFlowService.previewAipp(anyString(), any(), any())).thenReturn( - AippCreateDto.builder().aippId("123456").build()); - ClassLoader classLoader = AppBuilderAppServiceImplTest.class.getClassLoader(); - String config = IoUtils.content(classLoader, IMPORT_CONFIG); + @DisplayName("测试获取应用列表") + void testListApplication() { + String tenantId = "tenantId"; + long offset = 0L; + int limit = 10; + AppQueryCondition condition = new AppQueryCondition(); + + AppBuilderAppPo appPo = new AppBuilderAppPo(); + RangedResultSet mockResultSet = RangedResultSet.create( + Collections.singletonList(mockAppVersion(appPo)), 0, 10, 1L); + when(appVersionService.pageListByTenantId(condition, tenantId, offset, limit)).thenReturn(mockResultSet); + when(converterFactory.convert(any(), any())).thenReturn(AppBuilderAppMetadataDto.builder().build()); + OperationContext context = new OperationContext(); - context.setTenantId("123"); - context.setOperator("admin"); - when(this.appTypeService.queryAll(any())).thenReturn( - Collections.singletonList(AppTypeDto.builder().name("other").build())); - when(this.appTypeService.add(any(), any())).thenReturn(AppTypeDto.builder().id("1234556").build()); - AppBuilderAppDto appDto = this.appBuilderAppService.importApp(config, context); - - assertThat(appDto).extracting(AppBuilderAppDto::getVersion, AppBuilderAppDto::getCreateBy, - AppBuilderAppDto::getState, AppBuilderAppDto::getName, AppBuilderAppDto::getAippId, - AppBuilderAppDto::getAppType) - .containsExactly("1.0.0", "admin", "importing", "fyz01", "123456", "1234556"); + context.setTenantId(tenantId); + RangedResultSet resultSet = + this.appBuilderAppService.list(condition, context, offset, limit).getData(); + + assertEquals(1, resultSet.getResults().size()); + assertEquals(offset, resultSet.getRange().getOffset()); + assertEquals(limit, resultSet.getRange().getLimit()); + assertEquals(1, resultSet.getRange().getTotal()); + verify(appVersionService, times(1)).pageListByTenantId(condition, tenantId, offset, limit); } - @Test - @DisplayName("测试根据应用种类获取应用列表") - void testListApplication() { - AppBuilderApp app = this.mockApp(); - when(this.appRepository.selectWithLatestApp(any(), any(), anyLong(), anyInt())).thenReturn( - Collections.singletonList(app)); - AppQueryCondition cond = new AppQueryCondition(); - cond.setAppCategory("chatbot"); - RangedResultSet metaData = this.appBuilderAppService.list(cond, new OperationContext(), 0L, - 10).getData(); - AppBuilderAppMetadataDto dto = metaData.getResults().get(0); - assertThat(dto).extracting(AppBuilderAppMetadataDto::getAppCategory).isEqualTo("chatbot"); + /** + * mock 应用版本对象. + * + * @param appPo 数据对象. + * @return {@link AppVersion} 应用版本对象. + */ + public static AppVersion mockAppVersion(AppBuilderAppPo appPo) { + AppVersionFactory appVersionFactory = new AppVersionFactory(null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + 20000, + 300, + null, + "/var/share"); + if (StringUtils.isBlank(appPo.getConfigId())) { + appPo.setConfigId("defaultConfigId"); + } + return appVersionFactory.create(appPo, null); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderFormServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderFormServiceImplTest.java index f11927ad61..030f26b0e1 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderFormServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppBuilderFormServiceImplTest.java @@ -127,7 +127,8 @@ void testCreateSmartFormFailWhenWithoutiframeUrl() { appearance.put("fileName", "form.zip"); appearance.put("schema", new HashMap<>()); AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder().name("test").appearance(appearance).build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002131, exception.getCode()); @@ -143,7 +144,8 @@ void testCreateSmartFormFailWhenWithoutSchema() { appearance.put("fileUuid", "456"); appearance.put("fileName", "form.zip"); AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder().name("test").appearance(appearance).build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002131, exception.getCode()); @@ -159,7 +161,8 @@ void testCreateSmartFormFailWhenWithoutfileUuid() { appearance.put("schema", new HashMap<>()); appearance.put("fileName", "form.zip"); AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder().name("test").appearance(appearance).build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002131, exception.getCode()); @@ -175,7 +178,8 @@ void testCreateSmartFormFailWhenWithoutfileName() { appearance.put("schema", new HashMap<>()); appearance.put("fileUuid", "456"); AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder().name("test").appearance(appearance).build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002131, exception.getCode()); @@ -184,13 +188,11 @@ void testCreateSmartFormFailWhenWithoutfileName() { @Test @DisplayName("表单数量已经达到上限,创建表单失败") void testCreateSmartFormFailWhenNumUpToMaximum() { - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("test") - .appearance(new HashMap<>()) - .build(); + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("test").appearance(new HashMap<>()).build(); when(this.appBuilderFormRepository.countWithType(anyString(), any())).thenReturn(400L); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002134, exception.getCode()); @@ -199,13 +201,11 @@ void testCreateSmartFormFailWhenNumUpToMaximum() { @Test @DisplayName("表单名称重复,创建表单失败") void testCreateSmartFormFailWhenNameIsExisted() { - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("test") - .appearance(new HashMap<>()) - .build(); + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("test").appearance(new HashMap<>()).build(); when(this.appBuilderFormRepository.selectWithName(anyString(), any())).thenReturn(form); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002135, exception.getCode()); @@ -214,12 +214,10 @@ void testCreateSmartFormFailWhenNameIsExisted() { @Test @DisplayName("表单名称不符合规范,创建表单失败") void testCreateSmartFormFailWhenNameNotUpToFormat() { - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("123***haha") - .appearance(new HashMap<>()) - .build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("123***haha").appearance(new HashMap<>()).build(); + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002917, exception.getCode()); @@ -234,7 +232,8 @@ void testCreateSmartFormFailWhenNameLengthOutOfBound() { + "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest") .appearance(new HashMap<>()) .build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002129, exception.getCode()); @@ -253,12 +252,10 @@ void testCreateSmartFormFailWhenDescriptionLengthOutOfBound() { + "description.test form description.test form description.test form description.test form " + "description.test form description.test form description.test form description.test form " + "description."); - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("test") - .appearance(appearance) - .build(); - AippException exception = Assertions.assertThrows(AippException.class, () -> { + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("test").appearance(appearance).build(); + AippException exception = Assertions.assertThrows(AippException.class, + () -> { this.appBuilderFormService.create(appBuilderFormDto, new OperationContext()); }); Assertions.assertEquals(90002130, exception.getCode()); @@ -274,15 +271,12 @@ void testUpdateSmartFormSuccess() { appearance.put("fileUuid", "456"); appearance.put("fileName", "form.zip"); appearance.put("schema", new HashMap<>()); - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("test") - .appearance(appearance) - .build(); + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("test").appearance(appearance).build(); doNothing().when(this.appBuilderFormRepository).updateOne(any(AppBuilderForm.class)); when(this.appBuilderFormRepository.selectWithId(any())).thenReturn(form); - AppBuilderFormDto result = this.appBuilderFormService.update(appBuilderFormDto, appBuilderFormDto.getId(), - new OperationContext()); + AppBuilderFormDto result = + this.appBuilderFormService.update(appBuilderFormDto, appBuilderFormDto.getId(), new OperationContext()); verify(this.appBuilderFormRepository, times(1)).updateOne(any(AppBuilderForm.class)); Assertions.assertEquals(result.getName(), "test"); Assertions.assertEquals(result.getAppearance().get("imgUrl"), "/var/share/123/form.png"); @@ -296,11 +290,8 @@ void testUpdateSmartFormSuccess() { @Test @DisplayName("表单id有误,更新表单失败") void testUpdateSmartFormFailWhenFormIdIsError() { - AppBuilderFormDto appBuilderFormDto = AppBuilderFormDto.builder() - .id("formId") - .name("test") - .appearance(new HashMap<>()) - .build(); + AppBuilderFormDto appBuilderFormDto = + AppBuilderFormDto.builder().id("formId").name("test").appearance(new HashMap<>()).build(); when(this.appBuilderFormRepository.selectWithId(any())).thenReturn(null); AippException exception = Assertions.assertThrows(AippException.class, () -> { this.appBuilderFormService.update(appBuilderFormDto, appBuilderFormDto.getId(), new OperationContext()); @@ -311,20 +302,15 @@ void testUpdateSmartFormFailWhenFormIdIsError() { @Test @DisplayName("查询表单成功") void testQuerySmartFormSuccess() { - AppBuilderForm appBuilderForm1 = AppBuilderForm.builder() - .id("formId1") - .name("test1") - .appearance(new HashMap<>()) - .build(); - AppBuilderForm appBuilderForm2 = AppBuilderForm.builder() - .id("formId2") - .name("test2") - .appearance(new HashMap<>()) - .build(); - when(this.appBuilderFormRepository.selectWithCondition(any(FormQueryCondition.class))).thenReturn( - Arrays.asList(appBuilderForm1, appBuilderForm2)); - RangedResultSet result = this.appBuilderFormService.query(0L, 10, null, - new OperationContext()); + AppBuilderForm appBuilderForm1 = + AppBuilderForm.builder().id("formId1").name("test1").appearance(new HashMap<>()).build(); + AppBuilderForm appBuilderForm2 = + AppBuilderForm.builder().id("formId2").name("test2").appearance(new HashMap<>()).build(); + when(this.appBuilderFormRepository.selectWithCondition(any(FormQueryCondition.class))).thenReturn(Arrays.asList( + appBuilderForm1, + appBuilderForm2)); + RangedResultSet result = + this.appBuilderFormService.query(0L, 10, null, new OperationContext()); List forms = result.getResults(); Assertions.assertEquals(forms.size(), 2); Assertions.assertEquals(forms.get(0).getId(), "formId1"); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatServiceImplTest.java index feb020a176..2e7fcdbad6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatServiceImplTest.java @@ -6,54 +6,34 @@ package modelengine.fit.jober.aipp.service; -import static modelengine.fit.jober.aipp.enums.AppTypeEnum.APP; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.INPUT_PARAM_IS_INVALID; +import static modelengine.fit.jober.aipp.common.exception.AippErrCode.TASK_NOT_FOUND; +import static modelengine.fit.jober.aipp.constants.AippConst.BS_AIPP_QUESTION_KEY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import modelengine.fit.jade.waterflow.FlowsService; -import modelengine.fit.jade.waterflow.dto.FlowInfo; -import modelengine.fit.jade.waterflow.entity.FlowNodeInfo; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.meta.multiversion.MetaService; -import modelengine.fit.jane.meta.multiversion.definition.Meta; -import modelengine.fit.jane.meta.multiversion.definition.MetaFilter; import modelengine.fit.jober.aipp.common.exception.AippException; import modelengine.fit.jober.aipp.common.exception.AippParamException; -import modelengine.fit.jober.aipp.constants.AippConst; -import modelengine.fit.jober.aipp.domain.AppBuilderApp; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; +import modelengine.fit.jober.aipp.domains.taskinstance.service.AppTaskInstanceService; import modelengine.fit.jober.aipp.dto.chat.CreateAppChatRequest; -import modelengine.fit.jober.aipp.dto.chat.QueryChatRsp; -import modelengine.fit.jober.aipp.entity.AippInstLog; -import modelengine.fit.jober.aipp.enums.AppState; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; -import modelengine.fit.jober.aipp.genericable.AppBuilderAppService; -import modelengine.fit.jober.aipp.mapper.AippChatMapper; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; import modelengine.fit.jober.aipp.service.impl.AppChatServiceImpl; import modelengine.fit.jober.aipp.util.CacheUtils; -import modelengine.fit.jober.aipp.util.JsonUtils; -import modelengine.fit.jober.common.RangeResult; -import modelengine.fit.jober.common.RangedResultSet; import modelengine.fitframework.flowable.Choir; -import modelengine.fitframework.model.Tuple; -import modelengine.fitframework.util.StringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -68,41 +48,14 @@ public class AppChatServiceImplTest { private AppChatService appChatService; @Mock - private AppBuilderAppFactory appFactory; + private AppVersionService appVersionService; @Mock - private AippChatMapper aippChatMapper; - - @Mock - private AippRunTimeService aippRunTimeService; - - @Mock - private AppBuilderAppService appService; - - @Mock - private AippLogService aippLogService; - - @Mock - private AppBuilderAppRepository appRepository; - - @Mock - private MetaService metaService; - - @Mock - private FlowsService flowsService; + private AppTaskInstanceService appTaskInstanceService; @BeforeEach void before() { - this.appChatService = new AppChatServiceImpl(this.appFactory, - this.aippChatMapper, - this.aippRunTimeService, - this.appService, - this.aippLogService, - this.appRepository, - this.metaService, - this.flowsService, - null, - null); + this.appChatService = new AppChatServiceImpl(this.appVersionService); CacheUtils.clear(); } @@ -115,75 +68,27 @@ void testChat() { context.put("user_1", true); context.put("user_2", atChatAppId); CreateAppChatRequest hello = CreateAppChatRequest.builder() - .appId(chatAppId) - .question("你好") - .chatId("hello") - .context(CreateAppChatRequest.Context.builder() - .useMemory(true) - .atAppId(atChatAppId) - .userContext(context) - .build()) + .appId(chatAppId).question("你好").chatId("hello") + .context(CreateAppChatRequest.Context.builder().useMemory(true).atAppId(atChatAppId) + .userContext(context).build()) .build(); OperationContext operationContext = new OperationContext(); + when(this.appVersionService.run(any(CreateAppChatRequest.class), any(OperationContext.class))).thenReturn( + mock(Choir.class)); - Choir t2 = Choir.create((e) -> {}); - Mockito.when(this.aippRunTimeService.createInstanceByApp(Mockito.eq(atChatAppId), - Mockito.eq("你好"), - Mockito.anyMap(), - Mockito.any(), - Mockito.eq(false))).thenReturn(Tuple.duet("hello_inst", t2)); - Mockito.when(this.appFactory.create(Mockito.any())) - .thenReturn(AppBuilderApp.builder().id("id1").version("10.0.0").state("ACTIVE").build()); - Mockito.when(this.appRepository.selectWithId(Mockito.any())) - .thenReturn(AppBuilderApp.builder().id("id1").version("10.0.0").state("ACTIVE").build()); - Mockito.when(this.metaService.list(Mockito.any(MetaFilter.class), - Mockito.eq(false), - Mockito.eq(0L), - Mockito.eq(10), - Mockito.any(OperationContext.class))) - .thenReturn(new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 10, 1))); - Mockito.when(this.metaService.list(Mockito.any(MetaFilter.class), - Mockito.eq(true), - Mockito.eq(0L), - Mockito.eq(1), - Mockito.any(OperationContext.class))) - .thenReturn(new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 10, 1))); - Mockito.when(this.flowsService.getFlows(Mockito.any(), Mockito.any())) - .thenReturn(mockFlowInfo(new ArrayList<>(), false)); + // when. Choir objectChoir = Assertions.assertDoesNotThrow(() -> this.appChatService.chat(hello, operationContext, false)); - Assertions.assertEquals(t2, objectChoir); + + // then. + Assertions.assertNotNull(objectChoir); } @Test @DisplayName("测试重新对话") void testRestartChat() { - Mockito.when(this.aippLogService.getParentPath(Mockito.any())).thenReturn("/instanceId"); - List chatIds = Arrays.asList("chatId1", "chatId2"); - Mockito.when(this.aippChatMapper.selectChatIdByInstanceId(Mockito.eq("instanceId"))).thenReturn(chatIds); - Mockito.when(this.aippChatMapper.selectChatListByChatIds(chatIds)).thenReturn(this.mockChatList()); - Mockito.when(this.aippLogService.queryLogsByInstanceIdAndLogTypes(Mockito.anyString(), Mockito.anyList())) - .thenReturn(this.mockLog()); - Mockito.when(this.aippChatMapper.selectChatList(Mockito.any(), Mockito.anyString(), Mockito.any())) - .thenReturn(this.mockChatList()); - Mockito.when(this.aippRunTimeService.createInstanceByApp(Mockito.any(), - Mockito.any(), - Mockito.anyMap(), - Mockito.any(), - Mockito.eq(true))).thenReturn(Tuple.duet("hello_inst", Choir.create((e) -> {}))); - Mockito.when(this.appFactory.create(Mockito.any())) - .thenReturn(AppBuilderApp.builder().id("id1").version("10.0.0").state("ACTIVE").build()); - Mockito.when(this.appRepository.selectWithId(Mockito.any())) - .thenReturn(AppBuilderApp.builder().id("id1").version("10.0.0").state("ACTIVE").build()); - Mockito.when(this.metaService.list(Mockito.any(MetaFilter.class), - Mockito.eq(false), - Mockito.eq(0L), - Mockito.eq(10), - Mockito.any(OperationContext.class))) - .thenReturn(new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 10, 1))); - Mockito.lenient() - .when(this.flowsService.getFlows(Mockito.any(), Mockito.any())) - .thenReturn(mockFlowInfo(new ArrayList<>(), false)); + // when. + // then. Assertions.assertDoesNotThrow(() -> this.appChatService.restartChat("1", new HashMap<>(), new OperationContext())); @@ -192,12 +97,13 @@ void testRestartChat() { @Test @DisplayName("测试重新对话:没找到对应的对话") void testRestartChatFailedNoChat() { - Mockito.when(this.aippLogService.getParentPath(Mockito.any())).thenReturn("/instanceId"); - List chatIds = Arrays.asList("chatId1", "chatId2"); - Mockito.when(this.aippChatMapper.selectChatIdByInstanceId(Mockito.eq("instanceId"))).thenReturn(chatIds); + Map context = new HashMap<>(); + context.put("user_1", true); + context.put("user_2", "nofind"); + doThrow(new AippException(TASK_NOT_FOUND, "1234")).when(appVersionService).restart(any(), any(), any()); AippException exception = Assertions.assertThrows(AippException.class, - () -> this.appChatService.restartChat("1", new HashMap<>(), new OperationContext())); - Assertions.assertEquals(90002939, exception.getCode()); + () -> this.appChatService.restartChat("1", context, new OperationContext())); + Assertions.assertEquals(90002909, exception.getCode()); } @Test @@ -210,30 +116,16 @@ void testChatWithInvalidQuestion() { context.put("user_1", true); context.put("user_2", atChatAppId); OperationContext operationContext = new OperationContext(); - Mockito.when(this.appFactory.create(Mockito.any())) - .thenReturn(AppBuilderApp.builder() - .id("id1") - .version("10.0.0") - .state("ACTIVE") - .type(APP.code()) - .build()); - Mockito.when(this.appRepository.selectWithId(Mockito.any())) - .thenReturn(AppBuilderApp.builder() - .id("id1") - .version("10.0.0") - .state("ACTIVE") - .type(APP.code()) - .build()); CreateAppChatRequest hello = CreateAppChatRequest.builder() - .appId(chatAppId) - .chatId("hello") - .context(CreateAppChatRequest.Context.builder() - .useMemory(true) - .atAppId(atChatAppId) - .userContext(context) - .build()) - .build(); + .appId(chatAppId) + .chatId("hello") + .context(CreateAppChatRequest.Context.builder() + .useMemory(true) + .atAppId(atChatAppId) + .userContext(context) + .build()) + .build(); // question的长度在1-20000之间,在应用的场景下为必填 testInvalidQuestion(hello, "", operationContext); @@ -244,379 +136,10 @@ void testChatWithInvalidQuestion() { private void testInvalidQuestion(CreateAppChatRequest hello, String question, OperationContext operationContext) { hello.setQuestion(question); + doThrow(new AippParamException(INPUT_PARAM_IS_INVALID, BS_AIPP_QUESTION_KEY)).when(appVersionService) + .run(any(), any()); AippParamException exception = Assertions.assertThrows(AippParamException.class, - () -> this.appChatService.chat(hello, operationContext, false)); + () -> this.appChatService.chat(hello, operationContext, false)); Assertions.assertEquals(exception.getMessage(), "非法参数: Question。"); } - - @Nested - @DisplayName("测试自定义参数校验") - class TestAddUserContext { - private AppChatService chatService; - - @Mock - private MetaService metaService1; - - @Mock - private FlowsService flowsService1; - - @BeforeEach - void setup() { - this.chatService = - new AppChatServiceImpl(null, null, null, null, null, null, metaService1, flowsService1, null, null); - Mockito.when(metaService1.list(Mockito.any(MetaFilter.class), - Mockito.anyBoolean(), - Mockito.eq(0L), - Mockito.anyInt(), - Mockito.any(OperationContext.class))).thenAnswer((invocation) -> { - boolean argument = invocation.getArgument(1); - if (argument) { - return new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 1, 1)); - } else { - return new RangedResultSet<>(Collections.singletonList(mockMeta()), new RangeResult(0, 10, 1)); - } - }); - CacheUtils.clear(); - } - - @Test - @DisplayName("自定义参数为非必填,userContext中该参数的值为null,校验不报错") - public void testNotRequiredParamWithNull() throws NoSuchMethodException { - Map input = new HashMap<>(); - input.put("isRequired", false); - input.put("name", "input1"); - input.put("type", "String"); - setInputParams(new ArrayList<>(Collections.singletonList(input)), false); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - CreateAppChatRequest request = new CreateAppChatRequest(); - getCreateAppChatRequest(request, "input1", null); - request.setAppId("APPID"); - Assertions.assertDoesNotThrow(() -> method.invoke(this.chatService, - request, - new HashMap<>(), - false, - new OperationContext(), - "workflow")); - } - - @Test - @DisplayName("参数中有必填字段,userContext为null,校验报错") - public void testRequiredParamWithoutUserContext() throws NoSuchMethodException { - Map input = new HashMap<>(); - input.put("isRequired", true); - input.put("name", "input1"); - input.put("type", "String"); - setInputParams(new ArrayList<>(Collections.singletonList(input)), true); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - CreateAppChatRequest request = new CreateAppChatRequest(); - CreateAppChatRequest.Context context = new CreateAppChatRequest.Context(); - Map userContext = new HashMap<>(); - context.setUserContext(userContext); - request.setContext(context); - request.setAppId("APPID"); - failureSituation(request, "user context", "app"); - } - - @Test - @DisplayName("应用自定义参数中没有必填字段,userContext为null,校验不报错") - public void testAppNonRequiredParamWithoutUserContext() throws NoSuchMethodException { - Map input = new HashMap<>(); - input.put("isRequired", false); - input.put("name", "input1"); - input.put("type", "String"); - setInputParams(new ArrayList<>(Collections.singletonList(input)), true); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - CreateAppChatRequest request = new CreateAppChatRequest(); - CreateAppChatRequest.Context context = new CreateAppChatRequest.Context(); - Map userContext = new HashMap<>(); - context.setUserContext(userContext); - request.setContext(context); - request.setAppId("APPID"); - Assertions.assertDoesNotThrow(() -> method.invoke(this.chatService, - request, - new HashMap<>(), - false, - new OperationContext(), - "app")); - } - - @Test - @DisplayName("工作流自定义参数中没有必填字段,userContext为null,校验不报错") - public void testWorkflowNonRequiredParamWithoutUserContext() throws NoSuchMethodException { - Map input = new HashMap<>(); - input.put("isRequired", false); - input.put("name", "input1"); - input.put("type", "String"); - setInputParams(new ArrayList<>(Collections.singletonList(input)), false); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - CreateAppChatRequest request = new CreateAppChatRequest(); - CreateAppChatRequest.Context context = new CreateAppChatRequest.Context(); - Map userContext = new HashMap<>(); - context.setUserContext(userContext); - request.setContext(context); - request.setAppId("APPID"); - Assertions.assertDoesNotThrow(() -> method.invoke(this.chatService, - request, - new HashMap<>(), - false, - new OperationContext(), - "workflow")); - } - - @Test - @DisplayName("参数中有必填字段,userContext中没有传该字段,校验报错") - public void testRequiredParamNotExistInUserContext() throws NoSuchMethodException { - Map input = new HashMap<>(); - input.put("isRequired", true); - input.put("name", "input1"); - input.put("type", "String"); - setInputParams(new ArrayList<>(Collections.singletonList(input)), false); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - CreateAppChatRequest request = new CreateAppChatRequest(); - getCreateAppChatRequest(request, "not_input1", "1234"); - request.setAppId("APPID"); - failureSituation(request, "input1", "workflow"); - } - - @Test - @DisplayName("组合场景,校验通过") - public void testValidCombinationSituation() throws NoSuchMethodException { - List names = Arrays.asList("not_required_input", - "string_input", - "string_input_2", - "integer_input", - "integer_input_2", - "boolean_input", - "boolean_input_2", - "number_input", - "number_input_2", - "number_input_3"); - List types = Arrays.asList("String", - "String", - "String", - "Integer", - "Integer", - "Boolean", - "Boolean", - "Number", - "Number", - "Number"); - List requiredStates = - Arrays.asList(false, true, false, true, false, true, false, true, false, true); - setInputParams(createInputList(names, types, requiredStates), true); - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - String testInput = java.util.stream.Stream.generate(() -> "A").limit(500).collect(Collectors.joining()); - CreateAppChatRequest request = new CreateAppChatRequest(); - getCreateAppChatRequest(request, "string_input", "你"); - getCreateAppChatRequest(request, "string_input", testInput); - request.getContext().getUserContext().put("integer_input", -999999999); - request.getContext().getUserContext().put("integer_input_2", 999999999); - request.getContext().getUserContext().put("boolean_input", true); - request.getContext().getUserContext().put("boolean_input_2", false); - request.getContext().getUserContext().put("number_input", -999999999.99); - request.getContext().getUserContext().put("number_input_2", 999999999.99); - request.getContext().getUserContext().put("number_input_3", 1.1); - request.setAppId("APPID"); - Assertions.assertDoesNotThrow(() -> method.invoke(this.chatService, - request, - new HashMap<>(), - false, - new OperationContext(), - "app")); - } - - @Test - @DisplayName("字符串类型自定义参数输入不合法,校验失败") - public void testStringInputWithInvalidInput() throws NoSuchMethodException { - // 字符串长度为1~500 - String testInput = java.util.stream.Stream.generate(() -> "A").limit(501).collect(Collectors.joining()); - testInvalidInputParam("String", testInput); - testInvalidInputParam("String", ""); - testInvalidInputParam("String", 123); - testInvalidInputParam("String", true); - } - - @Test - @DisplayName("布尔类型自定义参数输入不合法,校验失败") - public void testBooleanInputWithInvalidInput() throws NoSuchMethodException { - testInvalidInputParam("Boolean", "true"); - testInvalidInputParam("Boolean", 123); - } - - @Test - @DisplayName("整型自定义参数输入不合法,校验失败") - public void testIntegerInputWithInvalidInput() throws NoSuchMethodException { - // 范围为-999999999~999999999 - testInvalidInputParam("Integer", "123"); - testInvalidInputParam("Integer", 1000000000); - testInvalidInputParam("Integer", -1000000000); - testInvalidInputParam("Integer", -999999999.1); - testInvalidInputParam("Integer", false); - } - - @Test - @DisplayName("数字类型自定义参数输入不合法,校验失败") - public void testNumberInputWithInvalidInput() throws NoSuchMethodException { - // 范围为-999999999.99~999999999.99,两位小数 - testInvalidInputParam("Number", -1000000000); - testInvalidInputParam("Number", 1000000000); - testInvalidInputParam("Number", "hi"); - testInvalidInputParam("Number", 1.999); - testInvalidInputParam("Number", true); - } - - private void testInvalidInputParam(String paramType, Object inputValue) throws NoSuchMethodException { - CacheUtils.clear(); - Map input = new HashMap<>(); - String paramName = paramType + "_input"; - input.put("isRequired", true); - input.put("name", paramName); - input.put("type", paramType); - setInputParams(new ArrayList<>(Collections.singletonList(input)), true); - CreateAppChatRequest request = new CreateAppChatRequest(); - request.setAppId("APPID"); - getCreateAppChatRequest(request, paramName, inputValue); - failureSituation(request, paramName, "app"); - } - - private void failureSituation(CreateAppChatRequest request, String invalidInput, String appType) - throws NoSuchMethodException { - Method method = AppChatServiceImpl.class.getDeclaredMethod("addUserContext", - CreateAppChatRequest.class, - Map.class, - boolean.class, - OperationContext.class, - String.class); - method.setAccessible(true); - Exception exception = Assertions.assertThrows(Exception.class, () -> { - try { - method.invoke(this.chatService, request, new HashMap<>(), false, new OperationContext(), appType); - } catch (InvocationTargetException e) { - throw e.getCause(); - } - }); - Assertions.assertInstanceOf(AippParamException.class, exception); - Assertions.assertEquals(StringUtils.format("非法参数: {0}。", invalidInput), exception.getMessage()); - } - - private void getCreateAppChatRequest(CreateAppChatRequest request, String paramName, Object value) { - CreateAppChatRequest.Context context = new CreateAppChatRequest.Context(); - Map userContext = new HashMap<>(); - userContext.put(paramName, value); - context.setUserContext(userContext); - request.setContext(context); - } - - private void setInputParams(List> inputs, boolean isApp) { - Mockito.lenient() - .when(this.flowsService1.getFlows(Mockito.any(), Mockito.any())) - .thenReturn(mockFlowInfo(inputs, isApp)); - } - - private List> createInputList(List names, List types, - List requiredStates) { - List> inputList = new ArrayList<>(); - for (int i = 0; i < names.size(); i++) { - Map inputMap = new HashMap<>(); - inputMap.put("isRequired", requiredStates.get(i)); - inputMap.put("name", names.get(i)); - inputMap.put("type", types.get(i)); - inputList.add(inputMap); - } - return inputList; - } - } - - private List mockChatList() { - Map attributesOrigin = new HashMap<>(); - attributesOrigin.put(AippConst.ATTR_CHAT_INST_ID_KEY, "instanceId"); - attributesOrigin.put(AippConst.ATTR_CHAT_STATE_KEY, AppState.INACTIVE.getName()); - QueryChatRsp chat1 = QueryChatRsp.builder() - .chatId("chatId1") - .chatName("1+1") - .attributes(JsonUtils.toJsonString(attributesOrigin)) - .build(); - Map other = new HashMap<>(); - other.put(AippConst.ATTR_CHAT_INST_ID_KEY, "instIdOther"); - other.put(AippConst.ATTR_CHAT_STATE_KEY, AppState.PUBLISHED.getName()); - other.put(AippConst.ATTR_CHAT_ORIGIN_APP_KEY, "originAppId"); - other.put(AippConst.ATTR_CHAT_ORIGIN_APP_VERSION_KEY, "1.0.0"); - QueryChatRsp chat2 = QueryChatRsp.builder() - .chatId("chatId2") - .chatName("1+1") - .attributes(JsonUtils.toJsonString(other)) - .build(); - return new ArrayList<>(Arrays.asList(chat1, chat2)); - } - - private List mockLog() { - return new ArrayList<>(Collections.singletonList(AippInstLog.builder().logData("{\"msg\":\"hello\"}").build())); - } - - private Meta mockMeta() { - Meta meta = new Meta(); - meta.setId("metaId"); - Map attributes = new HashMap<>(); - attributes.put(AippConst.ATTR_FLOW_DEF_ID_KEY, "flow_definition_id"); - meta.setAttributes(attributes); - return meta; - } - - private FlowInfo mockFlowInfo(List> inputs, boolean isApp) { - FlowInfo flowInfo = new FlowInfo(); - FlowNodeInfo startNode = new FlowNodeInfo(); - startNode.setType("start"); - List> inputParams = new ArrayList<>(); - Map inputConfig = new HashMap<>(); - inputConfig.put("name", "input"); - if (isApp) { - Map appDefaultInput = new HashMap<>(); - appDefaultInput.put("isRequired", true); - appDefaultInput.put("name", "Question"); - inputs.add(appDefaultInput); - } - inputConfig.put("value", inputs); - inputParams.add(inputConfig); - Map startNodeConfig = new HashMap<>(); - startNodeConfig.put("inputParams", inputParams); - startNode.setProperties(startNodeConfig); - flowInfo.setFlowNodes(Collections.singletonList(startNode)); - return flowInfo; - } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSessionServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSessionServiceImplTest.java index dfd0f38dec..22ba2398f2 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSessionServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSessionServiceImplTest.java @@ -11,6 +11,7 @@ import modelengine.fit.jober.aipp.entity.ChatSession; import modelengine.fit.jober.aipp.mapper.AppChatNumMapper; import modelengine.fit.jober.aipp.service.impl.AppChatSessionServiceImpl; + import modelengine.fitframework.flowable.Emitter; import modelengine.fitframework.flowable.emitter.DefaultEmitter; @@ -43,8 +44,8 @@ void before() { void testAddChatSession() { Emitter e = new DefaultEmitter<>(); this.appChatSessionService.addSession("hello", new ChatSession<>(e, "123", true, Locale.ENGLISH)); - Optional> hello = Assertions.assertDoesNotThrow( - () -> this.appChatSessionService.getSession("hello")); + Optional> hello = + Assertions.assertDoesNotThrow(() -> this.appChatSessionService.getSession("hello")); Assertions.assertTrue(hello.isPresent()); Assertions.assertEquals(e, hello.get().getEmitter()); } @@ -55,8 +56,8 @@ void testRemoveSession() { Emitter e = new DefaultEmitter<>(); this.appChatSessionService.addSession("hello", new ChatSession<>(e, "123", true, Locale.ENGLISH)); this.appChatSessionService.removeSession("hello"); - Optional> hello = Assertions.assertDoesNotThrow( - () -> this.appChatSessionService.getSession("hello")); + Optional> hello = + Assertions.assertDoesNotThrow(() -> this.appChatSessionService.getSession("hello")); Assertions.assertFalse(hello.isPresent()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSseServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSseServiceImplTest.java index 58dca8a989..2953c1c4e4 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSseServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/AppChatSseServiceImplTest.java @@ -9,9 +9,11 @@ import static org.mockito.Mockito.mock; import modelengine.fit.jober.aipp.entity.ChatSession; +import modelengine.fit.jober.aipp.mapper.AippLogMapper; import modelengine.fit.jober.aipp.mapper.AppChatNumMapper; import modelengine.fit.jober.aipp.service.impl.AppChatSessionServiceImpl; import modelengine.fit.jober.aipp.service.impl.AppChatSseServiceImpl; + import modelengine.fitframework.flowable.Emitter; import modelengine.fitframework.flowable.emitter.DefaultEmitter; @@ -41,25 +43,25 @@ public class AppChatSseServiceImplTest { private Emitter emitter; @Mock - private AippLogService logService; + private AippLogMapper aippLogMapper; private AppChatSessionService appChatSessionService; - private final AppChatNumMapper mockMapper = mock(AppChatNumMapper.class); @BeforeEach void before() { this.appChatSessionService = new AppChatSessionServiceImpl(mockMapper); - this.appChatSseService = new AppChatSseServiceImpl(logService, appChatSessionService); + this.appChatSseService = new AppChatSseServiceImpl(aippLogMapper, appChatSessionService); } @Test @DisplayName("测试获取") void testGetEmitter() { Emitter e = new DefaultEmitter<>(); - this.appChatSessionService.addSession("hello", new ChatSession<>(e, "123", true, Locale.ENGLISH)); - Optional> hello = Assertions.assertDoesNotThrow( - () -> this.appChatSseService.getEmitter("hello")); + this.appChatSessionService.addSession("hello", + new ChatSession<>(e, "123", true, Locale.ENGLISH)); + Optional> hello = + Assertions.assertDoesNotThrow(() -> this.appChatSseService.getEmitter("hello")); Assertions.assertTrue(hello.isPresent()); Assertions.assertEquals(e, hello.get().getEmitter()); } @@ -78,7 +80,7 @@ void testSendLastData() { @DisplayName("测试发送最后的消息到祖先") void testSendLastAncestor() { this.appChatSessionService.addSession("hello", new ChatSession<>(emitter, "123", true, Locale.ENGLISH)); - Mockito.when(logService.getParentPath("hello world")).thenReturn("world/hello"); + Mockito.when(aippLogMapper.getParentPath("hello world")).thenReturn("world/hello"); Assertions.assertDoesNotThrow(() -> appChatSseService.sendToAncestorLastData("hello world", "hello")); Mockito.verify(emitter, Mockito.times(1)).emit("hello"); Mockito.verify(emitter, Mockito.times(1)).complete(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/CheckerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/CheckerTest.java index be1a8af14f..d042bbc7e2 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/CheckerTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/CheckerTest.java @@ -9,8 +9,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import modelengine.jade.store.service.PluginToolService; - import modelengine.fit.jade.aipp.model.dto.ModelAccessInfo; import modelengine.fit.jade.aipp.model.dto.ModelListDto; import modelengine.fit.jade.aipp.model.service.AippModelCenter; @@ -20,6 +18,7 @@ import modelengine.fit.jober.aipp.service.impl.RetrievalNodeChecker; import modelengine.fit.jober.aipp.service.impl.ToolInvokeNodeChecker; import modelengine.fit.jober.aipp.util.JsonUtils; +import modelengine.jade.store.service.PluginToolService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -59,13 +58,13 @@ public void before() { @DisplayName("模型、工具流、工具配置存在,检查通过") public void testModelNodeSuccess() { String testNode = - "{\"type\":\"llmNodeState\",\"nodeInfos\":[{\"nodeId\":\"llmnode1\",\"nodeName\":\"大模型1\"," - + "\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77112\"," - + "\"configName\":\"accessInfo\"," + "\"serviceName\":\"Fake Model\",\"tag\":\"INTERNAL\"}," - + "{\"id\":\"383537a2-4371-1744-be05-7b5ab50dec0b\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"2de8e815-187f-4aeb-9363-13e7836f4271\"}," - + "{\"id\":\"06cefe21-e092-a04e-90c6-75e94030e0e2\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"6834efb7-ba3d-f044-a875-4db8be8754b0\"}]}]}"; + "{\"type\":\"llmNodeState\",\"nodeInfos\":[{\"nodeId\":\"llmnode1\",\"nodeName\":\"大模型1\"," + + "\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77112\",\"configName\":\"accessInfo\"," + + "\"serviceName\":\"Fake Model\",\"tag\":\"INTERNAL\"}," + + "{\"id\":\"383537a2-4371-1744-be05-7b5ab50dec0b\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"2de8e815-187f-4aeb-9363-13e7836f4271\"}," + + "{\"id\":\"06cefe21-e092-a04e-90c6-75e94030e0e2\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"6834efb7-ba3d-f044-a875-4db8be8754b0\"}]}]}"; AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); when(pluginToolService.hasPluginTools(any())).thenReturn(Collections.singletonList(true)); ModelAccessInfo modelAccessInfo = new ModelAccessInfo("Fake Model", "INTERNAL", "", ""); @@ -81,20 +80,19 @@ public void testModelNodeSuccess() { @DisplayName("模型、工具流、工具配置不存在,检查结果包含不可用配置") public void testModelNodeWithNonExistsConfigs() { String testNode = - "{\"type\":\"llmNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadewdnjbq\",\"nodeName\":\"大模型\"," - + "\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77111\"," - + "\"configName\":\"accessInfo\"," + "\"serviceName\":\"Fake Model\",\"tag\":\"INTERNAL\"}," - + "{\"id\":\"0afbb780-5b15-e741-a138-754fad3caa15\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"c373a626-f671-6040-8051-808185e9e5b4\"}]},{\"nodeId\":\"llmnode1\"," - + "\"nodeName\":\"大模型1\",\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77112\"," - + "\"configName\":\"accessInfo\",\"serviceName\":\"Another Fake Model\"," - + "\"tag\":\"INTERNAL\"}," - + "{\"id\":\"383537a2-4371-1744-be05-7b5ab50dec0b\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"2de8e815-187f-4aeb-9363-13e7836f4271\"}," - + "{\"id\":\"06cefe21-e092-a04e-90c6-75e94030e0e2\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"6834efb7-ba3d-f044-a875-4db8be8754b0\"}," - + "{\"id\":\"0afbb780-5b15-e741-a138-754fad3caa15\",\"configName\":\"plugin\"," - + "\"uniqueName\":\"c373a626-f671-6040-8051-808185e9e5b4\"}]}]}"; + "{\"type\":\"llmNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadewdnjbq\",\"nodeName\":\"大模型\"," + + "\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77111\",\"configName\":\"accessInfo\"," + + "\"serviceName\":\"Fake Model\",\"tag\":\"INTERNAL\"}," + + "{\"id\":\"0afbb780-5b15-e741-a138-754fad3caa15\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"c373a626-f671-6040-8051-808185e9e5b4\"}]},{\"nodeId\":\"llmnode1\"," + + "\"nodeName\":\"大模型1\",\"configs\":[{\"id\":\"db5fdafa-4cbf-44ba-9cca-8a98f1f77112\"," + + "\"configName\":\"accessInfo\",\"serviceName\":\"Another Fake Model\",\"tag\":\"INTERNAL\"}," + + "{\"id\":\"383537a2-4371-1744-be05-7b5ab50dec0b\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"2de8e815-187f-4aeb-9363-13e7836f4271\"}," + + "{\"id\":\"06cefe21-e092-a04e-90c6-75e94030e0e2\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"6834efb7-ba3d-f044-a875-4db8be8754b0\"}," + + "{\"id\":\"0afbb780-5b15-e741-a138-754fad3caa15\",\"configName\":\"plugin\"," + + "\"uniqueName\":\"c373a626-f671-6040-8051-808185e9e5b4\"}]}]}"; AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); when(pluginToolService.hasPluginTools(any())).thenReturn(Arrays.asList(false, false, false)); ModelAccessInfo modelAccessInfo = new ModelAccessInfo("Fake Model", "EXTERNAL", "", ""); @@ -123,14 +121,13 @@ public void before() { @DisplayName("不进行校验,返回所有配置不合法") public void testRetrievalNode() { String testNode = "{\"type\":\"knowledgeRetrievalNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadenthrcv\"," - + "\"nodeName\":\"知识检索\",\"configs\":[{\"id\":14,\"configName\":\"knowledgeRepos\"," - + "\"name\":\"k14\",\"description\":\"\",\"type\":\"VECTOR\",\"createdAt\":\"2024-12-02 12:41:14\"," - + "\"checked\":true},{\"id\":2,\"configName\":\"knowledgeRepos\",\"name\":\"k2\"," - + "\"description\":\"\"," - + "\"type\":\"VECTOR\",\"createdAt\":\"2024-12-02 12:37:17\",\"checked\":true}]}," - + "{\"nodeId\":\"jadenthrc1\",\"nodeName\":\"知识检索1\",\"configs\":[{\"id\":11," - + "\"configName\":\"knowledgeRepos\",\"name\":\"k14\",\"description\":\"\",\"type\":\"VECTOR\"," - + "\"createdAt\":\"2024-12-02 12:41:14\",\"checked\":true}]}]}"; + + "\"nodeName\":\"知识检索\",\"configs\":[{\"id\":14,\"configName\":\"knowledgeRepos\"," + + "\"name\":\"k14\",\"description\":\"\",\"type\":\"VECTOR\",\"createdAt\":\"2024-12-02 12:41:14\"," + + "\"checked\":true},{\"id\":2,\"configName\":\"knowledgeRepos\",\"name\":\"k2\",\"description\":\"\"," + + "\"type\":\"VECTOR\",\"createdAt\":\"2024-12-02 12:37:17\",\"checked\":true}]}," + + "{\"nodeId\":\"jadenthrc1\",\"nodeName\":\"知识检索1\",\"configs\":[{\"id\":11," + + "\"configName\":\"knowledgeRepos\",\"name\":\"k14\",\"description\":\"\",\"type\":\"VECTOR\"," + + "\"createdAt\":\"2024-12-02 12:41:14\",\"checked\":true}]}]}"; AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); List results = this.retrievalNodeChecker.validate(appCheckDto, null); Assertions.assertEquals(results.size(), 2); @@ -158,12 +155,11 @@ public void before() { @DisplayName("插件配置存在,检查通过") public void testPluginNodeSuccess() { String testNode = - "{\"type\":\"toolInvokeNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadecn3l6n\",\"nodeName\":\"666\"," - + "\"configs\":[{\"id\":\"jadecn3l6n\",\"configName\":\"toolInvokeNodeState\"," - + "\"uniqueName\":\"20ac1975-2455-4283-9a2f-30c7b5108df6\"}]},{\"nodeId\":\"pluginNode2\"," - + "\"nodeName\":\"555\",\"configs\":[{\"id\":\"jadecn3l6l\"," - + "\"configName\":\"toolInvokeNodeState\"," - + "\"uniqueName\":\"3e8c4186-5609-8148-a92e-3da9e4a1a660\"}]}]}"; + "{\"type\":\"toolInvokeNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadecn3l6n\",\"nodeName\":\"666\"," + + "\"configs\":[{\"id\":\"jadecn3l6n\",\"configName\":\"toolInvokeNodeState\"," + + "\"uniqueName\":\"20ac1975-2455-4283-9a2f-30c7b5108df6\"}]},{\"nodeId\":\"pluginNode2\"," + + "\"nodeName\":\"555\",\"configs\":[{\"id\":\"jadecn3l6l\",\"configName\":\"toolInvokeNodeState\"," + + "\"uniqueName\":\"3e8c4186-5609-8148-a92e-3da9e4a1a660\"}]}]}"; AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); when(pluginToolService.hasPluginTools(any())).thenReturn(Arrays.asList(true, true)); List results = this.toolInvokeNodeChecker.validate(appCheckDto, null); @@ -176,12 +172,11 @@ public void testPluginNodeSuccess() { @DisplayName("插件配置不存在,检查结果包含不可用配置") public void testPluginNodeWithNonExistsConfigs() { String testNode = - "{\"type\":\"toolInvokeNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadecn3l6n\",\"nodeName\":\"666\"," - + "\"configs\":[{\"id\":\"jadecn3l6n\",\"configName\":\"toolInvokeNodeState\"," - + "\"uniqueName\":\"20ac1975-2455-4283-9a2f-30c7b5108df6\"}]},{\"nodeId\":\"pluginNode2\"," - + "\"nodeName\":\"555\",\"configs\":[{\"id\":\"jadecn3l6l\"," - + "\"configName\":\"toolInvokeNodeState\"," - + "\"uniqueName\":\"3e8c4186-5609-8148-a92e-3da9e4a1a660\"}]}]}"; + "{\"type\":\"toolInvokeNodeState\",\"nodeInfos\":[{\"nodeId\":\"jadecn3l6n\",\"nodeName\":\"666\"," + + "\"configs\":[{\"id\":\"jadecn3l6n\",\"configName\":\"toolInvokeNodeState\"," + + "\"uniqueName\":\"20ac1975-2455-4283-9a2f-30c7b5108df6\"}]},{\"nodeId\":\"pluginNode2\"," + + "\"nodeName\":\"555\",\"configs\":[{\"id\":\"jadecn3l6l\",\"configName\":\"toolInvokeNodeState\"," + + "\"uniqueName\":\"3e8c4186-5609-8148-a92e-3da9e4a1a660\"}]}]}"; AppCheckDto appCheckDto = JsonUtils.parseObject(testNode, AppCheckDto.class); when(pluginToolService.hasPluginTools(any())).thenReturn(Arrays.asList(true, false)); List results = this.toolInvokeNodeChecker.validate(appCheckDto, null); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/DatabaseFieldLocaleServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/DatabaseFieldLocaleServiceImplTest.java index 20eccb444e..4ac2f85ffe 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/DatabaseFieldLocaleServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/DatabaseFieldLocaleServiceImplTest.java @@ -33,7 +33,6 @@ @DisplayName("DatabaseFieldLocaleService") public class DatabaseFieldLocaleServiceImplTest { private I18nMapper i18nMapper; - private DatabaseFieldLocaleServiceImplTestExt databaseFieldLocaleServiceImplTestExt; @BeforeEach @@ -51,10 +50,10 @@ void shouldSuccessWhenGetLocaleResource() { i18nPoList.add(new I18nPo("123", "name", "en", "ZhangSan")); Mockito.when(this.i18nMapper.selectResource()).thenReturn(i18nPoList); this.databaseFieldLocaleServiceImplTestExt.loadResource(); - assertThat(this.databaseFieldLocaleServiceImplTestExt.getLocaleMessage("name", Locale.ENGLISH)).isEqualTo( - "ZhangSan"); - assertThat(this.databaseFieldLocaleServiceImplTestExt.getLocaleMessage("name", Locale.CHINESE)).isEqualTo( - "张三"); + assertThat(this.databaseFieldLocaleServiceImplTestExt.getLocaleMessage("name", Locale.ENGLISH)) + .isEqualTo("ZhangSan"); + assertThat(this.databaseFieldLocaleServiceImplTestExt.getLocaleMessage("name", Locale.CHINESE)) + .isEqualTo("张三"); } private static class DatabaseFieldLocaleServiceImplTestExt extends DatabaseFieldLocaleServiceImpl { diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/LlmServiceTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/LlmServiceTest.java index 0025b02274..39c0afe95b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/LlmServiceTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/LlmServiceTest.java @@ -36,16 +36,13 @@ */ public class LlmServiceTest { private static final ChatModel OPENAI_CLIENT_MOCK = mock(ChatModel.class); - private static final VoiceService VOICE_SERVICE_MOCK = mock(VoiceService.class); - - private static final HttpClassicClientFactory FACTORY_MOCK = mock(HttpClassicClientFactory.class, - RETURNS_DEEP_STUBS); + private static final HttpClassicClientFactory FACTORY_MOCK = + mock(HttpClassicClientFactory.class, RETURNS_DEEP_STUBS); @Test void testAskWithText() { - LLMService llmServiceMock = new LLMServiceImpl(null, null, OPENAI_CLIENT_MOCK, - VOICE_SERVICE_MOCK, FACTORY_MOCK); + LLMService llmServiceMock = new LLMServiceImpl(OPENAI_CLIENT_MOCK); Choir responseMock = mock(Choir.class, RETURNS_DEEP_STUBS); try { when(OPENAI_CLIENT_MOCK.generate(any(Prompt.class), any(ChatOption.class))).thenReturn(responseMock); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StatisticsServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StatisticsServiceImplTest.java index 2bb17d8b04..b9e957c5a9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StatisticsServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StatisticsServiceImplTest.java @@ -9,12 +9,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import modelengine.jade.store.service.PluginService; -import modelengine.jade.store.service.support.DeployStatus; - import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.dto.StatisticsDTO; import modelengine.fit.jober.aipp.service.impl.StatisticsServiceImpl; +import modelengine.jade.store.service.PluginService; +import modelengine.jade.store.service.support.DeployStatus; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StoreServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StoreServiceImplTest.java index e8f7bc3bc2..0f8a13b877 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StoreServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/StoreServiceImplTest.java @@ -47,22 +47,16 @@ @DisplayName("测试 StoreServiceImpl") public class StoreServiceImplTest { private static final String RESOURCE_PATH = "component"; - private static final String BASIC_NODE_ZH_PATH = "/basic_node_zh.json"; - private static final String BASIC_NODE_EN_PATH = "/basic_node_en.json"; - private static final String EVALUATION_NODE_ZH_PATH = "/evaluation_node_zh.json"; - private static final String EVALUATION_NODE_EN_PATH = "/evaluation_node_en.json"; - private static final Map TAGS = MapBuilder.get() .put("CODENODESTATE", "codeNodeState") .put("QUERYOPTIMIZATIONNODESTATE", "queryOptimizationNodeState") .build(); private PluginToolService pluginToolService; - private StoreServiceImpl storeService; @BeforeEach diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImplTest.java index 9023a25b05..d4cbf17098 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTemplateServiceImplTest.java @@ -11,14 +11,14 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.alibaba.fastjson.JSONObject; - import modelengine.fit.jane.common.entity.OperationContext; import modelengine.fit.jober.aipp.condition.TemplateQueryCondition; +import modelengine.fit.jober.aipp.converters.ConverterFactory; import modelengine.fit.jober.aipp.domain.AppBuilderApp; import modelengine.fit.jober.aipp.domain.AppBuilderConfig; import modelengine.fit.jober.aipp.domain.AppBuilderConfigProperty; @@ -26,26 +26,27 @@ import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; import modelengine.fit.jober.aipp.domain.AppTemplate; -import modelengine.fit.jober.aipp.dto.AippCreateDto; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.dto.template.TemplateAppCreateDto; import modelengine.fit.jober.aipp.dto.template.TemplateInfoDto; -import modelengine.fit.jober.aipp.factory.AppBuilderAppFactory; import modelengine.fit.jober.aipp.factory.AppTemplateFactory; -import modelengine.fit.jober.aipp.repository.AppBuilderAppRepository; import modelengine.fit.jober.aipp.repository.AppBuilderConfigPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderConfigRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFlowGraphRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormPropertyRepository; import modelengine.fit.jober.aipp.repository.AppBuilderFormRepository; import modelengine.fit.jober.aipp.repository.AppTemplateRepository; -import modelengine.fit.jober.aipp.service.AippFlowService; import modelengine.fit.jober.aipp.service.AppBuilderAppService; import modelengine.fit.jober.aipp.service.AppTemplateService; import modelengine.fit.jober.aipp.service.UploadedFileManageService; import modelengine.fit.jober.aipp.util.AippFileUtils; import modelengine.fit.jober.aipp.util.TemplateUtils; import modelengine.fit.jober.common.RangedResultSet; + +import com.alibaba.fastjson.JSONObject; + import modelengine.fitframework.util.MapBuilder; import org.junit.jupiter.api.BeforeEach; @@ -63,7 +64,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Map; /** * 应用模板服务测试类。 @@ -76,18 +76,11 @@ public class AppTemplateServiceImplTest { private static final LocalDateTime TIME = LocalDateTime.of(2025, 1, 16, 9, 0); private AppTemplateService templateService; - private AppBuilderAppService appService; - @Mock - private AippFlowService aippFlowService; - @Mock private UploadedFileManageService uploadedFileManageService; - @Mock - private AppBuilderAppRepository appRepository; - @Mock private AppTemplateRepository templateRepository; @@ -106,16 +99,27 @@ public class AppTemplateServiceImplTest { @Mock private AppBuilderFormPropertyRepository formPropertyRepository; + @Mock + private AppVersionService appVersionService; + + private ConverterFactory converterFactory; + @BeforeEach void setup() { - AppBuilderAppFactory appFactory = new AppBuilderAppFactory(this.flowGraphRepository, this.configRepository, - this.formRepository, this.configPropertyRepository, this.formPropertyRepository, this.appRepository); - AppTemplateFactory templateFactory = new AppTemplateFactory(this.flowGraphRepository, this.configRepository, - this.formRepository, this.configPropertyRepository, this.formPropertyRepository, + AppTemplateFactory templateFactory = new AppTemplateFactory(this.flowGraphRepository, + this.configRepository, + this.formRepository, + this.configPropertyRepository, + this.formPropertyRepository, this.templateRepository); - this.appService = new AppBuilderAppServiceImpl(appFactory, this.aippFlowService, this.appRepository, - templateFactory, 64, null, null, null, null, this.uploadedFileManageService, null, null, null, null, - null, null, null, null, null, null, null, null, "", null); + this.converterFactory = mock(ConverterFactory.class); + this.appService = new AppBuilderAppServiceImpl( + templateFactory, + this.uploadedFileManageService, + null, + this.appVersionService, + null, + null, this.converterFactory, null); this.templateService = new AppTemplateServiceImpl(this.appService, this.templateRepository); } @@ -129,11 +133,13 @@ void testQueryTemplate() { when(this.templateRepository.selectWithCondition(any())).thenReturn(queryResult); when(this.templateRepository.countWithCondition(any())).thenReturn(queryResult.size()); - RangedResultSet res = this.templateService.query(TemplateQueryCondition.builder().build(), - null); + RangedResultSet res = + this.templateService.query(TemplateQueryCondition.builder().build(), null); assertThat(res.getResults()).hasSize(1) .element(0) - .extracting(TemplateInfoDto::getAppType, TemplateInfoDto::getId, TemplateInfoDto::getIcon, + .extracting(TemplateInfoDto::getAppType, + TemplateInfoDto::getId, + TemplateInfoDto::getIcon, TemplateInfoDto::getDescription) .containsExactly("default", null, "/path/to/icon", null); } @@ -142,10 +148,8 @@ void testQueryTemplate() { @DisplayName("测试将应用发布为应用模板") void testPublishAppTemplateFromApp() throws IOException { AppBuilderApp app = this.mockApp(); - when(this.appRepository.selectWithId(anyString())).thenReturn(app); - when(this.flowGraphRepository.selectWithId(any())).thenReturn(app.getFlowGraph()); - when(this.configRepository.selectWithId(any())).thenReturn(app.getConfig()); - when(this.formPropertyRepository.selectWithAppId(any())).thenReturn(app.getFormProperties()); + AppVersion mockappVersion = mock(AppVersion.class); + when(this.appVersionService.retrieval(anyString())).thenReturn(mockappVersion); File mockFile = File.createTempFile("old_test_icon", ".png"); TemplateAppCreateDto mockDto = this.mockCreateDto(); @@ -153,55 +157,25 @@ void testPublishAppTemplateFromApp() throws IOException { oldIcon = oldIcon.replace(AippFileUtils.getFileNameFromIcon(oldIcon), mockFile.getCanonicalPath()); mockDto.setIcon(oldIcon); app.getAttributes().put("icon", oldIcon); - TemplateInfoDto published = this.templateService.publish(mockDto, this.mockOperationContext()); - File icon = new File(AippFileUtils.getFileNameFromIcon(published.getIcon())); - mockFile.delete(); - icon.delete(); - - verify(this.uploadedFileManageService, times(1)).addFileRecord(any(), eq("demo_account"), any(), any()); - verify(this.uploadedFileManageService, times(1)).updateRecord(any(), any(), eq(0)); - - assertThat(published).extracting(TemplateInfoDto::getName, TemplateInfoDto::getDescription, - TemplateInfoDto::getAppType, TemplateInfoDto::getCreator) - .containsExactly("test_name", "test_description", "finance", "operator"); - - String templateId = published.getId(); - assertThat(app.getConfig()).extracting(AppBuilderConfig::getAppId).isEqualTo(templateId); - assertThat(app.getFormProperties()).element(0) - .extracting(AppBuilderFormProperty::getAppId) - .isEqualTo(templateId); + OperationContext context = this.mockOperationContext(); + this.templateService.publish(mockDto, context); + verify(mockappVersion, times(1)).publishTemplate(mockDto, context); } @Test @DisplayName("测试根据应用模板创建应用") void testCreateAppByTemplate() { AppTemplate template = this.mockTemplate(); + AppVersion mockappVersion = mock(AppVersion.class); + AppBuilderAppDto mockappBuilderAppDto = mock(AppBuilderAppDto.class); when(this.templateRepository.selectWithId(anyString())).thenReturn(template); - when(this.flowGraphRepository.selectWithId(any())).thenReturn(template.getFlowGraph()); - when(this.configRepository.selectWithId(any())).thenReturn(template.getConfig()); - when(this.formPropertyRepository.selectWithAppId(any())).thenReturn(template.getFormProperties()); - when(this.aippFlowService.previewAipp(any(), any(), any())).thenReturn( - AippCreateDto.builder().aippId("123456").build()); - - AppBuilderAppDto dto = this.templateService.createAppByTemplate(this.mockCreateDto(), - this.mockOperationContext()); + when(this.appVersionService.createByTemplate(any(AppTemplate.class), any(OperationContext.class))).thenReturn( + mockappVersion); + when(this.converterFactory.convert(any(), any())).thenReturn(mockappBuilderAppDto); + this.templateService.createAppByTemplate(this.mockCreateDto(), this.mockOperationContext()); verify(this.uploadedFileManageService, times(0)).addFileRecord(any(), any(), any(), any()); - verify(this.uploadedFileManageService, times(1)).updateRecord(any(), any(), eq(0)); + verify(this.uploadedFileManageService, times(0)).updateRecord(any(), any(), eq(0)); verify(this.templateRepository, times(1)).increaseUsage(eq("123456")); - - assertThat(dto).extracting(AppBuilderAppDto::getName, AppBuilderAppDto::getVersion, - AppBuilderAppDto::getAppType, AppBuilderAppDto::getCreateBy, AppBuilderAppDto::getType, - AppBuilderAppDto::getState, AppBuilderAppDto::getAippId) - .containsExactly("test_name", "1.0.0", "finance", "operator", "app", "inactive", "123456"); - String appId = dto.getId(); - assertThat(template.getConfig()).extracting(AppBuilderConfig::getAppId).isEqualTo(appId); - assertThat(template.getFormProperties()).element(0) - .extracting(AppBuilderFormProperty::getAppId) - .isEqualTo(appId); - Map attributes = dto.getAttributes(); - assertThat(attributes).containsEntry("description", "test_description") - .containsEntry("icon", "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/tmp/test_old" - + ".jpg&fileName=test_old.jpg"); } @Test @@ -211,20 +185,27 @@ void testDeleteAppTemplate() { when(this.templateRepository.selectWithId(anyString())).thenReturn(template); this.templateService.delete(template.getId(), this.mockOperationContext()); - verify(this.configRepository, times(1)).delete( - argThat(arg -> arg.size() == 1 && arg.get(0).equals(template.getConfigId()))); - verify(this.flowGraphRepository, times(1)).delete( - argThat(arg -> arg.size() == 1 && arg.get(0).equals(template.getFlowGraphId()))); + verify(this.configRepository, times(1)).delete(argThat(arg -> arg.size() == 1 && arg.get(0) + .equals(template.getConfigId()))); + verify(this.flowGraphRepository, times(1)).delete(argThat(arg -> arg.size() == 1 && arg.get(0) + .equals(template.getFlowGraphId()))); verify(this.templateRepository, times(1)).deleteOne(eq(template.getId())); - verify(this.formPropertyRepository, times(1)).deleteByAppIds( - argThat(arg -> arg.size() == 1 && arg.get(0).equals(template.getId()))); - verify(this.uploadedFileManageService, times(1)).cleanAippFiles( - argThat(arg -> arg.size() == 1 && arg.get(0).equals(template.getId()))); + verify(this.formPropertyRepository, times(1)).deleteByAppIds(argThat(arg -> arg.size() == 1 && arg.get(0) + .equals(template.getId()))); + verify(this.uploadedFileManageService, times(1)).cleanAippFiles(argThat(arg -> arg.size() == 1 && arg.get(0) + .equals(template.getId()))); } private OperationContext mockOperationContext() { - return new OperationContext("tenant_id", "operator", "global_user_id", "demo_account", "employ_number", "name", - "0.0.0.0", "unit test", "zh_CN"); + return new OperationContext("tenant_id", + "operator", + "global_user_id", + "account", + "employ_number", + "name", + "0.0.0.0", + "unit test", + "zh_CN"); } private TemplateAppCreateDto mockCreateDto() { @@ -239,8 +220,11 @@ private TemplateAppCreateDto mockCreateDto() { } private AppTemplate mockTemplate() { - AppTemplate template = new AppTemplate(flowGraphRepository, configRepository, formRepository, - configPropertyRepository, formPropertyRepository); + AppTemplate template = new AppTemplate(flowGraphRepository, + configRepository, + formRepository, + configPropertyRepository, + formPropertyRepository); template.setBuiltType("basic"); template.setCategory("chatbot"); template.setName("Unit Test Template"); @@ -264,8 +248,11 @@ private AppTemplate mockTemplate() { } private AppBuilderApp mockApp() { - AppBuilderApp appBuilderApp = new AppBuilderApp(flowGraphRepository, configRepository, formRepository, - configPropertyRepository, formPropertyRepository); + AppBuilderApp appBuilderApp = new AppBuilderApp(flowGraphRepository, + configRepository, + formRepository, + configPropertyRepository, + formPropertyRepository); appBuilderApp.setType("template"); appBuilderApp.setAppBuiltType("basic"); appBuilderApp.setAppCategory("chatbot"); @@ -274,8 +261,9 @@ private AppBuilderApp mockApp() { appBuilderApp.setId("45698235b3d24209aefd59eb7d1c3322"); appBuilderApp.setAttributes(new HashMap<>()); appBuilderApp.getAttributes() - .put("icon", "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/tmp/test_old" - + ".jpg&fileName=test_old.jpg"); + .put("icon", + "/api/jober/v1/api/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/tmp/test_old" + + ".jpg&fileName=test_old.jpg"); appBuilderApp.getAttributes().put("app_type", "编程开发"); appBuilderApp.getAttributes().put("greeting", "1"); appBuilderApp.getAttributes().put("description", "1"); @@ -287,7 +275,6 @@ private AppBuilderApp mockApp() { appBuilderApp.setVersion("1.0.0"); appBuilderApp.setPath("YGHmQFJE5ZaFW4wl"); appBuilderApp.setConfig(this.mockConfig()); - appBuilderApp.getConfig().setApp(appBuilderApp); appBuilderApp.setConfigId(appBuilderApp.getConfig().getId()); appBuilderApp.setFlowGraph(this.mockGraph()); appBuilderApp.setFlowGraphId(appBuilderApp.getFlowGraph().getId()); @@ -296,8 +283,10 @@ private AppBuilderApp mockApp() { } private AppBuilderConfig mockConfig() { - AppBuilderConfig config = new AppBuilderConfig(this.formRepository, this.formPropertyRepository, - this.configPropertyRepository, this.appRepository); + AppBuilderConfig config = new AppBuilderConfig(this.formRepository, + this.formPropertyRepository, + this.configPropertyRepository, + this.appVersionService); config.setAppId("45698235b3d24209aefd59eb7d1c3322"); config.setId("24581235b3d24209aefd59eb7d1c3322"); @@ -335,27 +324,36 @@ private AppBuilderForm mockForm() { } private List mockFormProperties() { - List values = Arrays.asList("null", "null", Collections.singletonList("jadewdnjbq"), + List values = Arrays.asList( + "null", "null", Collections.singletonList("jadewdnjbq"), Arrays.asList(Arrays.asList("jadewdnjbq", "tools"), Arrays.asList("jadewdnjbq", "workflows")), Arrays.asList("jade0pg2ag", "knowledge"), "null", Arrays.asList("jade6qm5eg", "memory"), JSONObject.parseObject( "{\"category\":[{\"title\":\"root\",\"id\":\"root\",\"children\":[]}],\"inspirations\":[]}"), JSONObject.parseObject("{\"showRecommend\":false, \"list\":[]}"), - "i18n_appBuilder_{form_property_opening_content}"); - List names = Arrays.asList("basic", "ability", "model", "tools", "knowledge", "chat", "memory", - "inspiration", "recommend", "opening"); - List dataTypes = Arrays.asList("String", "String", "List", "List>", "List", - "String", "List", "object", "object", "String"); - List from = Arrays.asList("none", "none", "graph", "graph", "graph", "none", "graph", "input", "input", - "input"); - List group = Arrays.asList("null", "basic", "ability", "ability", "ability", "basic", "chat", "chat", - "chat", "chat"); - List description = Arrays.asList("i18n_appBuilder_{form_property_basic}", - "i18n_appBuilder_{form_property_ability}", "i18n_appBuilder_{form_property_model}", - "i18n_appBuilder_{form_property_tools}", "i18n_appBuilder_{form_property_knowledge}", - "i18n_appBuilder_{form_property_chat}", "i18n_appBuilder_{form_property_memory}", - "i18n_appBuilder_{form_property_inspiration}", "i18n_appBuilder_{form_property_recommend}", - "i18n_appBuilder_{form_property_opening}"); + "i18n_appBuilder_{form_property_opening_content}" + ); + List names = Arrays.asList( + "basic", "ability", "model", "tools", "knowledge", "chat", "memory", "inspiration", "recommend", + "opening" + ); + List dataTypes = Arrays.asList( + "String", "String", "List", "List>", "List", "String", "List", + "object", "object", "String" + ); + List from = Arrays.asList( + "none", "none", "graph", "graph", "graph", "none", "graph", "input", "input", "input" + ); + List group = Arrays.asList( + "null", "basic", "ability", "ability", "ability", "basic", "chat", "chat", "chat", "chat" + ); + List description = Arrays.asList( + "i18n_appBuilder_{form_property_basic}", "i18n_appBuilder_{form_property_ability}", + "i18n_appBuilder_{form_property_model}", "i18n_appBuilder_{form_property_tools}", + "i18n_appBuilder_{form_property_knowledge}", "i18n_appBuilder_{form_property_chat}", + "i18n_appBuilder_{form_property_memory}", "i18n_appBuilder_{form_property_inspiration}", + "i18n_appBuilder_{form_property_recommend}", "i18n_appBuilder_{form_property_opening}" + ); List formProperties = new ArrayList<>(); for (int i = 0; i < 10; i++) { AppBuilderFormProperty formProperty = new AppBuilderFormProperty(); @@ -379,7 +377,8 @@ private List mockConfigProperties() { List configProperties = new ArrayList<>(); for (int i = 0; i < 8; i++) { AppBuilderConfigProperty configProperty = new AppBuilderConfigProperty(this.configRepository, - this.formRepository, this.formPropertyRepository); + this.formRepository, + this.formPropertyRepository); configProperty.setConfigId(i + "275e235b3d24209aefd59eb8541a549" + i); configProperty.setFormPropertyId(i + "c65e235b3d24209aefd59eb7d1a549" + i); configProperty.setNodeId(nodeIds[i]); @@ -390,8 +389,8 @@ private List mockConfigProperties() { } private AppBuilderFlowGraph mockGraph() { - String appearance - = "{\"id\": \"69e9dec999384b1791e24a3032010e77\", \"type\": \"jadeFlowGraph\", \"pages\": []}"; + String appearance = + "{\"id\": \"69e9dec999384b1791e24a3032010e77\", \"type\": \"jadeFlowGraph\", \"pages\": []}"; AppBuilderFlowGraph graph = new AppBuilderFlowGraph("69e9dec999384b1791e24a3032010e77", "graph", appearance); graph.setUpdateAt(TIME); graph.setCreateAt(TIME); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImplTest.java index 27a538fdf5..b0f79c08d3 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/AppTypeServiceImplTest.java @@ -95,8 +95,8 @@ void shouldReturnNewIdWhenAddGivenTypeWithoutId() { Mockito.doNothing() .when(this.appBuilderAppTypeMapper) .insert(Mockito.argThat( - po -> !po.getId().isEmpty() && po.getName().equals(expectPo.getName()) && po.getTenantId() - .equals(expectPo.getTenantId()))); + po -> !po.getId().isEmpty() && po.getName().equals(expectPo.getName()) + && po.getTenantId().equals(expectPo.getTenantId()))); AppTypeDto result = this.appTypeService.add(new AppTypeDto("", expectPo.getName()), tenantId); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/FileServiceImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/FileServiceImplTest.java index 18bb885b65..e924dda656 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/FileServiceImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/impl/FileServiceImplTest.java @@ -10,6 +10,16 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import modelengine.fit.jane.common.entity.OperationContext; +import modelengine.fit.jane.common.response.Rsp; +import modelengine.fit.jober.aipp.common.exception.AippErrCode; +import modelengine.fit.jober.aipp.common.exception.AippException; +import modelengine.fit.jober.aipp.config.FormFileUploadConfig; +import modelengine.fit.jober.aipp.dto.GenerateImageDto; +import modelengine.fit.jober.aipp.service.FileService; +import modelengine.fit.jober.aipp.service.UploadedFileManageService; +import modelengine.fit.jober.aipp.validation.FormFileValidator; + import modelengine.fit.http.HttpMessage; import modelengine.fit.http.client.HttpClassicClient; import modelengine.fit.http.client.HttpClassicClientFactory; @@ -29,15 +39,6 @@ import modelengine.fit.http.header.support.DefaultParameterCollection; import modelengine.fit.http.protocol.HttpRequestMethod; import modelengine.fit.http.protocol.HttpResponseStatus; -import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jane.common.response.Rsp; -import modelengine.fit.jober.aipp.common.exception.AippErrCode; -import modelengine.fit.jober.aipp.common.exception.AippException; -import modelengine.fit.jober.aipp.config.FormFileUploadConfig; -import modelengine.fit.jober.aipp.dto.GenerateImageDto; -import modelengine.fit.jober.aipp.service.FileService; -import modelengine.fit.jober.aipp.service.UploadedFileManageService; -import modelengine.fit.jober.aipp.validation.FormFileValidator; import modelengine.fitframework.util.FileUtils; import modelengine.fitframework.util.ObjectUtils; @@ -98,9 +99,15 @@ void setUp() { ContentType contentType = new DefaultContentType(headerValue); Optional optionalContentType = Optional.of(contentType); when(this.httpMessage.contentType()).thenReturn(optionalContentType); - this.fileService = new FileServiceImpl(this.httpClassicClientFactory, URL, "model", this.formFileValidator, - this.uploadedFileManageService, this.formFileUploadConfig, "form", "form/temporary", "", "form", - "/file/"); + this.fileService = new FileServiceImpl(this.httpClassicClientFactory, + URL, + "model", + this.formFileValidator, + this.uploadedFileManageService, + this.formFileUploadConfig, + "form", + "form/temporary", + "", "form", "/file/"); } @Test @@ -178,22 +185,27 @@ void givenNotExistEntitiesThenThrowException() { @DisplayName("当解压缩文件时,如果文件小于5M,解压缩成功") void givenNotExceed5MThenUnzipSucceed() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, URISyntaxException { - Method unZipFormFileMethod = FileServiceImpl.class.getDeclaredMethod("unZipFormFile", String.class, - String.class, String.class, List.class); + Method unZipFormFileMethod = FileServiceImpl.class.getDeclaredMethod("unZipFormFile", + String.class, + String.class, + String.class, + List.class); unZipFormFileMethod.setAccessible(true); List namedEntities = new ArrayList<>(); NamedEntity namedEntity = Mockito.mock(NamedEntity.class); - InputStream inputStream = FileServiceImplTest.class.getClassLoader() - .getResourceAsStream("form/testNotExceed5M.zip"); - FileEntity fileEntity = FileEntity.create(httpMessage, "entityFileName", inputStream, 0, - FileEntity.Position.INLINE, null); + InputStream inputStream = + FileServiceImplTest.class.getClassLoader().getResourceAsStream("form/testNotExceed5M.zip"); + FileEntity fileEntity = + FileEntity.create(httpMessage, "entityFileName", inputStream, 0, FileEntity.Position.INLINE, null); when(namedEntity.asFile()).thenReturn(fileEntity); namedEntities.add(namedEntity); String fromTemporaryPath = Paths.get(ClassLoader.getSystemResource("form_temporary").toURI()).toString(); String from = Paths.get(ClassLoader.getSystemResource("form").toURI()).toString(); - File unzipFile = ObjectUtils.cast( - unZipFormFileMethod.invoke(this.fileService, "form/testNotExceed5M.zip", fromTemporaryPath, from, - namedEntities)); + File unzipFile = ObjectUtils.cast(unZipFormFileMethod.invoke(this.fileService, + "form/testNotExceed5M.zip", + fromTemporaryPath, + from, + namedEntities)); List unzipFiles = FileUtils.list(unzipFile); Assertions.assertEquals(unzipFiles.size(), 3); FileUtils.delete(unzipFile); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java new file mode 100644 index 0000000000..5e026fa5ce --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/AippInstanceLogCleanerTest.java @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.opencsv.CSVWriter; + +import modelengine.fit.jober.aipp.entity.AippInstLog; +import modelengine.fit.jober.aipp.enums.AippTypeEnum; +import modelengine.fit.jober.aipp.repository.AippInstanceLogRepository; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * {@link AippInstanceLogCleaner} 对应测试类。 + * + * @author 杨祥宇 + * @since 2025-04-18 + */ +@ExtendWith(MockitoExtension.class) +public class AippInstanceLogCleanerTest { + @Mock + private AippInstanceLogRepository instanceLogRepo; + + @Mock + private CsvWriterHelper csvWriterHelper; + + private AippInstanceLogCleaner logCleaner; + + @BeforeEach + void setup() { + String aippInstanceLogFilePath = "/var/share/backup/aipp-instance-log/"; + this.logCleaner = new AippInstanceLogCleaner(this.instanceLogRepo, this.csvWriterHelper, aippInstanceLogFilePath); + } + + @Test + @DisplayName("测试清理 Normal 信息并备份") + void cleanAippInstanceNormalLogShouldBackupAndDelete() throws Exception { + List mockLogIds = List.of(1L, 2L); + when(this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), 30, 100)).thenReturn(mockLogIds) + .thenReturn(Collections.emptyList()); + AippInstLog mockLog = new AippInstLog(); + when(this.instanceLogRepo.selectByLogIds(anyList())).thenReturn(List.of(mockLog)); + CSVWriter csvWriter = mock(CSVWriter.class); + when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenReturn(csvWriter); + File file = mock(File.class); + File csvFile = mock(File.class); + when(this.csvWriterHelper.getFile(anyString())).thenReturn(file); + when(file.listFiles(any(FilenameFilter.class))).thenReturn(new File[] {csvFile}); + this.logCleaner.cleanAippInstanceNormalLog(30, 100); + verify(this.instanceLogRepo, times(2)).getExpireInstanceLogIds(anyString(), anyInt(), anyInt()); + verify(this.instanceLogRepo, times(1)).forceDeleteInstanceLogs(anyList()); + verify(csvWriter, times(1)).writeAll(anyList()); + verify(csvFile, times(0)).delete(); + } + + @Test + @DisplayName("测试 Normal 信息备份失败时不清理数据") + void cleanAippInstanceNormalLogShouldNotDeleteWhenBackupFailed() throws Exception { + List mockLogIds = List.of(1L, 2L); + when(this.instanceLogRepo.getExpireInstanceLogIds(AippTypeEnum.NORMAL.type(), 30, 100)).thenReturn(mockLogIds) + .thenReturn(Collections.emptyList()); + when(this.instanceLogRepo.selectByLogIds(anyList())).thenReturn(Collections.singletonList(new AippInstLog())); + CSVWriter csvWriter = mock(CSVWriter.class); + when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenThrow(new IOException("error")); + this.logCleaner.cleanAippInstanceNormalLog(30, 100); + verify(this.instanceLogRepo, times(1)).getExpireInstanceLogIds(anyString(), anyInt(), anyInt()); + verify(this.instanceLogRepo, times(0)).forceDeleteInstanceLogs(anyList()); + verify(csvWriter, times(0)).writeAll(anyList()); + } + + @Test + public void cleanAippInstancePreviewLogMultipleBatches_DeletesAll() { + Mockito.when(this.instanceLogRepo.getExpireInstanceLogIds(Mockito.eq(AippTypeEnum.PREVIEW.type()), + Mockito.anyInt(), + Mockito.anyInt())).thenReturn(Arrays.asList(1L, 2L)).thenReturn(Collections.emptyList()); + this.logCleaner.cleanAippInstancePreviewLog(30, 100); + Mockito.verify(this.instanceLogRepo, Mockito.times(2)).getExpireInstanceLogIds(AippTypeEnum.PREVIEW.type(), 30, 100); + Mockito.verify(this.instanceLogRepo).forceDeleteInstanceLogs(Arrays.asList(1L, 2L)); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java new file mode 100644 index 0000000000..fe3e473105 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/service/scheduletask/ChatSessionCleanerTest.java @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +package modelengine.fit.jober.aipp.service.scheduletask; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.opencsv.CSVWriter; + +import modelengine.fit.jober.aipp.entity.ChatAndInstanceMap; +import modelengine.fit.jober.aipp.entity.ChatInfo; +import modelengine.fit.jober.aipp.repository.AippChatRepository; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * {@link ChatSessionCleaner} 对应测试类。 + * + * @author 杨祥宇 + * @since 2025-04-18 + */ +@ExtendWith(MockitoExtension.class) +public class ChatSessionCleanerTest { + @Mock + private AippChatRepository chatRepo; + + @Mock + private CsvWriterHelper csvWriterHelper; + + private ChatSessionCleaner chatSessionCleaner; + + @BeforeEach + void setup() { + String chatSessionFilePath = "/var/share/backup/chat-session/"; + this.chatSessionCleaner = new ChatSessionCleaner(this.chatRepo, this.csvWriterHelper, chatSessionFilePath); + } + + @Test + @DisplayName("测试没有过期数据时直接返回,不会做备份和删除操作") + void cleanNoExpiredDataShouldDoNothing() throws IOException { + when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(Collections.emptyList()); + this.chatSessionCleaner.clean(30, 100); + verify(this.chatRepo, never()).forceDeleteChat(anyList()); + verify(this.csvWriterHelper, never()).createCsvWriter(any(), anyBoolean()); + } + + @Test + @DisplayName("测试有过期数据时备份并成功删除") + void cleanWithExpiredDataShouldBackupAndDelete() throws Exception { + List expiredChatIds = List.of("chat1", "chat2"); + when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(expiredChatIds) + .thenReturn(Collections.emptyList()); + when(this.chatRepo.selectByChatIds(expiredChatIds)).thenReturn(List.of(new ChatInfo(), new ChatInfo())); + when(this.chatRepo.selectTaskInstanceRelationsByChatIds(expiredChatIds)).thenReturn(List.of(new ChatAndInstanceMap(), + new ChatAndInstanceMap())); + CSVWriter csvWriter = mock(CSVWriter.class); + when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenReturn(csvWriter); + File file = mock(File.class); + File csvFile = mock(File.class); + when(this.csvWriterHelper.getFile(anyString())).thenReturn(file); + when(file.listFiles(any(FilenameFilter.class))).thenReturn(new File[] {csvFile}); + this.chatSessionCleaner.clean(30, 100); + verify(this.chatRepo, times(1)).forceDeleteChat(expiredChatIds); + verify(this.csvWriterHelper, atLeast(2)).createCsvWriter(any(), eq(true)); + verify(csvWriter, times(2)).writeAll(anyList()); + verify(csvFile, times(0)).delete(); + } + + @Test + @DisplayName("测试数据备份失败时不会删除过期数据") + void cleanShouldNotDeleteWhenBackupFailed() throws Exception { + List expiredChatIds = List.of("chat1", "chat2"); + when(this.chatRepo.getExpiredChatIds(anyInt(), anyInt())).thenReturn(expiredChatIds) + .thenReturn(Collections.emptyList()); + when(this.chatRepo.selectByChatIds(anyList())).thenReturn(Collections.singletonList(new ChatInfo())); + CSVWriter csvWriter = mock(CSVWriter.class); + when(this.csvWriterHelper.createCsvWriter(any(), anyBoolean())).thenThrow(new IOException("error")); + this.chatSessionCleaner.clean(30, 100); + verify(this.chatRepo, times(1)).getExpiredChatIds(anyInt(), anyInt()); + verify(this.chatRepo, times(0)).forceDeleteChat(anyList()); + verify(csvWriter, times(0)).writeAll(anyList()); + } +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AippJacksonObjectSerializer.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AippJacksonObjectSerializer.java index 85748d93d6..0d3ab2470b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AippJacksonObjectSerializer.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AippJacksonObjectSerializer.java @@ -8,6 +8,9 @@ import static modelengine.fitframework.inspection.Validation.notNull; +import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeDeserializer; +import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeSerializer; + import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactoryBuilder; import com.fasterxml.jackson.core.JsonGenerator; @@ -17,8 +20,6 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeDeserializer; -import modelengine.fit.jober.aipp.init.serialization.custom.LocalDateTimeSerializer; import modelengine.fitframework.annotation.Value; import modelengine.fitframework.serialization.ObjectSerializer; import modelengine.fitframework.serialization.SerializationException; @@ -55,8 +56,9 @@ public AippJacksonObjectSerializer(@Value("${jackson.datetime-format}") String d customSerialization.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormat)); customSerialization.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormat)); - this.mapper = JsonMapper.builder(new JsonFactoryBuilder().streamReadConstraints( - StreamReadConstraints.builder().maxStringLength(Integer.MAX_VALUE).build()).build()) + this.mapper = JsonMapper.builder(new JsonFactoryBuilder().streamReadConstraints(StreamReadConstraints.builder() + .maxStringLength(Integer.MAX_VALUE) + .build()).build()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .addModule(customSerialization) .build(); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AppBuilderAppToolTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AppBuilderAppToolTest.java index 7bf32750ce..5803c92be0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AppBuilderAppToolTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/tool/AppBuilderAppToolTest.java @@ -7,7 +7,13 @@ package modelengine.fit.jober.aipp.tool; import static modelengine.fit.jober.aipp.constants.AippConst.DEFAULT_DATE_TIME_FORMAT; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import modelengine.fit.jober.aipp.converters.ConverterFactory; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; +import modelengine.fit.jober.aipp.domains.appversion.service.AppVersionService; import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; import modelengine.fit.jober.aipp.service.AppBuilderAppService; import modelengine.fit.jober.aipp.tool.impl.AppBuilderAppToolImpl; @@ -35,20 +41,36 @@ public class AppBuilderAppToolTest { @Mock private AppBuilderAppService appService; + @Mock + private AppVersionService appVersionService; + + private ConverterFactory converterFactory; + private final String appEngineUrl = "localhost"; @BeforeEach void before() { - ObjectSerializer serializer = new AippJacksonObjectSerializer(DEFAULT_DATE_TIME_FORMAT); - this.appBuilderAppTool = new AppBuilderAppToolImpl(appService, serializer, this.appEngineUrl); + this.converterFactory = mock(ConverterFactory.class); + ObjectSerializer serializer = + new AippJacksonObjectSerializer(DEFAULT_DATE_TIME_FORMAT); + this.appBuilderAppTool = new AppBuilderAppToolImpl(appService, serializer, this.appEngineUrl, + this.appVersionService, this.converterFactory); } @Test @DisplayName("创建app方法测试") void testCreateApp() { - Mockito.when(appService.create(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyBoolean())) - .thenReturn(AppBuilderAppDto.builder().id("id").build()); + // given. + AppVersion mockAppVersion = Mockito.mock(AppVersion.class); + Mockito.when(this.appVersionService.create(Mockito.anyString(), Mockito.any(), Mockito.any())) + .thenReturn(mockAppVersion); + AppBuilderAppDto appDto = AppBuilderAppDto.builder().id("id").build(); + when(this.converterFactory.convert(any(), any())).thenReturn(appDto); + + // when. String s = Assertions.assertDoesNotThrow(() -> this.appBuilderAppTool.createApp("defaultErrorInfo", "me")); + + // then. Assertions.assertTrue(s.contains(this.appEngineUrl)); Assertions.assertTrue(s.endsWith("id")); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippLogUtilsTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippLogUtilsTest.java index 505dda2245..6aebe45568 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippLogUtilsTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippLogUtilsTest.java @@ -33,7 +33,8 @@ class AippLogUtilsTest { void testMSGType() { AippLogData test = AippLogData.builder().msg("This is a MSG log").build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertTrue((boolean) validFormMsg.invoke(null, test, "MSG")); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -47,7 +48,8 @@ void testBlankFormId() { AippLogData testEmpty = AippLogData.builder().formId("").formVersion("1.1").formArgs("test").build(); AippLogData testWhitespace = AippLogData.builder().formId(" ").formVersion("1.1").formArgs("test").build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertFalse((boolean) validFormMsg.invoke(null, testNull, "FORM")); assertFalse((boolean) validFormMsg.invoke(null, testEmpty, "FORM")); @@ -59,13 +61,15 @@ void testBlankFormId() { @Test void testInvalidFormId() { - AippLogData testInvalidFormId = AippLogData.builder() + AippLogData testInvalidFormId = AippLogData + .builder() .formId("undefined") .formVersion("1.1") .formArgs("test") .build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertFalse((boolean) validFormMsg.invoke(null, testInvalidFormId, "FORM")); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -79,7 +83,8 @@ void testBlankFormVersion() { AippLogData testEmpty = AippLogData.builder().formId("123456").formVersion("").formArgs("test").build(); AippLogData testWhitespace = AippLogData.builder().formId("123456").formVersion(" ").formArgs("test").build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertFalse((boolean) validFormMsg.invoke(null, testNull, "FORM")); assertFalse((boolean) validFormMsg.invoke(null, testEmpty, "FORM")); @@ -91,13 +96,14 @@ void testBlankFormVersion() { @Test void testInvalidFormVersion() { - AippLogData testInvalidVersionId = AippLogData.builder() + AippLogData testInvalidVersionId = AippLogData + .builder() .formId("123456") .formVersion("undefined") - .formArgs("test") - .build(); + .formArgs("test").build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertFalse((boolean) validFormMsg.invoke(null, testInvalidVersionId, "FORM")); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -107,13 +113,15 @@ void testInvalidFormVersion() { @Test void testNormal() { - AippLogData testInvalidVersionId = AippLogData.builder() + AippLogData testInvalidVersionId = AippLogData + .builder() .formId("123456") .formVersion("1.1") .formArgs("test") .build(); try { - Method validFormMsg = AippLogUtils.class.getDeclaredMethod("validFormMsg", AippLogData.class, String.class); + Method validFormMsg = AippLogUtils.class.getDeclaredMethod( + "validFormMsg", AippLogData.class, String.class); validFormMsg.setAccessible(true); assertTrue((boolean) validFormMsg.invoke(null, testInvalidVersionId, "FORM")); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippUtilTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippUtilTest.java index cdc795f320..745e759d7b 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippUtilTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AippUtilTest.java @@ -121,8 +121,8 @@ void testGetAgentParams() { @DisplayName("getBusiness失败:列表为空") void testGetBusinessWithEmptyInputList() { List> flowData = new ArrayList<>(); - JobberException exception = Assertions.assertThrows(JobberException.class, - () -> DataUtils.getBusiness(flowData)); + JobberException exception = + Assertions.assertThrows(JobberException.class, () -> DataUtils.getBusiness(flowData)); Assertions.assertEquals(10000000, exception.getCode()); } @@ -131,8 +131,8 @@ void testGetBusinessWithEmptyInputList() { void testGetBusinessWithoutBusinessData() { List> flowData = new ArrayList<>(); flowData.add(new HashMap<>()); - JobberException exception = Assertions.assertThrows(JobberException.class, - () -> DataUtils.getBusiness(flowData)); + JobberException exception = + Assertions.assertThrows(JobberException.class, () -> DataUtils.getBusiness(flowData)); Assertions.assertEquals(10000000, exception.getCode()); } @@ -166,8 +166,8 @@ void testGetAgentId() { @DisplayName("getContextData失败:列表为空") void testGetContextDataWithEmptyInputList() { List> flowData = new ArrayList<>(); - JobberException exception = Assertions.assertThrows(JobberException.class, - () -> DataUtils.getContextData(flowData)); + JobberException exception = + Assertions.assertThrows(JobberException.class, () -> DataUtils.getContextData(flowData)); Assertions.assertEquals(10000000, exception.getCode()); } @@ -176,8 +176,8 @@ void testGetContextDataWithEmptyInputList() { void testGetContextDataWithoutContextData() { List> flowData = new ArrayList<>(); flowData.add(new HashMap<>()); - JobberException exception = Assertions.assertThrows(JobberException.class, - () -> DataUtils.getContextData(flowData)); + JobberException exception = + Assertions.assertThrows(JobberException.class, () -> DataUtils.getContextData(flowData)); Assertions.assertEquals(10000000, exception.getCode()); } @@ -215,8 +215,8 @@ void testGetFlowTraceId() { flowData.add(MapBuilder.get(() -> new HashMap()) .put(AippConst.CONTEXT_DATA_KEY, contextData) .build()); - JobberException exception = Assertions.assertThrows(JobberException.class, - () -> DataUtils.getFlowTraceId(flowData)); + JobberException exception = + Assertions.assertThrows(JobberException.class, () -> DataUtils.getFlowTraceId(flowData)); Assertions.assertEquals(10000006, exception.getCode()); traceIds.add("11"); String traceId = Assertions.assertDoesNotThrow(() -> DataUtils.getFlowTraceId(flowData)); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AppImExportUtilTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AppImExportUtilTest.java index 5fbcee4019..c24e6fafb3 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AppImExportUtilTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/AppImExportUtilTest.java @@ -7,6 +7,7 @@ package modelengine.fit.jober.aipp.util; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -17,6 +18,7 @@ import modelengine.fit.jober.aipp.domain.AppBuilderFlowGraph; import modelengine.fit.jober.aipp.domain.AppBuilderForm; import modelengine.fit.jober.aipp.domain.AppBuilderFormProperty; +import modelengine.fit.jober.aipp.domains.appversion.AppVersion; import modelengine.fit.jober.aipp.dto.export.AppExportApp; import modelengine.fit.jober.aipp.dto.export.AppExportConfig; import modelengine.fit.jober.aipp.dto.export.AppExportConfigProperty; @@ -25,12 +27,13 @@ import modelengine.fit.jober.aipp.dto.export.AppExportFormProperty; import modelengine.fit.jober.aipp.service.StoreServiceImplTest; import modelengine.fitframework.util.IoUtils; +import modelengine.fitframework.util.ObjectUtils; import modelengine.fitframework.util.StringUtils; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; @@ -39,10 +42,12 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.stream.Stream; /** * 应用导入导出工具类单元测试。 @@ -65,36 +70,43 @@ void testConvertToAppExportApp() { .build(); AppExportApp exportApp = AppImExportUtil.convertToAppExportApp(mockApp); - assertThat(exportApp).extracting(AppExportApp::getName, AppExportApp::getTenantId, AppExportApp::getType, + assertThat(exportApp).extracting(AppExportApp::getName, + AppExportApp::getTenantId, + AppExportApp::getType, AppExportApp::getVersion).containsExactly("testApp", "tenant123", "testType", "1.0.0"); } @Test @DisplayName("测试将 AppBuilderConfig 转换为 AppExportConfig") void testConvertToAppExportConfig() { - List mockConfigProperties = Collections.singletonList( - AppBuilderConfigProperty.builder().id("123").nodeId("456").formPropertyId("789").build()); - List mockFormProperties = Collections.singletonList(AppBuilderFormProperty.builder() + List mockConfigProperties = + Collections.singletonList(AppBuilderConfigProperty.builder() + .id("123") + .nodeId("456") + .formPropertyId("789") + .build()); + AppBuilderFormProperty mockFormProperty = AppBuilderFormProperty.builder() .id("789") .formId("369") .name("test") .dataType("String") .defaultValue("test") - .build()); + .build(); AppBuilderForm mockForm = mock(AppBuilderForm.class); - AppBuilderApp mockApp = mock(AppBuilderApp.class); + AppVersion mockApp = mock(AppVersion.class); AppBuilderConfig mockConfig = AppBuilderConfig.builder() .id("258") .form(mockForm) .configProperties(mockConfigProperties) - .app(mockApp) + .appVersion(mockApp) .build(); - when(mockApp.getFormProperties()).thenReturn(mockFormProperties); + when(mockApp.getFormProperty(anyString())).thenReturn(mockFormProperty); AppExportConfig appExportConfig = AppImExportUtil.convertToAppExportConfig(mockConfig); assertThat(appExportConfig.getConfigProperties()).hasSize(1) .map(AppExportConfigProperty::getFormProperty) .element(0) - .extracting(AppExportFormProperty::getName, AppExportFormProperty::getDataType, + .extracting(AppExportFormProperty::getName, + AppExportFormProperty::getDataType, AppExportFormProperty::getDefaultValue) .containsExactly("test", "String", "\"test\""); } @@ -102,11 +114,8 @@ void testConvertToAppExportConfig() { @Test @DisplayName("测试将 AppBuilderFlowGraph 转换为 AppExportFlowGraph") void testConvertToAppExportFlowGraph() { - AppBuilderFlowGraph mockFlowGraph = AppBuilderFlowGraph.builder() - .id("123") - .name("testFlowGraph") - .appearance("testAppearance") - .build(); + AppBuilderFlowGraph mockFlowGraph = + AppBuilderFlowGraph.builder().id("123").name("testFlowGraph").appearance("testAppearance").build(); AppExportFlowGraph exportFlowGraph = AppImExportUtil.convertToAppExportFlowGraph(mockFlowGraph); assertThat(exportFlowGraph).extracting(AppExportFlowGraph::getName, AppExportFlowGraph::getAppearance) @@ -142,11 +151,13 @@ void testConvertToAppBuilderApp() throws IOException { String config = IoUtils.content(classLoader, IMPORT_CONFIG); AppExportDto configDto = JsonUtils.parseObject(config, AppExportDto.class); configDto.getApp().getAttributes().put("name", configDto.getApp().getName()); - OperationContext context = new OperationContext("123", "admin", null, "123456", null, "admin", "127.0.0.1", - "windows", "cn_zh"); + OperationContext context = + new OperationContext("123", "admin", null, "123456", null, "admin", "127.0.0.1", "windows", "cn_zh"); AppBuilderApp app = AppImExportUtil.convertToAppBuilderApp(configDto, context); - assertThat(app).extracting(AppBuilderApp::getType, AppBuilderApp::getState, AppBuilderApp::getVersion, + assertThat(app).extracting(AppBuilderApp::getType, + AppBuilderApp::getState, + AppBuilderApp::getVersion, AppBuilderApp::getTenantId).containsExactly("app", "importing", "1.0.2", "123"); assertThat(app.getAttributes()).containsEntry("icon", ""); assertThat(app.getConfig().getConfigProperties()).hasSize(10); @@ -166,12 +177,12 @@ void testReadAllBytes() throws IOException { @Test @DisplayName("测试保存头像文件") - @Disabled("无法跑通,需要仔细审视") void testSaveIconFile() throws IOException { - String iconContent = new String( - Base64.getEncoder().encode("This is an icon png.".getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8); - String iconUrl = AppImExportUtil.saveIconFile(iconContent, "png", "123", "/api/jober"); + String iconContent = + new String(Base64.getEncoder().encode("This is an icon png.".getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8); + String tempDir = System.getProperty("java.io.tmpdir"); + String iconUrl = AppImExportUtil.saveIconFile(iconContent, "png", "123", "/api/jober", tempDir); assertThat(iconUrl).startsWith("/api/jober/v1/api/123"); String iconPath = AippFileUtils.getFileNameFromIcon(iconUrl); @@ -186,13 +197,14 @@ void testSaveIconFile() throws IOException { @Test @DisplayName("测试拒绝非法的图像保存") void testIllegalIconSave() { - String iconContent = new String( - Base64.getEncoder().encode("This is an icon png.".getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8); - String iconUrl = AppImExportUtil.saveIconFile(iconContent, "txt", "123", "/api/jober"); + String iconContent = + new String(Base64.getEncoder().encode("This is an icon png.".getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8); + String tempDir = System.getProperty("java.io.tmpdir"); + String iconUrl = AppImExportUtil.saveIconFile(iconContent, "txt", "123", "/api/jober", tempDir); assertThat(iconUrl).isEqualTo(StringUtils.EMPTY); - iconUrl = AppImExportUtil.saveIconFile(iconContent, "../../../.jpg", "123", "/api/jober"); + iconUrl = AppImExportUtil.saveIconFile(iconContent, ".jpg", "123", "/api/jober", tempDir); assertThat(iconUrl).isEqualTo(StringUtils.EMPTY); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/ConvertUtilsTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/ConvertUtilsTest.java index f5b48633a8..7389d731c0 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/ConvertUtilsTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/ConvertUtilsTest.java @@ -83,11 +83,8 @@ void testConvertToAippDtoFromAppBuilderApp() { @Test @DisplayName("toAippCreate") void testToAippCreate() { - AippCreateDto dto = AippCreateDto.builder() - .aippId("aippId") - .toolUniqueName("uniqueName") - .version("1.0.0") - .build(); + AippCreateDto dto = + AippCreateDto.builder().aippId("aippId").toolUniqueName("uniqueName").version("1.0.0").build(); AippCreate aippCreate = Assertions.assertDoesNotThrow(() -> ConvertUtils.toAippCreate(dto)); diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/FormUtilsTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/FormUtilsTest.java index 23a222ed38..172990acc6 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/FormUtilsTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/FormUtilsTest.java @@ -60,8 +60,8 @@ void testBuildFormData() { Map businessData = new HashMap<>(); businessData.put("model", "Qianwen"); String parentId = "parentId"; - Map form = Assertions.assertDoesNotThrow( - () -> FormUtils.buildFormData(businessData, inputForm, parentId)); + Map form = + Assertions.assertDoesNotThrow(() -> FormUtils.buildFormData(businessData, inputForm, parentId)); Assertions.assertEquals(3, form.size()); Assertions.assertTrue(form.containsKey(AippConst.FORM_APPEARANCE_KEY)); } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/HttpUtilTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/HttpUtilTest.java index 6afef16fa6..e760ea795f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/HttpUtilTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/HttpUtilTest.java @@ -16,6 +16,7 @@ import modelengine.fit.http.client.HttpClassicClientRequest; import modelengine.fit.http.client.HttpClassicClientResponse; import modelengine.fit.http.entity.TextEntity; +import modelengine.fit.jober.aipp.util.HttpUtils; import org.junit.jupiter.api.Test; diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/JsonUtilsTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/JsonUtilsTest.java index 634cbd9363..7095518f1d 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/JsonUtilsTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/JsonUtilsTest.java @@ -39,8 +39,8 @@ void testParseObject() { @DisplayName("测试String转Map失败") void testParseObjectFailed() { String str = "{\"hello\": \"world\""; - AippJsonDecodeException aippJsonDecodeException = Assertions.assertThrows(AippJsonDecodeException.class, - () -> JsonUtils.parseObject(str)); + AippJsonDecodeException aippJsonDecodeException = + Assertions.assertThrows(AippJsonDecodeException.class, () -> JsonUtils.parseObject(str)); Assertions.assertEquals(90002900, aippJsonDecodeException.getCode()); } @@ -84,7 +84,6 @@ void testParseArrayClazzFailed() { @Data private static class TestEntity { private String hello; - private Integer start; } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/TemplateUtilsTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/TemplateUtilsTest.java index 31ae418187..2c8718f815 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/TemplateUtilsTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/util/TemplateUtilsTest.java @@ -28,7 +28,9 @@ public class TemplateUtilsTest { void testAppBuilderAppConvertToAppTemplate() { AppBuilderApp testApp = AppBuilderApp.builder().id("123456789").updateBy("jade").version("1.1.1").build(); AppTemplate template = TemplateUtils.convertToAppTemplate(testApp); - assertThat(template).extracting(AppTemplate::getId, AppTemplate::getLike, AppTemplate::getUpdateBy, + assertThat(template).extracting(AppTemplate::getId, + AppTemplate::getLike, + AppTemplate::getUpdateBy, AppTemplate::getVersion).containsExactly("123456789", 0L, null, "1.0.0"); } @@ -49,7 +51,9 @@ void testAppTemplateConvertToTemplateInfoDto() { void testAppTemplateConvertToAppBuilderApp() { AppTemplate testTemplate = AppTemplate.builder().id("123456789").version("1.1.1").build(); AppBuilderApp app = TemplateUtils.convertToAppBuilderApp(testTemplate); - assertThat(app).extracting(AppBuilderApp::getId, AppBuilderApp::getVersion, AppBuilderApp::getState, + assertThat(app).extracting(AppBuilderApp::getId, + AppBuilderApp::getVersion, + AppBuilderApp::getState, AppBuilderApp::getType).containsExactly("123456789", "1.0.0", "inactive", "template"); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/validation/FormFileValidatorImplTest.java b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/validation/FormFileValidatorImplTest.java index 357e73fe43..d473ff2539 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/validation/FormFileValidatorImplTest.java +++ b/app-builder/jane/plugins/aipp-plugin/src/test/java/modelengine/fit/jober/aipp/validation/FormFileValidatorImplTest.java @@ -38,23 +38,14 @@ @ExtendWith(MockitoExtension.class) public class FormFileValidatorImplTest { private static final String SCHEMA = "schema"; - private static final String PARAMETERS = "parameters"; - private static final String TYPE = "type"; - private static final String OBJECT = "object"; - private static final String REQUIRED = "required"; - private static final String PROPERTIES = "properties"; - private static final String RETURN = "return"; - private static final String ORDER = "order"; - private static final String ITEMS = "items"; - private static final String ENUM = "enum"; private FormFileValidator formFileValidator; @@ -109,8 +100,8 @@ void testValidateSchemaSuccess() { void testValidateSchemaFailedWithoutSchemaKey() { Map config = new HashMap(); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002109, ex.getCode()); } @@ -123,8 +114,8 @@ void testValidateSchemaFailedWithoutParametersKey() { schema.put(PARAMETERS, parameters); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002110, ex.getCode()); } @@ -135,8 +126,8 @@ void testValidateSchemaFailedWithParametersWithoutTypeKey() { Map schema = new HashMap(); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002109, ex.getCode()); } @@ -151,8 +142,8 @@ void testValidateSchemaFailedWithoutRequiredKey() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002111, ex.getCode()); } @@ -168,8 +159,8 @@ void testValidateSchemaFailedWithoutPropertiesKey() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002111, ex.getCode()); } @@ -187,8 +178,8 @@ void testValidateSchemaFailedWithoutReturnsKey() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002109, ex.getCode()); } @@ -210,8 +201,8 @@ void testValidateSchemaFailedWhenpropertyWithoutTypeKey() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002112, ex.getCode()); } @@ -234,8 +225,8 @@ void testValidateSchemaFailedWhenpropertyTypeKeyIsNotString() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002113, ex.getCode()); } @@ -258,8 +249,8 @@ void testValidateSchemaFailedWhenpropertyTypeKeyIllegal() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002114, ex.getCode()); } @@ -284,8 +275,8 @@ void testValidateSchemaFailedWhenArrayParameterWithoutTypeKey() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002112, ex.getCode()); } @@ -311,8 +302,8 @@ void testValidateSchemaFailedWhenArrayParameterTypeKeyIsNotString() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002115, ex.getCode()); } @@ -338,8 +329,8 @@ void testValidateSchemaWhenArrayTupleError() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002112, ex.getCode()); } @@ -365,8 +356,8 @@ void testValidateSchemaWhenArrayTupleWithEnumError() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002116, ex.getCode()); } @@ -389,8 +380,8 @@ void testValidateSchemaFailedWhenRequiredMoreParam() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002117, ex.getCode()); } @@ -413,8 +404,8 @@ void testValidateSchemaFailedWhenRequiredExtraParam() { schema.put(ORDER, new ArrayList<>()); config.put(SCHEMA, schema); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateSchema(config)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateSchema(config)); Assertions.assertEquals(90002118, ex.getCode()); } @@ -457,8 +448,8 @@ void testValidateComponentFailedWithEmptyBuild() throws IOException { File directory = Files.createTempDirectory("temp").toFile(); directory.deleteOnExit(); - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateComponent(directory)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateComponent(directory)); Assertions.assertEquals(90002119, ex.getCode()); } @@ -474,8 +465,8 @@ void testValidateComponentFailedWithoutIndexFile() throws IOException { writer.write("Test JS"); } - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateComponent(directory)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateComponent(directory)); Assertions.assertEquals(90002120, ex.getCode()); } @@ -501,8 +492,8 @@ void testValidateComponentFailedWithIllegalFile() throws IOException { writer.write("test"); } - AippException ex = Assertions.assertThrows(AippException.class, - () -> this.formFileValidator.validateComponent(directory)); + AippException ex = + Assertions.assertThrows(AippException.class, () -> this.formFileValidator.validateComponent(directory)); Assertions.assertEquals(90002124, ex.getCode()); } } diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/appearance.txt b/app-builder/jane/plugins/aipp-plugin/src/test/resources/appearance.txt new file mode 100644 index 0000000000..0d685084e5 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/appearance.txt @@ -0,0 +1,868 @@ +{ + "id": "51cc0d62b4534560b4fe5e9cea5679b8", + "type": "jadeFlowGraph", + "pages": [ + { + "x": 297.46031746031747, + "y": 121.01190476190482, + "id": "elsa-page:tvp1s6", + "bold": false, + "mode": "configuration", + "text": "newFlowPage", + "type": "jadeFlowPage", + "dirty": false, + "index": 0, + "width": 1600, + "hAlign": "left", + "height": 800, + "isPage": true, + "italic": false, + "scaleX": 0.8, + "scaleY": 0.8, + "shapes": [ + { + "x": 189.1071428571429, + "y": 383, + "id": "jade2zanyx", + "pad": 0, + "bold": false, + "text": "", + "type": "jadeEvent", + "dirty": true, + "index": -100, + "textX": 0, + "textY": 0, + "width": 86.642857142857, + "hAlign": "center", + "height": 50.857142857142776, + "italic": false, + "margin": 20, + "toShape": "jade0pg2ag", + "endArrow": true, + "hideText": true, + "lineMode": { + "type": "auto_curve" + }, + "runnable": true, + "allowLink": false, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "fromShape": "jade6qm5eg", + "lineWidth": 2, + "namespace": "flowable", + "beginArrow": false, + "borderColor": "#B1B1B7", + "borderWidth": 1, + "curvePoint1": { + "x": 0, + "y": 0 + }, + "curvePoint2": { + "x": 0, + "y": 0 + }, + "brokenPoints": [ + { + "x": 50, + "y": 0 + }, + { + "x": 50, + "y": 80 + } + ], + "endArrowSize": 4, + "arrowEndPoint": { + "x": 96, + "y": 80, + "direction": { + "ax": "x", + "key": "W", + "color": "whitesmoke", + "value": "W", + "cursor": "ew-resize", + "vector": -1 + } + }, + "endArrowEmpty": false, + "beginArrowSize": 4, + "arrowBeginPoint": { + "x": 0, + "y": 0, + "direction": { + "ax": "x", + "key": "E", + "color": "whitesmoke", + "value": "E", + "cursor": "ew-resize", + "vector": 1 + } + }, + "beginArrowEmpty": false, + "definedToConnector": "W", + "mouseInBorderColor": "#B1B1B7", + "allowSwitchLineMode": false, + "definedFromConnector": "E" + }, + { + "x": 635.7499999999999, + "y": 433.8571428571428, + "id": "jade5c5urs", + "pad": 0, + "bold": false, + "text": "", + "type": "jadeEvent", + "dirty": true, + "index": -99, + "textX": 0, + "textY": 0, + "width": 83.39285714285722, + "hAlign": "center", + "height": -12.571428571428555, + "italic": false, + "margin": 20, + "toShape": "jadewdnjbq", + "endArrow": true, + "hideText": true, + "lineMode": { + "type": "auto_curve" + }, + "runnable": true, + "allowLink": false, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "fromShape": "jade0pg2ag", + "lineWidth": 2, + "namespace": "flowable", + "beginArrow": false, + "borderColor": "#B1B1B7", + "borderWidth": 1, + "curvePoint1": { + "x": 0, + "y": 0 + }, + "curvePoint2": { + "x": 0, + "y": 0 + }, + "brokenPoints": [ + { + "x": 50, + "y": 0 + }, + { + "x": 50, + "y": 80 + } + ], + "endArrowSize": 4, + "arrowEndPoint": { + "x": 96, + "y": 80, + "direction": { + "ax": "x", + "key": "W", + "color": "whitesmoke", + "value": "W", + "cursor": "ew-resize", + "vector": -1 + } + }, + "endArrowEmpty": false, + "beginArrowSize": 4, + "arrowBeginPoint": { + "x": 0, + "y": 0, + "direction": { + "ax": "x", + "key": "E", + "color": "whitesmoke", + "value": "E", + "cursor": "ew-resize", + "vector": 1 + } + }, + "beginArrowEmpty": false, + "definedToConnector": "W", + "mouseInBorderColor": "#B1B1B7", + "allowSwitchLineMode": false, + "definedFromConnector": "E" + }, + { + "x": 1079.142857142857, + "y": 421.2857142857142, + "id": "jade1p0cdu", + "pad": 0, + "bold": false, + "text": "", + "type": "jadeEvent", + "dirty": true, + "index": -98, + "textX": 0, + "textY": 0, + "width": 90.32142857142776, + "hAlign": "center", + "height": 31.142857142857054, + "italic": false, + "margin": 20, + "toShape": "jadesoux5i", + "endArrow": true, + "hideText": true, + "lineMode": { + "type": "auto_curve" + }, + "runnable": true, + "allowLink": false, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "fromShape": "jadewdnjbq", + "lineWidth": 2, + "namespace": "flowable", + "beginArrow": false, + "borderColor": "#B1B1B7", + "borderWidth": 1, + "curvePoint1": { + "x": 0, + "y": 0 + }, + "curvePoint2": { + "x": 0, + "y": 0 + }, + "brokenPoints": [ + { + "x": 50, + "y": 0 + }, + { + "x": 50, + "y": 80 + } + ], + "endArrowSize": 4, + "arrowEndPoint": { + "x": 96, + "y": 80, + "direction": { + "ax": "x", + "key": "W", + "color": "whitesmoke", + "value": "W", + "cursor": "ew-resize", + "vector": -1 + } + }, + "endArrowEmpty": false, + "beginArrowSize": 4, + "arrowBeginPoint": { + "x": 0, + "y": 0, + "direction": { + "ax": "x", + "key": "E", + "color": "whitesmoke", + "value": "E", + "cursor": "ew-resize", + "vector": 1 + } + }, + "beginArrowEmpty": false, + "definedToConnector": "W", + "mouseInBorderColor": "#B1B1B7", + "allowSwitchLineMode": false, + "definedFromConnector": "E" + }, + { + "x": -170.8928571428571, + "y": 32.5, + "id": "jade6qm5eg", + "pad": 6, + "bold": false, + "text": "开始", + "type": "startNodeStart", + "dirty": true, + "index": 103, + "width": 360, + "height": 701, + "italic": false, + "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", + "disabled": false, + "flowMeta": { + "inputParams": [ + { + "id": "91138f09-b635-43df-95c6-1fe3d1745829", + "from": "Expand", + "name": "input", + "type": "Object", + "value": [ + { + "id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", + "from": "Input", + "name": "Question", + "type": "String", + "value": "", + "description": "这是用户输入的问题。", + "disableModifiable": true + } + ], + "config": [ + { + "allowAdd": true + } + ] + }, + { + "id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", + "from": "Expand", + "name": "memory", + "type": "Object", + "value": [ + { + "id": "a7675623-7fc7-468c-8910-e73c70e5e468", + "from": "Input", + "name": "memorySwitch", + "type": "Boolean", + "value": true + }, + { + "id": "cee9a31b-781c-4835-a616-ceed73be22f2", + "from": "Input", + "name": "type", + "type": "String", + "value": "ByConversationTurn" + }, + { + "id": "69592622-4291-409d-9d65-9faea83db657", + "from": "Input", + "name": "value", + "type": "Integer", + "value": "3" + } + ] + } + ], + "triggerMode": "auto" + }, + "hideText": true, + "moveable": true, + "runnable": true, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "dashWidth": 0, + "deletable": false, + "namespace": "flowable", + "autoHeight": true, + "emphasized": false, + "rotateAble": false, + "borderColor": "rgba(28,31,35,.08)", + "borderWidth": 1, + "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", + "runningTask": 0, + "triggerMode": "auto", + "warningTask": 0, + "cornerRadius": 8, + "outlineColor": "rgba(74,147,255,0.12)", + "outlineWidth": 10, + "completedTask": 0, + "componentName": "startComponent", + "focusBackColor": "white", + "sourcePlatform": "official", + "enableAnimation": false, + "mouseInBorderColor": "rgba(28,31,35,.08)" + }, + { + "x": 275.7499999999999, + "y": 192.3571428571428, + "id": "jade0pg2ag", + "pad": 6, + "bold": false, + "text": "普通检索", + "type": "retrievalNodeState", + "dirty": true, + "index": 104, + "width": 360, + "height": 483, + "italic": false, + "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", + "disabled": false, + "flowMeta": { + "jober": { + "name": "", + "type": "general_jober", + "fitables": [ + "modelengine.fit.jober.aipp.fitable.NaiveRAGComponent" + ], + "converter": { + "type": "mapping_converter", + "entity": { + "inputParams": [ + { + "id": "query_0ab55575-f21d-4b19-9676-57fcb4b0b783", + "from": "Reference", + "name": "query", + "type": "String", + "value": [ + "Question" + ], + "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", + "referenceKey": "Question", + "referenceNode": "jade6qm5eg" + }, + { + "id": "knowledge_01c41edd-a22b-4289-b1cf-8db835833261", + "from": "Expand", + "name": "knowledge", + "type": "Array", + "value": [ + { + "id": "55f8e6eb-dab5-435f-94ea-18108eaba982", + "from": "Expand", + "type": "Object", + "value": [] + } + ] + }, + { + "id": "maximum_2da115cd-c1ce-485f-ba98-b4c995f3d6ff", + "from": "Input", + "name": "maximum", + "type": "Integer", + "value": 3 + } + ], + "outputParams": [ + { + "id": "output_cd5cbe89-0d9f-4cf1-9e09-afb325576b84", + "from": "Expand", + "name": "output", + "type": "Object", + "value": [ + { + "id": "5c9c6535-c127-445a-862a-966cf1083929", + "from": "Input", + "name": "retrievalOutput", + "type": "String", + "value": "String" + } + ] + } + ] + } + } + }, + "joberFilter": { + "type": "MINIMUM_SIZE_FILTER", + "threshold": 1 + }, + "triggerMode": "auto" + }, + "hideText": true, + "moveable": true, + "runnable": true, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "dashWidth": 0, + "namespace": "flowable", + "autoHeight": true, + "emphasized": false, + "rotateAble": false, + "borderColor": "rgba(28,31,35,.08)", + "borderWidth": 1, + "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", + "runningTask": 0, + "triggerMode": "auto", + "warningTask": 0, + "cornerRadius": 8, + "outlineColor": "rgba(74,147,255,0.12)", + "outlineWidth": 10, + "completedTask": 0, + "componentName": "retrievalComponent", + "focusBackColor": "white", + "sourcePlatform": "official", + "enableAnimation": false, + "mouseInBorderColor": "rgba(28,31,35,.08)" + }, + { + "x": 719.1428571428571, + "y": -53.21428571428578, + "id": "jadewdnjbq", + "pad": 6, + "bold": false, + "text": "大模型", + "type": "llmNodeState", + "dirty": true, + "index": 105, + "width": 360, + "height": 949, + "italic": false, + "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", + "disabled": false, + "flowMeta": { + "jober": { + "name": "", + "type": "general_jober", + "isAsync": "true", + "fitables": [ + "modelengine.fit.jober.aipp.fitable.LLMComponent" + ], + "converter": { + "type": "mapping_converter", + "entity": { + "inputParams": [ + { + "id": "6c414e75-971e-403a-b2b1-c6850f128cc4", + "from": "Input", + "name": "model", + "type": "String", + "value": "Qwen1.5-32B-Chat" + }, + { + "id": "db5fdafa-4cbf-44ba-9cca-8a98f1f771f4", + "from": "Input", + "name": "temperature", + "type": "Number", + "value": "0.3" + }, + { + "id": "88f74d78-4711-4f81-a2e7-74d0034c5e88", + "from": "Expand", + "name": "prompt", + "type": "Object", + "value": [ + { + "id": "35a710cf-1b79-4523-b16f-b50878d677fe", + "from": "Input", + "name": "template", + "type": "String", + "value": "请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n上下文信息:\n\n{{knowledge}}\n\n问题:{{query}}" + }, + { + "id": "38fb27a1-71f4-4fcc-87d5-9d8a880bc04d", + "from": "Expand", + "name": "variables", + "type": "Object", + "value": [ + { + "id": "aeba7823-8d14-4750-9723-55265ae71c4e", + "from": "Reference", + "name": "knowledge", + "type": "String", + "value": [ + "output", + "retrievalOutput" + ], + "referenceId": "5c9c6535-c127-445a-862a-966cf1083929", + "referenceKey": "retrievalOutput", + "referenceNode": "jade0pg2ag" + }, + { + "id": "eee66922-4304-4209-89fc-b13ffa101630", + "from": "Reference", + "name": "query", + "type": "String", + "value": [ + "Question" + ], + "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", + "referenceKey": "Question", + "referenceNode": "jade6qm5eg" + } + ] + } + ] + }, + { + "id": "a6865419-867c-4bfb-855c-f5c1876c965a", + "from": "Expand", + "name": "tools", + "type": "Array", + "value": [] + }, + { + "id": "308e2023-a8e9-486e-9784-8680addbb786", + "from": "Expand", + "name": "workflows", + "type": "Array", + "value": [] + }, + { + "id": "68f92923-d5da-42ce-8478-d7ac7d90664e", + "from": "Input", + "name": "systemPrompt", + "type": "String", + "value": "" + } + ], + "outputParams": [ + { + "id": "95d84d67-3198-415e-a63c-bc9a2da8d821", + "from": "Expand", + "name": "output", + "type": "Object", + "value": [ + { + "id": "272c927a-9e25-48b6-a921-6a8ab20267a4", + "from": "Input", + "name": "llmOutput", + "type": "String", + "value": "", + "description": "" + } + ] + } + ] + } + } + }, + "joberFilter": { + "type": "MINIMUM_SIZE_FILTER", + "threshold": 1 + }, + "triggerMode": "auto" + }, + "hideText": true, + "moveable": true, + "runnable": true, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "dashWidth": 0, + "namespace": "flowable", + "autoHeight": true, + "emphasized": false, + "rotateAble": false, + "borderColor": "rgba(28,31,35,.08)", + "borderWidth": 1, + "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", + "runningTask": 0, + "triggerMode": "auto", + "warningTask": 0, + "cornerRadius": 8, + "outlineColor": "rgba(74,147,255,0.12)", + "outlineWidth": 10, + "completedTask": 0, + "componentName": "llmComponent", + "focusBackColor": "white", + "sourcePlatform": "official", + "enableAnimation": false, + "mouseInBorderColor": "rgba(28,31,35,.08)" + }, + { + "x": 1169.4642857142849, + "y": 306.4285714285713, + "id": "jadesoux5i", + "pad": 6, + "bold": false, + "text": "结束", + "type": "endNodeEnd", + "dirty": true, + "index": 106, + "width": 360, + "height": 292, + "italic": false, + "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", + "disabled": false, + "flowMeta": { + "callback": { + "name": "通知回调", + "type": "general_callback", + "fitables": [ + "modelengine.fit.jober.aipp.fitable.AippFlowEndCallback" + ], + "converter": { + "type": "mapping_converter", + "entity": { + "inputParams": [ + { + "id": "ffad80c2-3f60-4d57-93b2-c2362a5dab9c", + "from": "Reference", + "name": "finalOutput", + "type": "String", + "value": [ + "output", + "llmOutput" + ], + "referenceId": "272c927a-9e25-48b6-a921-6a8ab20267a4", + "referenceKey": "llmOutput", + "referenceNode": "jadewdnjbq" + } + ], + "outputParams": [ + {} + ] + } + } + }, + "triggerMode": "auto" + }, + "hideText": true, + "moveable": true, + "runnable": true, + "backColor": "white", + "container": "elsa-page:tvp1s6", + "dashWidth": 0, + "deletable": false, + "namespace": "flowable", + "autoHeight": true, + "emphasized": false, + "rotateAble": false, + "borderColor": "rgba(28,31,35,.08)", + "borderWidth": 1, + "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", + "runningTask": 0, + "triggerMode": "auto", + "warningTask": 0, + "cornerRadius": 8, + "outlineColor": "rgba(74,147,255,0.12)", + "outlineWidth": 10, + "completedTask": 0, + "componentName": "endComponent", + "focusBackColor": "white", + "sourcePlatform": "official", + "enableAnimation": false, + "mouseInBorderColor": "rgba(28,31,35,.08)" + } + ], + "vAlign": "top", + "itemPad": [ + 0, + 0, + 0, + 0 + ], + "division": -1, + "dockMode": "none", + "fontFace": "arial", + "fontSize": 18, + "hideText": true, + "moveable": true, + "shapesAs": {}, + "backColor": "#fbfbfc", + "container": "elsa-page:tvp1s6", + "dockAlign": "top", + "fontColor": "#ECD0A7", + "fontStyle": "normal", + "itemSpace": 5, + "namespace": "jadeFlow", + "fontWeight": "bold", + "itemScroll": { + "x": 0, + "y": 0 + }, + "borderColor": "white", + "focusBackColor": "#fbfbfc" + } + ], + "title": "51cc0d62b4534560b4fe5e9cea5679b8", + "source": "elsa", + "tenant": "31f20efc7e0848deab6a6bc10fc3021e", + "setting": { + "pad": 10, + "tag": {}, + "code": "", + "pDock": "none", + "hAlign": "center", + "margin": 25, + "shadow": "", + "shared": false, + "vAlign": "top", + "itemPad": [ + 5, + 5, + 5, + 5 + ], + "visible": true, + "autoText": false, + "dockMode": "none", + "dragable": true, + "editable": true, + "fontFace": "arial", + "fontSize": 12, + "infoType": { + "name": "none", + "next": "INFORMATION" + }, + "moveable": true, + "priority": 0, + "allowLink": true, + "autoWidth": false, + "backAlpha": 0.15, + "backColor": "whitesmoke", + "dashWidth": 0, + "deletable": true, + "fontColor": "steelblue", + "fontStyle": "normal", + "headColor": "steelblue", + "lineWidth": 2, + "underline": false, + "autoHeight": false, + "emphasized": false, + "fontWeight": "lighter", + "itemScroll": { + "x": 0, + "y": 0 + }, + "lineHeight": 1.5, + "resizeable": true, + "rotateAble": true, + "scrollLock": { + "x": false, + "y": false + }, + "selectable": true, + "shadowData": "2px 2px 4px", + "borderColor": "#047bfc", + "borderWidth": 1, + "bulletSpeed": 1, + "focusMargin": 0, + "focusShadow": "", + "globalAlpha": 1, + "outstanding": false, + "bulletedList": false, + "cornerRadius": 4, + "enableSocial": true, + "mouseInColor": "orange", + "numberedList": false, + "outlineColor": "rgba(74,147,255,0.12)", + "outlineWidth": 10, + "rotateDegree": 0, + "captionhAlign": "center", + "strikethrough": false, + "focusBackColor": "whitesmoke", + "focusFontColor": "darkorange", + "progressStatus": { + "name": "NONE", + "next": "UNKNOWN", + "color": "gray" + }, + "showedProgress": false, + "captionfontFace": "arial black", + "captionfontSize": 14, + "enableAnimation": false, + "progressPercent": 0.65, + "captionfontColor": "whitesmoke", + "captionfontStyle": "normal", + "focusBorderColor": "#047bfc", + "focusBorderWidth": 1, + "mouseInBackColor": "whitesmoke", + "mouseInFontColor": "orange", + "captionfontWeight": "lighter", + "captionlineHeight": 1, + "mouseInBorderColor": "#047bfc" + }, + "flowMeta": { + "callback": { + "name": "通知回调", + "type": "general_callback", + "fitables": [ + "modelengine.fit.jober.fitable.FlowInfoCallback" + ] + }, + "enableOutputScope": true, + "exceptionFitables": [ + "modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", + "modelengine.fit.jober.fitable.FlowInfoException" + ] + }, + "enableText": false +} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/export-data.txt b/app-builder/jane/plugins/aipp-plugin/src/test/resources/export-data.txt new file mode 100644 index 0000000000..ebdb0b7dd2 --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/export-data.txt @@ -0,0 +1 @@ +{"version":"1.0.1","app":{"name":"测试一下","tenantId":"31f20efc7e0848deab6a6bc10fc3021e","type":"app","appBuiltType":"basic","version":"1.0.0","attributes":{"icon":"","app_type":"4db152b24f94473ab683b1acbfe3c865","greeting":"","description":""},"appCategory":"chatbot","appType":"通用"},"config":{"form":{"id":"b8986770a6ffef44bbf2a9f26d6fc1bc","name":"llm_config","appearance":{},"type":"component","version":"1.0.0","formSuiteId":"5bdd730775ae40a395d3a4b560fb3b21"},"configProperties":[{"nodeId":"jadewdnjbq","formProperty":{"name":"basic","dataType":"String","defaultValue":"null","from":"none","group":"null","description":"基础编排","index":0}},{"nodeId":"jadewdnjbq","formProperty":{"name":"ability","dataType":"String","defaultValue":"null","from":"none","group":"basic","description":"能力配置","index":1}},{"nodeId":"jade6qm5eg","formProperty":{"name":"model","dataType":"List","defaultValue":"[\"jadewdnjbq\"]","from":"graph","group":"ability","description":"大模型","index":2}},{"nodeId":"jadewdnjbq","formProperty":{"name":"tools","dataType":"List>","defaultValue":"[[\"jadewdnjbq\",\"tools\"],[\"jadewdnjbq\",\"workflows\"]]","from":"graph","group":"ability","description":"工具","index":3}},{"nodeId":"jadewdnjbq","formProperty":{"name":"knowledge","dataType":"List","defaultValue":"[\"jade0pg2ag\",\"knowledgeRepos\"]","from":"graph","group":"ability","description":"知识库","index":4}},{"nodeId":null,"formProperty":{"name":"chat","dataType":"String","defaultValue":"null","from":"none","group":"basic","description":"聊天设置","index":5}},{"nodeId":"jade0pg2ag","formProperty":{"name":"memory","dataType":"List","defaultValue":"[\"jade6qm5eg\",\"memory\"]","from":"graph","group":"chat","description":"多轮对话","index":7}},{"nodeId":"jadewdnjbq","formProperty":{"name":"opening","dataType":"String","defaultValue":"\"Hi~我是综合面试助手,想问点什么呢?\"","from":"input","group":"chat","description":"开场白","index":6}},{"nodeId":"jadewdnjbq","formProperty":{"name":"inspiration","dataType":"object","defaultValue":"{\"category\":[{\"title\":\"root\",\"id\":\"root\",\"children\":[]}],\"inspirations\":[]}","from":"input","group":"chat","description":"创意灵感","index":9}},{"nodeId":"jadewdnjbq","formProperty":{"name":"recommend","dataType":"object","defaultValue":"{\"showRecommend\":false,\"list\":[]}","from":"input","group":"chat","description":"猜你想问","index":8}}]},"flowGraph":{"name":"LLM模板","appearance":"{\"id\": \"908168f59dbe49a287fd232877ba1a1a\", \"type\": \"jadeFlowGraph\", \"pages\": [{\"x\": 297.46031746031749, \"y\": 121.01190476190482, \"id\": \"elsa-page:tvp1s6\", \"bold\": false, \"mode\": \"configuration\", \"text\": \"newFlowPage\", \"type\": \"jadeFlowPage\", \"dirty\": true, \"index\": 0, \"width\": 1600, \"hAlign\": \"left\", \"height\": 800, \"isPage\": true, \"italic\": false, \"scaleX\": 0.8, \"scaleY\": 0.8, \"shapes\": [{\"x\": -170.8928571428571, \"y\": 32.5, \"id\": \"jade6qm5eg\", \"pad\": 6, \"bold\": false, \"text\": \"开始\", \"type\": \"startNodeStart\", \"dirty\": true, \"index\": 100, \"width\": 360, \"height\": 554, \"italic\": false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", \"flowMeta\": {\"inputParams\": [{\"id\": \"91138f09-b635-43df-95c6-1fe3d1745829\", \"from\": \"Expand\", \"name\": \"input\", \"type\": \"Object\", \"value\": [{\"id\": \"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb\", \"from\": \"Input\", \"name\": \"Question\", \"type\": \"String\", \"value\": \"\", \"description\": \"这是用户输入的问题。\", \"disableModifiable\": true}], \"config\": [{\"allowAdd\": false}]}, {\"id\": \"4a770dc6-e3c9-475d-84c7-48dacc74a5b6\", \"from\": \"Expand\", \"name\": \"memory\", \"type\": \"Object\", \"value\": [{\"id\": \"a7675623-7fc7-468c-8910-e73c70e5e468\", \"from\": \"Input\", \"name\": \"memorySwitch\", \"type\": \"Boolean\", \"value\": true}, {\"id\": \"cee9a31b-781c-4835-a616-ceed73be22f2\", \"from\": \"Input\", \"name\": \"type\", \"type\": \"String\", \"value\": \"ByConversationTurn\"}, {\"id\": \"69592622-4291-409d-9d65-9faea83db657\", \"from\": \"Input\", \"name\": \"value\", \"type\": \"Integer\", \"value\": \"3\"}]}], \"triggerMode\": \"auto\"}, \"hideText\": true, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"dashWidth\": 0, \"deletable\": false, \"namespace\": \"flowable\", \"autoHeight\": true, \"rotateAble\": false, \"borderColor\": \"rgba(28,31,35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": \"auto\", \"warningTask\": 0, \"cornerRadius\": 8, \"outlineColor\": \"rgba(74,147,255,0.12)\", \"outlineWidth\": 10, \"completedTask\": 0, \"componentName\": \"startComponent\", \"focusBackColor\": \"white\", \"sourcePlatform\": \"official\", \"enableAnimation\": false, \"mouseInBorderColor\": \"rgba(28,31,35,.08)\"}, {\"x\": 275.7499999999999, \"y\": 192.3571428571428, \"id\": \"jade0pg2ag\", \"pad\": 6, \"bold\": false, \"text\": \"知识检索\", \"type\": \"knowledgeRetrievalNodeState\", \"dirty\": true, \"index\": 101, \"width\": 360, \"height\": 459, \"italic\": false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", \"flowMeta\": {\"jober\": {\"name\": \"\", \"type\": \"STORE_JOBER\", \"entity\": {\"params\": [{\"name\": \"query\"}, {\"name\": \"knowledgeRepos\"}, {\"name\": \"option\"}], \"return\": {\"type\": \"object\"}, \"uniqueName\": \"25887d76-e358-4121-800c-31eb3390fdbd\"}, \"fitables\": [], \"converter\": {\"type\": \"mapping_converter\", \"entity\": {\"inputParams\": [{\"id\": \"query_0ab55575-f21d-4b19-9676-57fcb4b0b783\", \"from\": \"Reference\", \"name\": \"query\", \"type\": \"Object\", \"value\": [\"Question\"], \"referenceId\": \"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb\", \"referenceKey\": \"Question\", \"referenceNode\": \"jade6qm5eg\"}, {\"id\": \"knowledgeRepos_01c41edd-a22b-4289-b1cf-8db835833261\", \"from\": \"Expand\", \"name\": \"knowledgeRepos\", \"type\": \"Array\", \"value\": []}, {\"id\": \"option_68f4b238-8e3c-42e1-9795-5a2c8593c22c\", \"from\": \"Expand\", \"name\": \"option\", \"type\": \"Object\", \"value\": [{\"id\": \"03ce03b6-8d00-4fb0-bf32-85b2b40aaaee\", \"from\": \"Expand\", \"name\": \"indexType\", \"type\": \"Object\", \"value\": [{\"id\": \"543ff920-9927-48c6-bb65-cb1b97944b65\", \"from\": \"input\", \"name\": \"type\", \"type\": \"String\", \"value\": \"semantic\"}, {\"id\": \"03d471a3-d4da-48a3-bbf8-d05bf06374e1\", \"from\": \"input\", \"name\": \"name\", \"type\": \"String\", \"value\": \"语义检索\"}, {\"id\": \"647d0884-5539-4618-922e-af12b08d1d34\", \"from\": \"input\", \"name\": \"description\", \"type\": \"String\", \"value\": \"基于文本的含义检索出最相关的内容\"}]}, {\"id\": \"a6a619c8-eef0-4bfa-9e12-a8994edfb83f\", \"from\": \"input\", \"name\": \"similarityThreshold\", \"type\": \"Number\", \"value\": 0.5}, {\"id\": \"c809934a-9023-48dc-a2c8-e33274ab7101\", \"from\": \"Expand\", \"name\": \"referenceLimit\", \"type\": \"Object\", \"value\": [{\"id\": \"369ad79e-397f-417c-b671-c4f714734693\", \"from\": \"input\", \"name\": \"type\", \"type\": \"String\", \"value\": \"topK\"}, {\"id\": \"31071b92-7d9f-443b-930c-3329d05671f5\", \"from\": \"input\", \"name\": \"value\", \"type\": \"Integer\", \"value\": 3}]}, {\"id\": \"e45abef0-e276-42ea-832a-87e4a2aeb2be\", \"from\": \"Expand\", \"name\": \"rerankParam\", \"type\": \"Object\", \"value\": [{\"id\": \"5b737124-7de9-45b9-bff3-87c6b4d817e8\", \"from\": \"input\", \"name\": \"enableRerank\", \"type\": \"Boolean\", \"value\": false}]}]}], \"outputParams\": [{\"id\": \"output_cd5cbe89-0d9f-4cf1-9e09-afb325576b84\", \"from\": \"Expand\", \"name\": \"output\", \"type\": \"Array\", \"value\": []}]}}}, \"joberFilter\": {\"type\": \"MINIMUM_SIZE_FILTER\", \"threshold\": 1}, \"triggerMode\": \"auto\"}, \"hideText\": true, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"dashWidth\": 0, \"namespace\": \"flowable\", \"autoHeight\": true, \"rotateAble\": false, \"borderColor\": \"rgba(28,31,35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": \"auto\", \"warningTask\": 0, \"cornerRadius\": 8, \"outlineColor\": \"rgba(74,147,255,0.12)\", \"outlineWidth\": 10, \"completedTask\": 0, \"componentName\": \"knowledgeRetrievalComponent\", \"focusBackColor\": \"white\", \"sourcePlatform\": \"official\", \"enableAnimation\": false, \"mouseInBorderColor\": \"rgba(28,31,35,.08)\"}, {\"x\": 719.1428571428571, \"y\": -30.71403761434209, \"id\": \"jadewdnjbq\", \"pad\": 6, \"bold\": false, \"text\": \"大模型\", \"type\": \"llmNodeState\", \"dirty\": true, \"index\": 102, \"width\": 360, \"height\": 787, \"italic\": false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", \"flowMeta\": {\"jober\": {\"name\": \"\", \"type\": \"general_jober\", \"isAsync\": \"true\", \"fitables\": [\"modelengine.fit.jober.aipp.fitable.LLMComponent\"], \"converter\": {\"type\": \"mapping_converter\", \"entity\": {\"inputParams\": [{\"id\": \"31ba235d-1b26-4780-a7a7-32eca9500919\", \"from\": \"Expand\", \"name\": \"accessInfo\", \"type\": \"Object\", \"value\": [{\"id\": \"83653b54-dd04-4da9-957d-adb7c2728632\", \"from\": \"Input\", \"name\": \"serviceName\", \"type\": \"String\", \"value\": \"Qwen1.5-32B-Chat\"}, {\"id\": \"dd588a17-a69c-40c0-859a-d9930202a148\", \"from\": \"Input\", \"name\": \"tag\", \"type\": \"String\", \"value\": \"INTERNAL\"}]}, {\"id\": \"6c414e75-971e-403a-b2b1-c6850f128cc4\", \"from\": \"Input\", \"name\": \"model\", \"type\": \"String\", \"value\": \"Qwen1.5-32B-Chat\"}, {\"id\": \"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4\", \"from\": \"Input\", \"name\": \"temperature\", \"type\": \"Number\", \"value\": \"0.3\"}, {\"id\": \"88f74d78-4711-4f81-a2e7-74d0034c5e88\", \"from\": \"Expand\", \"name\": \"prompt\", \"type\": \"Object\", \"value\": [{\"id\": \"35a710cf-1b79-4523-b16f-b50878d677fe\", \"from\": \"Input\", \"name\": \"template\", \"type\": \"String\", \"value\": \"请按照以下步骤生成您的回复:\\n1. 递归地将问题分解为更小的问题。\\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\\n3. 使用所选信息生成回复草稿。\\n4. 删除回复草稿中的重复内容。\\n5. 在调整后生成最终答案,以提高准确性和相关性。\\n6. 请注意,只需要回复最终答案。\\n-------------------------------------\\n上下文信息:\\n\\n{{knowledge}}\\n\\n问题:{{query}}\"}, {\"id\": \"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d\", \"from\": \"Expand\", \"name\": \"variables\", \"type\": \"Object\", \"value\": [{\"id\": \"aeba7823-8d14-4750-9723-55265ae71c4e\", \"from\": \"Reference\", \"name\": \"knowledge\", \"type\": \"String\", \"value\": [\"output\"], \"referenceId\": \"output_cd5cbe89-0d9f-4cf1-9e09-afb325576b84\", \"referenceKey\": \"output\", \"referenceNode\": \"jade0pg2ag\"}, {\"id\": \"eee66922-4304-4209-89fc-b13ffa101630\", \"from\": \"Reference\", \"name\": \"query\", \"type\": \"String\", \"value\": [\"Question\"], \"referenceId\": \"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb\", \"referenceKey\": \"Question\", \"referenceNode\": \"jade6qm5eg\"}]}]}, {\"id\": \"a6865419-867c-4bfb-855c-f5c1876c965a\", \"from\": \"Expand\", \"name\": \"tools\", \"type\": \"Array\", \"value\": []}, {\"id\": \"308e2023-a8e9-486e-9784-8680addbb786\", \"from\": \"Expand\", \"name\": \"workflows\", \"type\": \"Array\", \"value\": []}, {\"id\": \"68f92923-d5da-42ce-8478-d7ac7d90664e\", \"from\": \"Input\", \"name\": \"systemPrompt\", \"type\": \"String\", \"value\": \"\"}, {\"id\": \"78baad16-173f-4d70-a7cd-d1a2abc2f0d1\", \"from\": \"input\", \"name\": \"enableLog\", \"type\": \"Boolean\", \"value\": true}], \"outputParams\": [{\"id\": \"95d84d67-3198-415e-a63c-bc9a2da8d821\", \"from\": \"Expand\", \"name\": \"output\", \"type\": \"Object\", \"value\": [{\"id\": \"272c927a-9e25-48b6-a921-6a8ab20267a4\", \"from\": \"Input\", \"name\": \"llmOutput\", \"type\": \"String\", \"value\": \"\", \"description\": \"\"}]}]}}}, \"joberFilter\": {\"type\": \"MINIMUM_SIZE_FILTER\", \"threshold\": 1}, \"triggerMode\": \"auto\"}, \"hideText\": true, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"dashWidth\": 0, \"namespace\": \"flowable\", \"autoHeight\": true, \"rotateAble\": false, \"borderColor\": \"rgba(28,31,35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": \"auto\", \"warningTask\": 0, \"cornerRadius\": 8, \"outlineColor\": \"rgba(74,147,255,0.12)\", \"outlineWidth\": 10, \"completedTask\": 0, \"componentName\": \"llmComponent\", \"focusBackColor\": \"white\", \"sourcePlatform\": \"official\", \"enableAnimation\": false, \"mouseInBorderColor\": \"rgba(28,31,35,.08)\"}, {\"x\": 1169.4642857142849, \"y\": 306.4285714285713, \"id\": \"jadesoux5i\", \"pad\": 6, \"bold\": false, \"text\": \"结束\", \"type\": \"endNodeEnd\", \"dirty\": true, \"index\": 104, \"width\": 360, \"height\": 212, \"italic\": false, \"shadow\": \"0 2px 4px 0 rgba(0,0,0,.1)\", \"flowMeta\": {\"callback\": {\"name\": \"通知回调\", \"type\": \"general_callback\", \"fitables\": [\"modelengine.fit.jober.aipp.fitable.AippFlowEndCallback\"], \"converter\": {\"type\": \"mapping_converter\", \"entity\": {\"inputParams\": [{\"id\": \"ffad80c2-3f60-4d57-93b2-c2362a5dab9c\", \"from\": \"Reference\", \"name\": \"finalOutput\", \"type\": \"String\", \"value\": [\"output\", \"llmOutput\"], \"referenceId\": \"272c927a-9e25-48b6-a921-6a8ab20267a4\", \"referenceKey\": \"llmOutput\", \"referenceNode\": \"jadewdnjbq\"}], \"outputParams\": [{}]}}}, \"triggerMode\": \"auto\"}, \"hideText\": true, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"dashWidth\": 0, \"deletable\": true, \"namespace\": \"flowable\", \"autoHeight\": true, \"rotateAble\": false, \"borderColor\": \"rgba(28,31,35,.08)\", \"borderWidth\": 1, \"focusShadow\": \"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)\", \"runningTask\": 0, \"triggerMode\": \"auto\", \"warningTask\": 0, \"cornerRadius\": 8, \"outlineColor\": \"rgba(74,147,255,0.12)\", \"outlineWidth\": 10, \"completedTask\": 0, \"componentName\": \"endComponent\", \"focusBackColor\": \"white\", \"sourcePlatform\": \"official\", \"enableAnimation\": false, \"mouseInBorderColor\": \"rgba(28,31,35,.08)\"}, {\"x\": 189.1071428571429, \"y\": 309.5, \"id\": \"jade2zanyx\", \"pad\": 0, \"bold\": false, \"text\": \"\", \"type\": \"jadeEvent\", \"dirty\": true, \"index\": -95, \"textX\": 0, \"textY\": 0, \"width\": 86.642857142857, \"hAlign\": \"center\", \"height\": 112.35714285714278, \"italic\": false, \"margin\": 20, \"toShape\": \"jade0pg2ag\", \"endArrow\": true, \"hideText\": true, \"lineMode\": {\"type\": \"auto_curve\"}, \"allowLink\": false, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"fromShape\": \"jade6qm5eg\", \"lineWidth\": 2, \"namespace\": \"flowable\", \"beginArrow\": false, \"borderColor\": \"#B1B1B7\", \"borderWidth\": 1, \"curvePoint1\": {\"x\": 0, \"y\": 0}, \"curvePoint2\": {\"x\": 0, \"y\": 0}, \"brokenPoints\": [{\"x\": 50, \"y\": 0}, {\"x\": 50, \"y\": 80}], \"endArrowSize\": 4, \"arrowEndPoint\": {\"x\": 96, \"y\": 80, \"direction\": {\"ax\": \"x\", \"key\": \"W\", \"color\": \"whitesmoke\", \"value\": \"W\", \"cursor\": \"ew-resize\", \"vector\": -1}}, \"endArrowEmpty\": false, \"beginArrowSize\": 4, \"arrowBeginPoint\": {\"x\": 0, \"y\": 0, \"direction\": {\"ax\": \"x\", \"key\": \"E\", \"color\": \"whitesmoke\", \"value\": \"E\", \"cursor\": \"ew-resize\", \"vector\": 1}}, \"beginArrowEmpty\": false, \"definedToConnector\": \"W\", \"mouseInBorderColor\": \"#B1B1B7\", \"allowSwitchLineMode\": false, \"definedFromConnector\": \"E\"}, {\"x\": 635.7499999999999, \"y\": 421.8571428571428, \"id\": \"jade5c5urs\", \"pad\": 0, \"bold\": false, \"text\": \"\", \"type\": \"jadeEvent\", \"dirty\": true, \"index\": -94, \"textX\": 0, \"textY\": 0, \"width\": 83.39285714285723, \"hAlign\": \"center\", \"height\": -81.57142857142856, \"italic\": false, \"margin\": 20, \"toShape\": \"jadewdnjbq\", \"endArrow\": true, \"hideText\": true, \"lineMode\": {\"type\": \"auto_curve\"}, \"allowLink\": false, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"fromShape\": \"jade0pg2ag\", \"lineWidth\": 2, \"namespace\": \"flowable\", \"beginArrow\": false, \"borderColor\": \"#B1B1B7\", \"borderWidth\": 1, \"curvePoint1\": {\"x\": 0, \"y\": 0}, \"curvePoint2\": {\"x\": 0, \"y\": 0}, \"brokenPoints\": [{\"x\": 50, \"y\": 0}, {\"x\": 50, \"y\": 80}], \"endArrowSize\": 4, \"arrowEndPoint\": {\"x\": 96, \"y\": 80, \"direction\": {\"ax\": \"x\", \"key\": \"W\", \"color\": \"whitesmoke\", \"value\": \"W\", \"cursor\": \"ew-resize\", \"vector\": -1}}, \"endArrowEmpty\": false, \"beginArrowSize\": 4, \"arrowBeginPoint\": {\"x\": 0, \"y\": 0, \"direction\": {\"ax\": \"x\", \"key\": \"E\", \"color\": \"whitesmoke\", \"value\": \"E\", \"cursor\": \"ew-resize\", \"vector\": 1}}, \"beginArrowEmpty\": false, \"definedToConnector\": \"W\", \"mouseInBorderColor\": \"#B1B1B7\", \"allowSwitchLineMode\": false, \"definedFromConnector\": \"E\"}, {\"x\": 1079.142857142857, \"y\": 340.2857142857142, \"id\": \"jade1p0cdu\", \"pad\": 0, \"bold\": false, \"text\": \"\", \"type\": \"jadeEvent\", \"dirty\": true, \"index\": -94, \"textX\": 0, \"textY\": 0, \"width\": 90.32142857142776, \"hAlign\": \"center\", \"height\": 72.14285714285706, \"italic\": false, \"margin\": 20, \"toShape\": \"jadesoux5i\", \"endArrow\": true, \"hideText\": true, \"lineMode\": {\"type\": \"auto_curve\"}, \"allowLink\": false, \"backColor\": \"white\", \"container\": \"elsa-page:tvp1s6\", \"fromShape\": \"jadewdnjbq\", \"lineWidth\": 2, \"namespace\": \"flowable\", \"beginArrow\": false, \"borderColor\": \"#B1B1B7\", \"borderWidth\": 1, \"curvePoint1\": {\"x\": 0, \"y\": 0}, \"curvePoint2\": {\"x\": 0, \"y\": 0}, \"brokenPoints\": [{\"x\": 50, \"y\": 0}, {\"x\": 50, \"y\": 80}], \"endArrowSize\": 4, \"arrowEndPoint\": {\"x\": 96, \"y\": 80, \"direction\": {\"ax\": \"x\", \"key\": \"W\", \"color\": \"whitesmoke\", \"value\": \"W\", \"cursor\": \"ew-resize\", \"vector\": -1}}, \"endArrowEmpty\": false, \"beginArrowSize\": 4, \"arrowBeginPoint\": {\"x\": 0, \"y\": 0, \"direction\": {\"ax\": \"x\", \"key\": \"E\", \"color\": \"whitesmoke\", \"value\": \"E\", \"cursor\": \"ew-resize\", \"vector\": 1}}, \"beginArrowEmpty\": false, \"definedToConnector\": \"W\", \"mouseInBorderColor\": \"#B1B1B7\", \"allowSwitchLineMode\": false, \"definedFromConnector\": \"E\"}], \"vAlign\": \"top\", \"itemPad\": [0, 0, 0, 0], \"division\": -1, \"dockMode\": \"none\", \"fontFace\": \"arial\", \"fontSize\": 18, \"hideText\": true, \"moveable\": true, \"shapesAs\": {}, \"backColor\": \"#fbfbfc\", \"container\": \"elsa-page:tvp1s6\", \"dockAlign\": \"top\", \"fontColor\": \"#ECD0A7\", \"fontStyle\": \"normal\", \"itemSpace\": 5, \"namespace\": \"jadeFlow\", \"fontWeight\": \"bold\", \"itemScroll\": {\"x\": 0, \"y\": 0}, \"borderColor\": \"white\", \"focusBackColor\": \"#fbfbfc\"}], \"title\": \"908168f59dbe49a287fd232877ba1a1a\", \"source\": \"elsa\", \"tenant\": \"default\", \"setting\": {\"pad\": 10, \"tag\": {}, \"code\": \"\", \"pDock\": \"none\", \"hAlign\": \"center\", \"margin\": 25, \"shadow\": \"\", \"shared\": false, \"vAlign\": \"top\", \"itemPad\": [5, 5, 5, 5], \"visible\": true, \"autoText\": false, \"dockMode\": \"none\", \"dragable\": true, \"editable\": true, \"fontFace\": \"arial\", \"fontSize\": 12, \"infoType\": {\"name\": \"none\", \"next\": \"INFORMATION\"}, \"moveable\": true, \"priority\": 0, \"allowLink\": true, \"autoWidth\": false, \"backAlpha\": 0.15, \"backColor\": \"whitesmoke\", \"dashWidth\": 0, \"deletable\": true, \"fontColor\": \"steelblue\", \"fontStyle\": \"normal\", \"headColor\": \"steelblue\", \"lineWidth\": 2, \"underline\": false, \"autoHeight\": false, \"emphasized\": false, \"fontWeight\": \"lighter\", \"itemScroll\": {\"x\": 0, \"y\": 0}, \"lineHeight\": 1.5, \"resizeable\": true, \"rotateAble\": true, \"scrollLock\": {\"x\": false, \"y\": false}, \"selectable\": true, \"shadowData\": \"2px 2px 4px\", \"borderColor\": \"#047bfc\", \"borderWidth\": 1, \"bulletSpeed\": 1, \"focusMargin\": 0, \"focusShadow\": \"\", \"globalAlpha\": 1, \"outstanding\": false, \"bulletedList\": false, \"cornerRadius\": 4, \"enableSocial\": true, \"mouseInColor\": \"orange\", \"numberedList\": false, \"outlineColor\": \"rgba(74,147,255,0.12)\", \"outlineWidth\": 10, \"rotateDegree\": 0, \"captionhAlign\": \"center\", \"strikethrough\": false, \"focusBackColor\": \"whitesmoke\", \"focusFontColor\": \"darkorange\", \"progressStatus\": {\"name\": \"NONE\", \"next\": \"UNKNOWN\", \"color\": \"gray\"}, \"showedProgress\": false, \"captionfontFace\": \"arial black\", \"captionfontSize\": 14, \"enableAnimation\": false, \"progressPercent\": 0.65, \"captionfontColor\": \"whitesmoke\", \"captionfontStyle\": \"normal\", \"focusBorderColor\": \"#047bfc\", \"focusBorderWidth\": 1, \"mouseInBackColor\": \"whitesmoke\", \"mouseInFontColor\": \"orange\", \"captionfontWeight\": \"lighter\", \"captionlineHeight\": 1, \"mouseInBorderColor\": \"#047bfc\"}, \"flowMeta\": {\"callback\": {\"name\": \"通知回调\", \"type\": \"general_callback\", \"fitables\": [\"modelengine.fit.jober.fitable.FlowInfoCallback\"]}, \"enableOutputScope\": true, \"exceptionFitables\": [\"modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler\", \"modelengine.fit.jober.fitable.FlowInfoException\"]}, \"enableText\": false}"}} \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql index 4dbd79dd8c..0ded4919d9 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/clear_table.sql @@ -16,4 +16,10 @@ truncate table app_builder_form; truncate table app_builder_form_property; -truncate table app_template; \ No newline at end of file +truncate table app_template; + +truncate table app_builder_runtime_info; + +truncate table t_chat_session; + +truncate table t_chat_session_task_instance_wide_relationship; \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql index 6858e0a7cf..8ae2db0095 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/init.sql @@ -145,4 +145,62 @@ create table if not exists app_template ( update_by varchar(64) not null, update_at timestamp(6) not null default CURRENT_TIMESTAMP, is_deleted int2 not null default 0 +); + +create table if not exists t_chat_session ( + chat_id VARCHAR(32) NOT NULL DEFAULT NULL, + app_id VARCHAR(32) NULL DEFAULT NULL, + app_version VARCHAR(32) NULL DEFAULT NULL, + name VARCHAR(2000) NULL DEFAULT NULL, + attributes JSON NULL DEFAULT NULL, + create_at TIMESTAMP(6) NULL DEFAULT NULL, + create_by VARCHAR(32) NULL DEFAULT NULL, + update_at TIMESTAMP(6) NULL DEFAULT NULL, + update_by VARCHAR(32) NULL DEFAULT NULL, + status INT4 NULL DEFAULT NULL + ); + +CREATE TABLE IF NOT EXISTS t_chat_session_task_instance_wide_relationship ( + msg_id VARCHAR(32) NOT NULL DEFAULT NULL, + chat_id VARCHAR(32) NULL DEFAULT NULL, + task_instance_wide_id VARCHAR(32) NULL DEFAULT NULL, + create_at TIMESTAMP(6) NULL DEFAULT NULL, + create_by VARCHAR NULL DEFAULT NULL, + update_at TIMESTAMP(6) NULL DEFAULT NULL, + update_by VARCHAR NULL DEFAULT NULL + ); + +ALTER TABLE app_builder_app ADD COLUMN IF NOT EXISTS app_suite_id VARCHAR(32) NULL; +-- UPDATE app_builder_app SET app_suite_id = task.template_id from task where task.attributes->>'app_id' = app_builder_app.id; +ALTER TABLE app_builder_app + ADD COLUMN IF NOT EXISTS is_active bool NULL DEFAULT false; +ALTER TABLE app_builder_app + ADD COLUMN IF NOT EXISTS status varchar(16) NULL; +ALTER TABLE app_builder_app + ADD COLUMN IF NOT EXISTS unique_name varchar(64) NULL; +ALTER TABLE app_builder_app + ADD COLUMN IF NOT EXISTS publish_at timestamp(6) NULL; +ALTER TABLE app_builder_app + ADD COLUMN IF NOT EXISTS app_id varchar(64) NULL; + + +create table if not exists app_builder_runtime_info +( + id BIGSERIAL primary key, + trace_id varchar(64) not null, + flow_definition_id varchar(64) not null, + instance_id varchar(64) not null, + node_id varchar(64) not null, + node_type varchar(32) not null, + start_time bigint not null, + end_time bigint not null, + status varchar(32), + published smallint not null, + error_msg text, + next_position_id varchar(64), + parameters json not null DEFAULT '[]', + create_by varchar(64), + create_at timestamp not null default current_timestamp, + update_by varchar(64), + update_at timestamp not null default current_timestamp ); \ No newline at end of file diff --git a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/insert.sql b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/insert.sql index 7d60265e51..a930ddc495 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/insert.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/test/resources/sql/insert.sql @@ -145,4 +145,6 @@ INSERT INTO `app_template` VALUES ('c74bddff85a24dcdacf35e2533b52206', '莲花 INSERT INTO `app_template` VALUES ('83fc18bb7e6b4e03a9b04108809a4c51', 'fyz模板', 'workflow', 'chatbot', '{"icon": "", "app_type": "编程开发", "greeting": "null", "description": "这是简介"}' FORMAT JSON, 'finance', 0, 0, 0, '1.0.0', 'f10dd75828c343ada8fa6547152ba776', '5a71fefe81d349cdafe4c2c141e80e2c', 'Jade', '2025-01-08 14:50:57.376782', 'Jade', '2025-01-08 14:50:57.376782', 0); INSERT INTO `app_template` VALUES ('637b1bf75ae347979ee84311fdb74733', '熊猫工具流模板', 'workflow', 'workflow', '{"icon": "", "app_type": "编程开发", "greeting": null, "description": "这是简介"}' FORMAT JSON, 'default', 0, 0, 0, '1.0.0', '951b195266fa48a0ae04214e7470d635', 'fc883834e5c44a2bb15f62de66afc24d', 'Jade', '2025-01-08 15:03:47.608057', 'Jade', '2025-01-08 15:03:47.608057', 0); INSERT INTO `app_template` VALUES ('43e666af34d540cbb8e99744eb8e41c0', '熊猫工具流模板(带头像)', 'workflow', 'workflow', '{"icon": "/aippApi/31f20efc7e0848deab6a6bc10fc3021e/file?filePath=/var/share/3ba23e1c-fa88-4540-a05d-1bacc589af6b.png&fileName=8ae8e0f0-55b9-4f0e-a628-27ab64cb5b42.png", "app_type": "编程开发", "greeting": null, "description": "这是简介"}' FORMAT JSON, 'default', 0, 0, 0, '1.0.0', '52188bc0648949e6b0bcaa8fe8877513', '52f76fae305c41a491d66ae2bd3b9ab1', 'Jade', '2025-01-08 15:05:16.33932', 'Jade', '2025-01-08 15:05:16.33932', 0); -INSERT INTO `app_template` VALUES ('2ef1f428c38f48fba1f86a6bb9d7fdb1', 'postman创建的模板', 'workflow', 'chatbot', '{"icon": "", "app_type": "编程开发", "greeting": "null", "description": "这是简介"}' FORMAT JSON, 'postman', 0, 0, 0, '1.0.0', '2a5c9c0c6b6c4bc8862279f25b5e9286', '851a116857434cfd8c88c995bcae9336', 'Jade', '2025-01-07 18:57:53.897046', 'Jade', '2025-01-07 18:57:53.897046', 0); \ No newline at end of file +INSERT INTO `app_template` VALUES ('2ef1f428c38f48fba1f86a6bb9d7fdb1', 'postman创建的模板', 'workflow', 'chatbot', '{"icon": "", "app_type": "编程开发", "greeting": "null", "description": "这是简介"}' FORMAT JSON, 'postman', 0, 0, 0, '1.0.0', '2a5c9c0c6b6c4bc8862279f25b5e9286', '851a116857434cfd8c88c995bcae9336', 'Jade', '2025-01-07 18:57:53.897046', 'Jade', '2025-01-07 18:57:53.897046', 0); + +INSERT INTO `t_chat_session` (chat_id, app_id, app_version, name, attributes, create_at, create_by, update_at, update_by, status) VALUES ('003f0cd8dcfb4aca88af34d8f85750d2', 'ebc5afee8bd94c5eb5d36da049396864', '1.0.0', '1+1', '{"state": "inactive", "instId": "90e195284b7a4abe9579b990034c1edc", "aipp_id": "924b72b7cc5f441eb894de69a95893e1"}', '2024-11-12 14:08:53.498', 'tester 12345678', '2024-11-12 14:08:53.498', 'tester 12345678', 0); \ No newline at end of file diff --git a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/constants/AippConst.java b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/constants/AippConst.java index 0a42c1d894..be4b4e17bc 100644 --- a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/constants/AippConst.java +++ b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/constants/AippConst.java @@ -721,6 +721,16 @@ public class AippConst { */ public static final String BS_CHAT_ID = "chatId"; + /** + * business中的原始会话的id的key,用于应用之间@的场景. + */ + public static final String BS_ORIGIN_CHAT_ID = "originChatId"; + + /** + * business中的原始应用版本的id的key,用于应用之间@的场景. + */ + public static final String BS_ORIGIN_APP_ID = "originAppId"; + /** * aippId */ @@ -763,12 +773,8 @@ public class AippConst { /** * aipp initial static meta items */ - public static final List STATIC_META_ITEMS = - Collections.unmodifiableList(Arrays.asList(new FormMetaItem(INST_NAME_KEY, - "meta实例名称", - "TEXT", - STRING_LEN, - null), + public static final List STATIC_META_ITEMS = Collections.unmodifiableList(Arrays.asList( + new FormMetaItem(INST_NAME_KEY, "meta实例名称", "TEXT", STRING_LEN, null), new FormMetaItem(INST_CREATOR_KEY, "创建人", "TEXT", STRING_LEN, null), new FormMetaItem(INST_CREATE_TIME_KEY, "创建时间", "DATETIME", null, null), new FormMetaItem(INST_MODIFY_BY_KEY, "更新人", "TEXT", STRING_LEN, null), diff --git a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppDto.java b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppDto.java index 680ad8d824..842c8c6416 100644 --- a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppDto.java +++ b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/dto/AppBuilderAppDto.java @@ -11,6 +11,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import modelengine.fitframework.annotation.Property; +import modelengine.fitframework.util.StringUtils; import java.time.LocalDateTime; import java.util.List; @@ -74,4 +75,41 @@ public class AppBuilderAppDto { @Property(description = "aipp 发布更新日志") private String publishedUpdateLog; + + + /** + * 获取描述. + * + * @return {@link String} 描述信息. + */ + public String getDescription() { + return String.valueOf(this.attributes.getOrDefault("description", StringUtils.EMPTY)); + } + + /** + * 获取图标. + * + * @return {@link String} 图标信息. + */ + public String getIcon() { + return String.valueOf(this.attributes.getOrDefault("icon", StringUtils.EMPTY)); + } + + /** + * 设置图标. + * + * @param icon 图标. + */ + public void setIcon(String icon) { + this.attributes.put("icon", icon); + } + + /** + * 设置描述. + * + * @param description 描述信息. + */ + public void setDescription(String description) { + this.attributes.put("description", description); + } } diff --git a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/genericable/AppBuilderAppService.java b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/genericable/AppBuilderAppService.java index 0f7ba1b4c3..ee2fa3a68f 100644 --- a/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/genericable/AppBuilderAppService.java +++ b/app-builder/jane/services/aipp-genericable/src/main/java/modelengine/fit/jober/aipp/genericable/AppBuilderAppService.java @@ -67,11 +67,11 @@ public interface AppBuilderAppService { AppBuilderAppDto queryLatestOrchestration(String appId, OperationContext context); /** - * 查询 app 最新可编排的版本。 + * 查询 app 最新发布的版本。 * * @param appId 表示 app 唯一标识的 {@link String}。 * @param context 表示操作者上下文的 {@link OperationContext}。 - * @return 表示查询到的 app 最新可编排的版本的 {@link AppBuilderAppDto}。 + * @return 表示查询到的 app 最新发布的版本的 {@link AppBuilderAppDto}。 */ @Genericable(id = "modelengine.fit.jober.aipp.service.app.query.latest.published") AippCreate queryLatestPublished(String appId, OperationContext context); diff --git a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/entity/AippInstLog.java b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/entity/AippInstLog.java index ec4fe6ce3b..8ee061c322 100644 --- a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/entity/AippInstLog.java +++ b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/entity/AippInstLog.java @@ -6,6 +6,8 @@ package modelengine.fit.jober.aipp.entity; +import com.alibaba.fastjson.JSON; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -49,9 +51,6 @@ public class AippInstLog { @Property(description = "aipp version") private String version; - /** - * {@link modelengine.fit.jober.aipp.enums.AippTypeEnum}字面值 - */ @Property(description = "aipp type(NORMAL/PREVIEW)") private String aippType; @@ -63,4 +62,13 @@ public class AippInstLog { @Property(description = "历史数据类型 {@link AippInstLogType}") private String logType; + + /** + * 获取 question 数据. + * + * @return {@link String} 问题. + */ + public String getQuestion() { + return JSON.parseObject(this.getLogData()).getString("msg"); + } } diff --git a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/events/AppCreatingEvent.java b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/events/AppCreatingEvent.java index 9cf8a65df1..41df04a67c 100644 --- a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/events/AppCreatingEvent.java +++ b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/events/AppCreatingEvent.java @@ -1,6 +1,8 @@ -/* - * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. - */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ package modelengine.fit.jober.aipp.events; diff --git a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/service/AippRunTimeService.java b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/service/AippRunTimeService.java index 0134d9e1ae..9318e8aac0 100644 --- a/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/service/AippRunTimeService.java +++ b/app-builder/jane/services/aipp-service/src/main/java/modelengine/fit/jober/aipp/service/AippRunTimeService.java @@ -7,12 +7,12 @@ package modelengine.fit.jober.aipp.service; import modelengine.fit.jane.common.entity.OperationContext; -import modelengine.fit.jober.aipp.dto.AippInstanceCreateDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppDto; -import modelengine.fit.jober.aipp.dto.AppBuilderAppStartDto; +import modelengine.fit.jober.aipp.common.PageResponse; +import modelengine.fit.jober.aipp.condition.AippInstanceQueryCondition; +import modelengine.fit.jober.aipp.condition.PaginationCondition; +import modelengine.fit.jober.aipp.dto.AippInstanceDto; import modelengine.fit.jober.aipp.vo.MetaVo; import modelengine.fitframework.flowable.Choir; -import modelengine.fitframework.model.Tuple; import java.util.Map; @@ -34,19 +34,6 @@ public interface AippRunTimeService { */ String createAippInstance(String aippId, String version, Map initContext, OperationContext context); - /** - * 指定版本启动一个App - * - * @param appId appId - * @param question 对话提问 - * @param businessData 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @param context 操作上下文 - * @param isDebug 是否是调试对话 - * @return 实例id - */ - Tuple createInstanceByApp(String appId, String question, Map businessData, - OperationContext context, boolean isDebug); - /** * 查询app对应的metaVo * @@ -69,17 +56,6 @@ Tuple createInstanceByApp(String appId, String question, Map bus Choir startFlowWithUserSelectMemory(String metaInstId, Map initContext, OperationContext context, boolean isDebug); - /** - * 启动一个最新版本的Aipp - * - * @param context 操作上下文 - * @param aippId aippId - * @param initContext 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @return 实例响应 - */ - AippInstanceCreateDto createAippInstanceLatest(String aippId, Map initContext, - OperationContext context); - /** * 删除应用实例 * @@ -133,15 +109,4 @@ Choir resumeAndUpdateAippInstance(String instanceId, Map * @param context 操作上下文 */ void terminateAllPreviewInstances(String aippId, String versionId, boolean isDeleteLog, OperationContext context); - - /** - * 启动对话实例 - * - * @param appDto app信息 - * @param initContext 表示start表单填充的内容,作为流程初始化的businessData。 例如 图片url, 文本输入, prompt - * @param context 操作上下文 - * @return 实例id - */ - AppBuilderAppStartDto startInstance(AppBuilderAppDto appDto, Map initContext, - OperationContext context); } diff --git a/app-builder/jane/task-new/pom.xml b/app-builder/jane/task-new/pom.xml index d0c7beb5c9..bdfe476a91 100644 --- a/app-builder/jane/task-new/pom.xml +++ b/app-builder/jane/task-new/pom.xml @@ -63,6 +63,10 @@ modelengine.fit.jane aipp-service + + org.fitframework.extension + fit-schedule + diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java index 873db4fe9d..7726d0d0f3 100644 --- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java +++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/MetaInstanceServiceImpl.java @@ -20,6 +20,10 @@ import modelengine.fit.task_new.repository.MetaInstanceRepository; import modelengine.fit.task_new.util.UUIDUtil; import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.annotation.Value; +import modelengine.fitframework.log.Logger; +import modelengine.fitframework.schedule.annotation.Scheduled; +import modelengine.fitframework.util.CollectionUtils; import java.util.Collections; import java.util.List; @@ -33,10 +37,26 @@ */ @Component public class MetaInstanceServiceImpl implements MetaInstanceService { + private static final Logger log = Logger.get(MetaInstanceServiceImpl.class); + + /** + * 表示待清理数据的数量的上限。 + */ + private static final int LIMIT = 1000; + private final MetaInstanceRepository metaInstanceRepository; + private final int expiredDays; - public MetaInstanceServiceImpl(MetaInstanceRepository metaInstanceRepository) { + /** + * 表示用元数据仓库和过期时间构造 {@link MetaInstanceServiceImpl} 的实例。 + * + * @param metaInstanceRepository 表示元数据仓库实例的 {@link MetaInstanceRepository}。 + * @param expiredDays 表示过期时间的 {@link String}。 + */ + public MetaInstanceServiceImpl(MetaInstanceRepository metaInstanceRepository, + @Value("${task.expiredDays}") int expiredDays) { this.metaInstanceRepository = metaInstanceRepository; + this.expiredDays = expiredDays; } @Override @@ -104,4 +124,24 @@ public Instance retrieveById(String instanceId, OperationContext context) { RangedResultSet resultSet = this.list(Collections.singletonList(instanceId), 0, 1, null); return resultSet.getResults().get(0); } + + /** + * 每天凌晨 3 点定时清理超期指定天数的任务实例数据。 + */ + @Scheduled(strategy = Scheduled.Strategy.CRON, value = "0 0 3 * * ?") + public void taskInstanceDbCleanSchedule() { + log.info("Start clean task instance db"); + try { + while (true) { + List expiredInstanceIds = this.metaInstanceRepository.getExpiredInstanceIds(expiredDays, LIMIT); + if (CollectionUtils.isEmpty(expiredInstanceIds)) { + break; + } + this.metaInstanceRepository.forceDelete(expiredInstanceIds); + } + } catch (Exception e) { + log.error("Error clean task instance db, exception:", e); + } + log.info("Finish clean task instance db"); + } } diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java index e6cf8d3c51..262fe5786b 100644 --- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java +++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/mapper/MetaInstanceMapper.java @@ -12,7 +12,7 @@ import java.util.List; /** - * Meta 实例数据库 Mapper 类 + * Meta 实例数据库 Mapper 类。 * * @author 邬涨财 * @since 2025-03-31 @@ -54,4 +54,20 @@ public interface MetaInstanceMapper { * @return 表示查询后的结果的 {@code long} */ long count(MetaInstanceCondition cond); + + /** + * 根据元数据实例唯一标识列表强制删除会话记录。 + * + * @param ids 表示元数据实例唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + void forceDelete(List ids); + + /** + * 获取超期的元数据实例唯一标识列表。 + * + * @param expiredDays 表示超期时间的 {@code int}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示元数据实例的唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + List getExpiredInstanceIds(int expiredDays, int limit); } diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java index c452aa86cc..6b96e39452 100644 --- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java +++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/MetaInstanceRepository.java @@ -9,10 +9,11 @@ import modelengine.fit.task_new.condition.MetaInstanceCondition; import modelengine.fit.task_new.entity.MetaInstance; +import java.time.LocalDateTime; import java.util.List; /** - * Meta 实例数据库 Repo 层接口 + * Meta 实例数据库 Repo 层接口。 * * @author 邬涨财 * @since 2025-03-31 @@ -54,4 +55,20 @@ public interface MetaInstanceRepository { * @return 表示查询后的结果的 {@code long} */ long count(MetaInstanceCondition condition); + + /** + * 获取超期的元数据实例唯一标识列表。 + * + * @param expiredDays 表示超期时间的 {@link LocalDateTime}。 + * @param limit 表示查询条数的 {@code int}。 + * @return 表示元数据实例的唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + List getExpiredInstanceIds(int expiredDays, int limit); + + /** + * 根据元数据实例唯一标识列表强制删除会话记录。 + * + * @param ids 表示元数据实例唯一标识列表的 {@link List}{@code <}{@link String}{@code >}。 + */ + void forceDelete(List ids); } diff --git a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java index 087cf20a03..cc1ed45b91 100644 --- a/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java +++ b/app-builder/jane/task-new/src/main/java/modelengine/fit/task_new/repository/impl/MetaInstanceRepositoryImpl.java @@ -12,12 +12,13 @@ import modelengine.fit.task_new.repository.MetaInstanceRepository; import modelengine.fit.task_new.serializer.impl.MetaInstanceSerializer; import modelengine.fitframework.annotation.Component; +import modelengine.fitframework.util.CollectionUtils; import java.util.List; import java.util.stream.Collectors; /** - * Meta 实例数据库 Repo 层实现 + * Meta 实例数据库 Repo 层实现。 * * @author 邬涨财 * @since 2025-03-31 @@ -59,4 +60,17 @@ public List select(MetaInstanceCondition cond) { public long count(MetaInstanceCondition cond) { return this.metaInstanceMapper.count(cond); } + + @Override + public List getExpiredInstanceIds(int expiredDays, int limit) { + return this.metaInstanceMapper.getExpiredInstanceIds(expiredDays, limit); + } + + @Override + public void forceDelete(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return; + } + this.metaInstanceMapper.forceDelete(ids); + } } diff --git a/app-builder/jane/task-new/src/main/resources/application-prod.yml b/app-builder/jane/task-new/src/main/resources/application-prod.yml index 41312e05d6..280f27b086 100644 --- a/app-builder/jane/task-new/src/main/resources/application-prod.yml +++ b/app-builder/jane/task-new/src/main/resources/application-prod.yml @@ -25,4 +25,6 @@ fit: testOnBorrow: false testOnReturn: false mybatis: - mapper-locations: mapper/*Mapper.xml \ No newline at end of file + mapper-locations: mapper/*Mapper.xml +task: + expiredDays: 1 \ No newline at end of file diff --git a/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml b/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml index d411d60a8d..f8b4068756 100644 --- a/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml +++ b/app-builder/jane/task-new/src/main/resources/mapper/MetaInstanceMapper.xml @@ -139,4 +139,25 @@ + + + + + DELETE FROM + task_instance_new + where id in + + #{item} + + \ No newline at end of file diff --git a/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/po/ModelPo.java b/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/po/ModelPo.java index 096a0d66c1..76b32b48c5 100644 --- a/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/po/ModelPo.java +++ b/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/po/ModelPo.java @@ -6,6 +6,7 @@ package modelengine.fit.jade.aipp.model.po; +import lombok.AllArgsConstructor; import lombok.Data; import modelengine.jade.common.po.BasePo; diff --git a/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenter.java b/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenter.java index bd47202d86..332f23e355 100644 --- a/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenter.java +++ b/app-builder/plugins/aipp-custom-model-center/src/main/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenter.java @@ -58,6 +58,7 @@ public ModelListDto fetchModelList(String type, String scene, OperationContext c List modelDtoList = modelList.stream() .map(po -> ModelAccessInfo.builder() .serviceName(po.getName()) + .baseUrl(po.getBaseUrl()) .tag(CustomTag.pack(context, po)) .build()) .collect(Collectors.toList()); diff --git a/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_init_models.sql b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_init_models.sql new file mode 100644 index 0000000000..0c870c3e9d --- /dev/null +++ b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_init_models.sql @@ -0,0 +1,3 @@ +INSERT INTO "t_app_engine_model" ("created_at", "updated_at", "created_by", "updated_by", "model_id", "name", "tag", "base_url", "type") VALUES ('2025-03-19 18:32:08.086508', '2025-03-19 18:32:08.086508', 'system', 'system', 'm6', 'Qwen/Qwen2.5-72B-Instruct', 'SiliconFlow', 'https://api.siliconflow.cn', 'chat_completions') ON CONFLICT ("model_id") DO NOTHING; + +INSERT INTO "t_app_engine_user_model" ("created_at", "updated_at", "created_by", "updated_by", "user_id", "model_id", "api_key", "is_default") VALUES ('2025-03-19 16:04:40.776244', '2025-03-19 16:04:40.776244', 'system', 'system', 'Jade', 'm6', 'TODO', 1) ON CONFLICT ("user_id", "model_id") DO NOTHING; \ No newline at end of file diff --git a/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_t_model_import.sql b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_t_model_import.sql new file mode 100644 index 0000000000..d50fb687da --- /dev/null +++ b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_t_model_import.sql @@ -0,0 +1,66 @@ +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('添加模型', '{"name":"添加模型","description":"为用户添加可用的模型信息","parameters":{"type":"object","properties":{"modelName":{"examples":"","defaultValue":"","name":"modelName","description":"模型名称","type":"string","required":false},"baseUrl":{"examples":"","defaultValue":"","name":"baseUrl","description":"模型访问地址","type":"string","required":false},"apiKey":{"examples":"","defaultValue":"","name":"apiKey","description":"模型访问的 API Key","type":"string","required":false},"userId":{"examples":"","defaultValue":"","name":"userId","description":"用户id","type":"string","required":false}},"required":["modelName","baseUrl","apiKey","userId"]},"return":{"convertor":"","examples":"","name":"","description":"为用户添加可用的模型信息","type":"string"},"order":["modelName","baseUrl","apiKey","userId"]}', '{"FIT":{"genericableId":"modelengine.fit.jade.aipp.model.service.addUserModel","fitableId":"aipp.model.service.impl"}}', '{"tags":["FIT","MODEL"]}', '77ee3e02-5870-4338-a8be-52c8ed42f3c7', '1.0.0', 't', 'User_Model_Config_Service', 'add_user_model', 'User_Model_Config') ON CONFLICT ("unique_name", "version") DO NOTHING; +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('删除模型', '{"name":"删除模型","description":"删除用户绑定的模型信息","parameters":{"type":"object","properties":{"modelId":{"examples":"","defaultValue":"","name":"modelId","description":"模型id","type":"string","required":false},"userId":{"examples":"","defaultValue":"","name":"userId","description":"用户id","type":"string","required":false}},"required":["modelId","userId"]},"return":{"convertor":"","examples":"","name":"","description":"删除用户绑定的模型信息","type":"string"},"order":["modelId","userId"]}', '{"FIT":{"genericableId":"modelengine.fit.jade.aipp.model.service.deleteUserModel","fitableId":"aipp.model.service.impl"}}', '{"tags":["FIT","MODEL"]}', 'ff0a4b59-0be7-436f-8931-6a599ed9f8f3', '1.0.0', 't', 'User_Model_Config_Service', 'delete_user_model', 'User_Model_Config') ON CONFLICT ("unique_name", "version") DO NOTHING; +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('切换默认模型', '{"name":"切换默认模型","description":"将指定模型设置为用户的默认模型","parameters":{"type":"object","properties":{"modelId":{"examples":"","defaultValue":"","name":"modelId","description":"默认模型id","type":"string","required":false},"userId":{"examples":"","defaultValue":"","name":"userId","description":"用户id","type":"string","required":false}},"required":["modelId","userId"]},"return":{"convertor":"","examples":"","name":"","description":"将指定模型设置为用户的默认模型","type":"string"},"order":["modelId","userId"]}', '{"FIT":{"genericableId":"modelengine.fit.jade.aipp.model.service.switchDefaultModel","fitableId":"aipp.model.service.impl"}}', '{"tags":["FIT","MODEL"]}', 'f76205df-1814-49b9-a473-c03ee99770b7', '1.0.0', 't', 'User_Model_Config_Service', 'switch_default_model', 'User_Model_Config') ON CONFLICT ("unique_name", "version") DO NOTHING; +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('获取用户模型列表', '{"name":"获取用户模型列表","description":"根据用户标识来查询该用户可用的模型列表","parameters":{"type":"object","properties":{"userId":{"examples":"","defaultValue":"","name":"userId","description":"用户id","type":"string","required":false}},"required":["userId"]},"return":{"convertor":"","examples":"","name":"","description":"返回该用户可用的模型列表","type":"array","items":{"type":"object","properties":{"createdAt":{"format":"date-time","type":"string"},"modelName":{"type":"string"},"baseUrl":{"type":"string"},"isDefault":{"type":"integer"},"modelId":{"type":"string"},"userId":{"type":"string"}}}},"order":["userId"]}', '{"FIT":{"genericableId":"modelengine.fit.jade.aipp.model.service.getUserModelList","fitableId":"aipp.model.service.impl"}}', '{"tags":["FIT","MODEL"]}', '16816279-411b-48e1-b754-191abd61dcd0', '1.0.0', 't', 'User_Model_Config_Service', 'get_user_model_list', 'User_Model_Config') ON CONFLICT ("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('switch_default_model', '{"name":"switch_default_model","description":"将指定模型设置为用户的默认模型","parameters":{"type":"object","properties":{"userId":{"defaultValue":"","description":"用户id","name":"userId","type":"string","examples":"","required":true},"modelId":{"defaultValue":"","description":"默认模型id","name":"modelId","type":"string","examples":"","required":true}},"required":["userId","modelId"]},"order":["userId","modelId"],"parameterExtensions":null,"return":{"type":"string","convertor":""}}', 'User_Model_Config') ON CONFLICT ("definition_group_name", "name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('add_user_model', '{"name":"add_user_model","description":"为用户添加可用的模型信息","parameters":{"type":"object","properties":{"userId":{"defaultValue":"","description":"用户id","name":"userId","type":"string","examples":"","required":true},"apiKey":{"defaultValue":"","description":"模型访问的 API Key","name":"apiKey","type":"string","examples":"","required":true},"modelName":{"defaultValue":"","description":"模型名称","name":"modelName","type":"string","examples":"","required":true},"baseUrl":{"defaultValue":"","description":"模型访问地址","name":"baseUrl","type":"string","examples":"","required":true}},"required":["userId","apiKey","modelName","baseUrl"]},"order":["userId","apiKey","modelName","baseUrl"],"parameterExtensions":null,"return":{"type":"string","convertor":""}}', 'User_Model_Config') ON CONFLICT ("definition_group_name", "name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('delete_user_model', '{"name":"delete_user_model","description":"删除用户绑定的模型信息","parameters":{"type":"object","properties":{"userId":{"defaultValue":"","description":"用户id","name":"userId","type":"string","examples":"","required":true},"modelId":{"defaultValue":"","description":"模型id","name":"modelId","type":"string","examples":"","required":true}},"required":["userId","modelId"]},"order":["userId","modelId"],"parameterExtensions":null,"return":{"type":"string","convertor":""}}', 'User_Model_Config') ON CONFLICT ("definition_group_name", "name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('get_user_model_list', '{"name":"get_user_model_list","description":"根据用户标识来查询该用户可用的用戶模型列表","parameters":{"type":"object","properties":{"userId":{"defaultValue":"","description":"用户id","name":"userId","type":"string","examples":"","required":true}},"required":["userId"]},"order":["userId"],"parameterExtensions":null,"return":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"},"modelId":{"type":"string"},"userId":{"type":"string"},"modelName":{"type":"string"},"baseUrl":{"type":"string"},"isDefault":{"type":"integer"}}},"convertor":""}}', 'User_Model_Config') ON CONFLICT ("definition_group_name", "name") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('16816279-411b-48e1-b754-191abd61dcd0', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('ff0a4b59-0be7-436f-8931-6a599ed9f8f3', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('77ee3e02-5870-4338-a8be-52c8ed42f3c7', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('ff0a4b59-0be7-436f-8931-6a599ed9f8f3', 'MODEL') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('f76205df-1814-49b9-a473-c03ee99770b7', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('f76205df-1814-49b9-a473-c03ee99770b7', 'MODEL') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('77ee3e02-5870-4338-a8be-52c8ed42f3c7', 'MODEL') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('16816279-411b-48e1-b754-191abd61dcd0', 'MODEL') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; + +INSERT INTO "public"."store_plugin_tool" ("like_count", "download_count", "tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES (0, 0, '切换默认模型', 'f231f908d0f0959acbc1df7b2f4b8f76b05219e3fa82fcb715def9f8858139e5', 'f76205df-1814-49b9-a473-c03ee99770b7', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; +INSERT INTO "public"."store_plugin_tool" ("like_count", "download_count", "tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES (0, 0, '删除模型', 'f231f908d0f0959acbc1df7b2f4b8f76b05219e3fa82fcb715def9f8858139e5', 'ff0a4b59-0be7-436f-8931-6a599ed9f8f3', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; +INSERT INTO "public"."store_plugin_tool" ("like_count", "download_count", "tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES (0, 0, '添加模型', 'f231f908d0f0959acbc1df7b2f4b8f76b05219e3fa82fcb715def9f8858139e5', '77ee3e02-5870-4338-a8be-52c8ed42f3c7', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; +INSERT INTO "public"."store_plugin_tool" ("like_count", "download_count", "tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES (0, 0, '获取用户模型列表', 'f231f908d0f0959acbc1df7b2f4b8f76b05219e3fa82fcb715def9f8858139e5', '16816279-411b-48e1-b754-191abd61dcd0', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; + +INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "deploy_status", "is_builtin", "source", "icon") VALUES ('f231f908d0f0959acbc1df7b2f4b8f76b05219e3fa82fcb715def9f8858139e5', '用戶查询模型工具', '{"uniqueness.groupId":"user-model-edit-groupId","pluginFullName":"aipp-custom-model-center-1.0.0-SNAPSHOT_1744769471992.jar","checksum":"77679d190769b46a8da7e2c08a1f2ce2680647e430514e7da1d39be3f3d17925","name":"用戶查询模型工具","description":"这是一个用戶查询模型工具","uniqueness.artifactId":"user-model-edit-artifactId","type":"java"}', 'DEPLOYED', TRUE, '', NULL) ON CONFLICT ("plugin_id") DO NOTHING; + +INSERT INTO "public"."app_builder_form" ("id", "name", "tenant_id", "appearance", "type", "create_by", "create_at", "update_by", "update_at", "is_deleted", "form_suite_id", "version") VALUES ('1568509614c245a39ce53bda9c3c2ec1', '模型管理表单', '31f20efc7e0848deab6a6bc10fc3021e', '{"imgUrl": "smart_form/6befc536-7e6d-48b5-8dcb-1c4d04ca4e92/form.png", "schema": {"name": "模型管理表单", "return": {"type": "object", "required": ["action", "info"], "properties": {"info": {"type": "object", "required": ["modelName", "modelId", "baseUrl", "isDefault", "userId", "apiKey"], "properties": {"apiKey": {"type": "string"}, "userId": {"type": "string"}, "baseUrl": {"type": "string"}, "modelId": {"type": "string"}, "isDefault": {"type": "integer"}, "modelName": {"type": "string"}}}, "action": {"enum": ["add", "delete", "switch", "quit"], "type": "string"}}}, "parameters": {"type": "object", "required": ["models"], "properties": {"models": {"type": "array", "items": {"type": "object", "required": ["modelId", "isDefault", "userId"], "properties": {"userId": {"type": "string"}, "baseUrl": {"type": "string"}, "modelId": {"type": "string"}, "createdAt": {"type": "string"}, "isDefault": {"type": "integer"}, "modelName": {"type": "string"}}}}}}}, "fileName": "模型管理表单.zip", "fileSize": 352510, "fileUuid": "7e9db4c01d8f482596fc1ddc78625496", "iframeUrl": "smart_form/6befc536-7e6d-48b5-8dcb-1c4d04ca4e92/build/index.html", "description": "这是一个模型管理表单"}', 'runtime', 'system', '2025-04-18 12:09:28.732006', 'system', '2025-04-18 12:09:28.73202', 0, '07b7ebd306944de683c2d060944da579', '1.0.0') ON CONFLICT ("id") DO NOTHING; + +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "collection_usr_cnt", "is_deleted", "path", "app_type", "app_suite_id", "is_active", "status", "unique_name", "publish_at", "app_id") VALUES ('cec6bfe7cb3a444f8a26a97ea513e501', '模型配置应用', 'system', '2025-04-18 11:54:33.515505', 'system', '2025-04-18 12:12:12.847078', '99bc4eda890041878bc0e4fe13114956', 'df3c7df760964d08b092f7c9d0eb9513', '31f20efc7e0848deab6a6bc10fc3021e', 'app', '1.0.0', '{"icon": "", "name": "模型配置应用", "greeting": "", "store_id": "7a76cbd2-881d-469b-b2df-76abed7d0b61", "is_update": false, "description": "当你想要配置模型的时候,请使用我!", "publishedUpdateLog": "", "publishedDescription": ""}', 'active', 'workflow', 'chatbot', 0, 0, 'P920UNZY2JkYoWhl', 'b653edb7eb5a49be91abcd2c5877c6ad', '0b4fe5a430104edfbe0dc6cff0ebea19', 't', 'published', '7a76cbd2-881d-469b-b2df-76abed7d0b61', '2025-04-18 12:12:25', 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config" ("id", "form_id", "app_id", "tenant_id", "create_by", "create_at", "update_by", "update_at", "is_deleted") VALUES ('99bc4eda890041878bc0e4fe13114956', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'cec6bfe7cb3a444f8a26a97ea513e501', '31f20efc7e0848deab6a6bc10fc3021e', 'system', '2025-04-18 11:54:33.515505', 'system', '2025-04-18 12:12:12.847078', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('3deb2fffd3e248fbbd0ce5cd55d1faa7', 'jadewdnjbq', 'df5c490b8c25454698b4bd7877628703', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('65bd77ba6db54aad85be7f9301744860', 'jadewdnjbq', '52a8ebe4eec44858b41baa59e0256697', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('e2bcf4291e214937b5717f865a9d7e8c', 'jade6qm5eg', '569f4946f1e749e0a84b2f77c603735e', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('f0a5a5c071cf4125a235c5c8044cc923', 'jade0pg2ag', '39974065ba1146f89db4a2404e6195a0', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('4d148e7480f04435af8fb3fcdf217053', 'jadewdnjbq', '283a6f9690d34a1eb9f23bf0a39d808a', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('3cfcb631c4bb4cb188dd0ee9d51173cc', 'jadewdnjbq', 'e1434967e75048669144ea19067c8124', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('b15a6d9274104eccab2a3de4014b8d2a', 'jadewdnjbq', '0442a2b2608e475bb8876749117cad43', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('3031b678ec974973b78306ea505df91c', 'jadewdnjbq', 'db6182bf103d4989abb6fc0ef10e87af', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('27242ad7124e4b778c45bac18b8a66f4', NULL, '0bb21aedaf134434a703c506da45791f', '99bc4eda890041878bc0e4fe13114956', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance", "is_deleted") VALUES ('df3c7df760964d08b092f7c9d0eb9513', 'LLM模板', 'system', '2025-04-18 11:54:33.515505', 'system', '2025-04-18 12:12:12.847078', '{"id": "df3c7df760964d08b092f7c9d0eb9513", "type": "jadeFlowGraph", "pages": [{"x": -1225.7009556725914, "y": 160.9007936507931, "id": "elsa-page:tvp1s6", "bold": false, "mode": "configuration", "text": "newFlowPage", "type": "jadeFlowPage", "dirty": true, "index": 0, "width": 1600, "hAlign": "left", "height": 800, "isPage": true, "italic": false, "scaleX": 0.7000000000000001, "scaleY": 0.7000000000000001, "shapes": [{"x": -170.8928571428571, "y": 32.5, "id": "jade6qm5eg", "pad": 6, "bold": false, "text": "开始", "type": "startNodeStart", "dirty": false, "index": 0, "width": 360, "height": 728, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": true}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}, {"id": "91138f09-b635-43df-95c6-1fe3d1745830", "from": "Expand", "name": "appConfig", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dadf", "from": "Input", "name": "appChatStyle", "type": "String", "value": "default"}]}], "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": false, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "startComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "mouseInBorderColor": "rgba(28,31,35,.08)"}, {"x": 2564.107142857142, "y": -145.1785714285714, "id": "jadesoux5i", "pad": 6, "bold": false, "text": "添加模型结束", "type": "endNodeEnd", "dirty": false, "index": 1, "width": 360, "height": 313, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "54dab89c-5693-4082-baa7-12c648d812f7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "ffad80c2-3f60-4d57-93b2-c2362a5dab9c", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1", "referenceKey": "output", "referenceNode": "jadehyxese"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "c26bf2ca-75b3-4a6f-bc47-132c2e170895", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 278.02238424402026, "y": 428.19444444444423, "id": "jadevpescp", "pad": 6, "bold": false, "text": "获取用户模型列表", "type": "toolInvokeNodeState", "dirty": false, "index": 2, "width": 360, "height": 297, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "userId"}], "return": {"type": "array"}, "uniqueName": "16816279-411b-48e1-b754-191abd61dcd0"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "userId_f4725c4a-ac63-4b51-ae86-8c0baca9b71d", "from": "Reference", "name": "userId", "type": "String", "value": ["userId"], "isRequired": true, "description": "用户id", "referenceId": "userId", "referenceKey": "userId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_78982629-c58c-4211-9f4b-369bdcfef31f", "name": "output", "type": "Array", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 189.1071428571429, "y": 396.5, "id": "jadexixl9n", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 3, "textX": 0, "textY": 0, "width": 88.91524138687737, "hAlign": "center", "height": 180.19444444444423, "italic": false, "margin": 20, "toShape": "jadevpescp", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade6qm5eg", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 722.4668286884644, "y": 289.30555555555554, "id": "jade1osjbn", "pad": 6, "bold": false, "text": "人工表单", "type": "manualCheckNodeState", "dirty": true, "index": 4, "width": 360, "height": 489, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/6befc536-7e6d-48b5-8dcb-1c4d04ca4e92/form.png", "taskId": "1568509614c245a39ce53bda9c3c2ec1", "formName": "模型管理表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "models_6db2dcd7-94f1-4eb9-88d8-69e68e3fc990", "from": "Reference", "name": "models", "type": "Array", "value": ["output"], "isRequired": true, "referenceId": "output_78982629-c58c-4211-9f4b-369bdcfef31f", "referenceKey": "output", "referenceNode": "jadevpescp"}], "outputParams": [{"id": "output_14c32c0b-52d1-4d40-8b5d-b0470f3ddd9a", "name": "output", "type": "Object", "value": [{"id": "output_c2404fc9-56de-4cca-9706-0e63c49ad4d0", "name": "info", "type": "Object", "value": [{"id": "d270cf95-6f76-49bd-81b2-66692109034b", "name": "apiKey", "type": "String", "value": "String"}, {"id": "ae07dc15-b235-4139-ad89-9b6160988472", "name": "userId", "type": "String", "value": "String"}, {"id": "8bfa7821-ff6b-4e53-bfbc-213d68a613a2", "name": "baseUrl", "type": "String", "value": "String"}, {"id": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "name": "modelId", "type": "String", "value": "String"}, {"id": "62d172c1-b852-4b5d-97d8-aa303956aae0", "name": "isDefault", "type": "Integer", "value": "Integer"}, {"id": "ce53a197-f57a-4bfa-9343-b6f993b61883", "name": "modelName", "type": "String", "value": "String"}]}, {"id": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "name": "action", "type": "String", "value": "String"}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "manualCheckComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 638.0223842440203, "y": 576.6944444444442, "id": "jade419ps4", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 5, "textX": 0, "textY": 0, "width": 84.44444444444412, "hAlign": "center", "height": -42.88888888888869, "italic": false, "margin": 20, "toShape": "jade1osjbn", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadevpescp", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1150.8795271011613, "y": 195.33730158730177, "id": "jade0305an", "pad": 6, "bold": false, "text": "条件", "type": "conditionNodeCondition", "dirty": false, "index": 6, "width": 600, "height": 735, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e", "type": "if", "runnable": true, "conditions": [{"id": "eaa48316-b65d-49f4-a9c2-0d1261cb4efe", "value": [{"id": "3288e216-4f89-473c-b6b9-e9d80e66fe0c", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "e63c3cba-9edc-4d94-af84-e8ad0c319aa8", "from": "Input", "name": "right", "type": "String", "value": "add", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "42754fab-4274-4e32-9811-9088eb80f691", "type": "if", "runnable": true, "conditions": [{"id": "7122796c-36ce-41ea-96be-0ce69cfe704b", "value": [{"id": "b9a64d7a-1ebc-40e1-a02b-2a793154b5fa", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "51b80568-1f7a-4cca-8c31-c42f41d5b63d", "from": "Input", "name": "right", "type": "String", "value": "delete", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "79592b84-07fd-4212-9f50-71479aa86b22", "type": "if", "runnable": true, "conditions": [{"id": "42692336-de84-4023-864e-3ee666da5d93", "value": [{"id": "14a9b304-09dd-45ce-a421-6aa91703a1f4", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "557856d5-18c8-4a67-91d4-0cf4faec4060", "from": "Input", "name": "right", "type": "String", "value": "switch", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "50b171e2-b8b6-46cb-89a3-a6e0883ae710", "type": "else", "runnable": true, "conditions": [{"id": "90303c25-9b1b-4e87-8af8-e3c997331c38", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1082.4668286884644, "y": 533.8055555555555, "id": "jadezmsms0", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 7, "textX": 0, "textY": 0, "width": 68.41269841269695, "hAlign": "center", "height": 29.031746031746252, "italic": false, "margin": 20, "toShape": "jade0305an", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade1osjbn", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1975.165241386877, "y": -157.51984126984166, "id": "jadehyxese", "pad": 6, "bold": false, "text": "添加模型", "type": "toolInvokeNodeState", "dirty": false, "index": 8, "width": 360, "height": 411, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelName"}, {"name": "baseUrl"}, {"name": "apiKey"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "77ee3e02-5870-4338-a8be-52c8ed42f3c7"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelName_216f7038-ade2-465a-9d24-9fb39d4af5e5", "from": "Reference", "name": "modelName", "type": "String", "value": ["output", "info", "modelName"], "isRequired": true, "description": "模型名称", "referenceId": "ce53a197-f57a-4bfa-9343-b6f993b61883", "referenceKey": "modelName", "referenceNode": "jade1osjbn"}, {"id": "baseUrl_49ee4206-7c9c-4311-b749-da274d3d14df", "from": "Reference", "name": "baseUrl", "type": "String", "value": ["output", "info", "baseUrl"], "isRequired": true, "description": "模型访问地址", "referenceId": "8bfa7821-ff6b-4e53-bfbc-213d68a613a2", "referenceKey": "baseUrl", "referenceNode": "jade1osjbn"}, {"id": "apiKey_d776d4db-4b49-4dee-b054-5b43d28c9c64", "from": "Reference", "name": "apiKey", "type": "String", "value": ["output", "info", "apiKey"], "isRequired": true, "description": "模型访问的 API Key", "referenceId": "d270cf95-6f76-49bd-81b2-66692109034b", "referenceKey": "apiKey", "referenceNode": "jade1osjbn"}, {"id": "userId_9a314ce2-d74b-471e-a7f8-ac5bf189f253", "from": "Reference", "name": "userId", "type": "String", "value": ["userId"], "isRequired": true, "description": "用户id", "referenceId": "userId", "referenceKey": "userId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1", "name": "output", "type": "String", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1973.736669958303, "y": 369.623015873016, "id": "jadeas9z1s", "pad": 6, "bold": false, "text": "删除模型", "type": "toolInvokeNodeState", "dirty": false, "index": 9, "width": 360, "height": 335, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelId"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "ff0a4b59-0be7-436f-8931-6a599ed9f8f3"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelId_e329ead9-4485-453a-8973-9047c1e06a9a", "from": "Reference", "name": "modelId", "type": "String", "value": ["output", "info", "modelId"], "isRequired": true, "description": "模型id", "referenceId": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "referenceKey": "modelId", "referenceNode": "jade1osjbn"}, {"id": "userId_47ee255d-9eeb-418c-aabc-8eb7871d0be5", "from": "Reference", "name": "userId", "type": "String", "value": ["output", "info", "userId"], "isRequired": true, "description": "用户id", "referenceId": "ae07dc15-b235-4139-ad89-9b6160988472", "referenceKey": "userId", "referenceNode": "jade1osjbn"}], "outputParams": [{"id": "output_786cf758-f9af-4959-83e7-251a01e04fb0", "name": "output", "type": "String", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1739.953710973929, "y": 381.97731623573924, "id": "jade2cfmdw", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 10, "textX": 0, "textY": 0, "width": 235.21153041294792, "hAlign": "center", "height": -333.9971575055809, "italic": false, "margin": 20, "toShape": "jadehyxese", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade0305an", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-0|20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e"}, {"x": 1966.593812815448, "y": 741.0515873015875, "id": "jade3q2qyw", "pad": 6, "bold": false, "text": "切换默认模型", "type": "toolInvokeNodeState", "dirty": false, "index": 11, "width": 360, "height": 335, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelId"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "f76205df-1814-49b9-a473-c03ee99770b7"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelId_1312ce29-7bb9-4ac2-8cb9-e53a3f6b68c8", "from": "Reference", "name": "modelId", "type": "String", "value": ["output", "info", "modelId"], "isRequired": true, "description": "默认模型id", "referenceId": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "referenceKey": "modelId", "referenceNode": "jade1osjbn"}, {"id": "userId_71b52489-3462-48ea-824c-ce04d99ba7e2", "from": "Reference", "name": "userId", "type": "String", "value": ["output", "info", "userId"], "isRequired": true, "description": "用户id", "referenceId": "ae07dc15-b235-4139-ad89-9b6160988472", "referenceKey": "userId", "referenceNode": "jade1osjbn"}], "outputParams": [{"id": "output_4113326b-5e9f-495e-8112-2ca9a0dca171", "name": "output", "type": "String", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "toolInvokeComponent", "focusBackColor": "white", "sourcePlatform": "", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1739.953710973929, "y": 569.9773031567772, "id": "jade31ntj1", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 12, "textX": 0, "textY": 0, "width": 233.7829589843741, "hAlign": "center", "height": -32.85428728376121, "italic": false, "margin": 20, "toShape": "jadeas9z1s", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade0305an", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-1|42754fab-4274-4e32-9811-9088eb80f691"}, {"x": 1739.953710973929, "y": 757.977268279545, "id": "jadedmxi6l", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 13, "textX": 0, "textY": 0, "width": 226.64010184151903, "hAlign": "center", "height": 150.57431902204246, "italic": false, "margin": 20, "toShape": "jade3q2qyw", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade0305an", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-2|79592b84-07fd-4212-9f50-71479aa86b22"}, {"x": 2335.165241386877, "y": 47.98015873015834, "id": "jadejcuwis", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 14, "textX": 0, "textY": 0, "width": 228.9419014702653, "hAlign": "center", "height": -36.658730158729725, "italic": false, "margin": 20, "toShape": "jadesoux5i", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadehyxese", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 2633.736669958307, "y": 322.4801587301588, "id": "jade7dv633", "pad": 6, "bold": false, "text": "删除模型结束", "type": "endNodeEnd", "dirty": false, "index": 15, "width": 360, "height": 313, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "104c12c3-804b-419f-a1c7-24b66516ed42", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "020da94c-e511-4ee9-8e2b-0ce5eb3103cb", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_786cf758-f9af-4959-83e7-251a01e04fb0", "referenceKey": "output", "referenceNode": "jadeas9z1s"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "0367a295-adad-4a19-a08e-ab04c37db3ac", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2602.308098529733, "y": 732.4801587301587, "id": "jade1thrs1", "pad": 6, "bold": false, "text": "结束_2", "type": "endNodeEnd", "dirty": false, "index": 16, "width": 360, "height": 313, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "9f2d49d0-240d-46bb-80cb-a4391ac0de69", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "0f777bfd-06e5-452a-b80e-509057551d72", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_4113326b-5e9f-495e-8112-2ca9a0dca171", "referenceKey": "output", "referenceNode": "jade3q2qyw"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "fac8c254-46c4-42b2-92f0-32990dcd0086", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1863.736669958305, "y": 1129.6230158730177, "id": "jadeifaftx", "pad": 6, "bold": false, "text": "退出表单结束", "type": "endNodeEnd", "dirty": false, "index": 17, "width": 360, "height": 313, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "59007e69-9047-42d4-bfcd-3150e78f0215", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "cffd1f94-832a-4f23-b8d5-b72db4d521c1", "from": "Input", "name": "finalOutput", "type": "String", "value": "已退出,请重新开始对话来发起新的模型配置请求。", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "6f7d0c10-fe39-4dbe-8e72-0d5202e42a22", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2333.736669958303, "y": 537.123015873016, "id": "jadei8jdd4", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 18, "textX": 0, "textY": 0, "width": 300.0000000000041, "hAlign": "center", "height": -58.142857142857224, "italic": false, "margin": 20, "toShape": "jade7dv633", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeas9z1s", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1739.953710973929, "y": 870.9773478432309, "id": "jadecg9j3o", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 19, "textX": 0, "textY": 0, "width": 123.78295898437591, "hAlign": "center", "height": 415.14566802978686, "italic": false, "margin": 20, "toShape": "jadeifaftx", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade0305an", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 2326.593812815448, "y": 908.5515873015875, "id": "jadewcxlt5", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 20, "textX": 0, "textY": 0, "width": 275.71428571428487, "hAlign": "center", "height": -19.571428571428783, "italic": false, "margin": 20, "toShape": "jade1thrs1", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade3q2qyw", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}], "vAlign": "top", "itemPad": [0, 0, 0, 0], "division": -1, "dockMode": "none", "fontFace": "arial", "fontSize": 18, "hideText": true, "moveable": true, "shapesAs": {}, "backColor": "#fbfbfc", "container": "elsa-page:tvp1s6", "dockAlign": "top", "fontColor": "#ECD0A7", "fontStyle": "normal", "itemSpace": 5, "namespace": "jadeFlow", "fontWeight": "bold", "itemScroll": {"x": 0, "y": 0}, "borderColor": "white", "focusBackColor": "#fbfbfc"}], "title": "df3c7df760964d08b092f7c9d0eb9513", "source": "elsa", "tenant": "31f20efc7e0848deab6a6bc10fc3021e", "setting": {"pad": 10, "tag": {}, "code": "", "pDock": "none", "hAlign": "center", "margin": 25, "shadow": "", "shared": false, "vAlign": "top", "itemPad": [5, 5, 5, 5], "visible": true, "autoText": false, "dockMode": "none", "dragable": true, "editable": true, "fontFace": "arial", "fontSize": 12, "infoType": {"name": "none", "next": "INFORMATION"}, "moveable": true, "priority": 0, "allowLink": true, "autoWidth": false, "backAlpha": 0.15, "backColor": "whitesmoke", "dashWidth": 0, "deletable": true, "fontColor": "steelblue", "fontStyle": "normal", "headColor": "steelblue", "lineWidth": 2, "underline": false, "autoHeight": false, "emphasized": false, "fontWeight": "lighter", "itemScroll": {"x": 0, "y": 0}, "lineHeight": 1.5, "resizeable": true, "rotateAble": true, "scrollLock": {"x": false, "y": false}, "selectable": true, "shadowData": "2px 2px 4px", "borderColor": "#047bfc", "borderWidth": 1, "bulletSpeed": 1, "focusMargin": 0, "focusShadow": "", "globalAlpha": 1, "outstanding": false, "bulletedList": false, "cornerRadius": 4, "enableSocial": true, "mouseInColor": "orange", "numberedList": false, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "rotateDegree": 0, "captionhAlign": "center", "strikethrough": false, "focusBackColor": "whitesmoke", "focusFontColor": "darkorange", "progressStatus": {"name": "NONE", "next": "UNKNOWN", "color": "gray"}, "showedProgress": false, "allNodeNumLimit": 99, "captionfontFace": "arial black", "captionfontSize": 14, "enableAnimation": false, "progressPercent": 0.65, "captionfontColor": "whitesmoke", "captionfontStyle": "normal", "focusBorderColor": "#047bfc", "focusBorderWidth": 1, "mouseInBackColor": "whitesmoke", "mouseInFontColor": "orange", "captionfontWeight": "lighter", "captionlineHeight": 1, "mouseInBorderColor": "#047bfc", "sameTypeNodeNumLimit": 19}, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}, "enableText": false}', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('52a8ebe4eec44858b41baa59e0256697', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'ability', 'String', 'null', 'none', 'workflow', '能力配置', 1, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('569f4946f1e749e0a84b2f77c603735e', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'chat', 'String', 'null', 'none', 'workflow', '聊天设置', 3, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('39974065ba1146f89db4a2404e6195a0', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'memory', 'List', '["jade6qm5eg","memory"]', 'graph', 'chat', '多轮对话', 5, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('283a6f9690d34a1eb9f23bf0a39d808a', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'opening', 'String', '"任意输入之后开始配置模型。"', 'input', 'chat', '开场白', 4, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('e1434967e75048669144ea19067c8124', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'enterWorkflow', 'String', 'null', 'none', 'ability', '进入工作流编排', 2, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('0442a2b2608e475bb8876749117cad43', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'inspiration', 'object', '{"category":[{"title":"root","id":"root","children":[]}],"inspirations":[{"name":"开始配置","description":"当你想要开始配置的时候,请点击。","prompt":"开始配置","promptVarData":[],"category":null,"auto":true,"id":"1v21m5"}],"showInspiration":true}', 'input', 'chat', '创意灵感', 7, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('db6182bf103d4989abb6fc0ef10e87af', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'recommend', 'object', '{"showRecommend":false,"list":[]}', 'input', 'chat', '猜你想问', 6, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('0bb21aedaf134434a703c506da45791f', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'multimodal', 'object', '{"useMultimodal":true,"maxUploadFilesNum":5,"autoChatOnUpload":false}', 'input', 'chat', '多模态', 8, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('df5c490b8c25454698b4bd7877628703', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'workflow', 'String', 'null', 'none', 'null', '工作流编排', 0, 0, 'cec6bfe7cb3a444f8a26a97ea513e501') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."task_new" ("id", "name", "version", "template_id", "tenant_id", "attributes", "created_by", "created_at", "updated_by", "updated_at", "is_deleted") VALUES ('17997d19af65470d90f0581d8fa43834', '模型配置应用', '1.0.0', '0b4fe5a430104edfbe0dc6cff0ebea19', '31f20efc7e0848deab6a6bc10fc3021e', '{"app_id": "cec6bfe7cb3a444f8a26a97ea513e501", "version": "1.0.0", "aipp_type": "NORMAL", "meta_icon": "", "publish_at": "2025-04-18T12:12:25.856075548", "description": "当你想要配置模型的时候,请使用我!", "meta_status": "active", "unique_name": "7a76cbd2-881d-469b-b2df-76abed7d0b61", "flow_config_id": "df3c7df760964d08b092f7c9d0eb9513", "flow_definition_id": "502f4a99db924e75adf6d9f403c8b905", "publish_update_log": "", "publish_description": ""}', 'system', '2025-04-18 12:12:24.802552', 'system', '2025-04-18 12:12:25.869732', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."flow_definition" ("definition_id", "meta_id", "name", "tenant", "version", "status", "graph", "created_by", "created_at") VALUES ('502f4a99db924e75adf6d9f403c8b905', 'df3c7df760964d08b092f7c9d0eb9513', 'df3c7df760964d08b092f7c9d0eb9513', '31f20efc7e0848deab6a6bc10fc3021e', '1.0.0', 'active', '{"name": "df3c7df760964d08b092f7c9d0eb9513", "nodes": [{"name": "开始", "type": "startNodeStart", "metaId": "jade6qm5eg", "runnable": true, "inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": true}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}, {"id": "91138f09-b635-43df-95c6-1fe3d1745830", "from": "Expand", "name": "appConfig", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dadf", "from": "Input", "name": "appChatStyle", "type": "String", "value": "default"}]}], "triggerMode": "auto"}, {"name": "添加模型结束", "type": "endNodeEnd", "metaId": "jadesoux5i", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "54dab89c-5693-4082-baa7-12c648d812f7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "ffad80c2-3f60-4d57-93b2-c2362a5dab9c", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1", "referenceKey": "output", "referenceNode": "jadehyxese"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "c26bf2ca-75b3-4a6f-bc47-132c2e170895", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"name": "获取用户模型列表", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "userId"}], "return": {"type": "array"}, "uniqueName": "16816279-411b-48e1-b754-191abd61dcd0"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "userId_f4725c4a-ac63-4b51-ae86-8c0baca9b71d", "from": "Reference", "name": "userId", "type": "String", "value": ["userId"], "isRequired": true, "description": "用户id", "referenceId": "userId", "referenceKey": "userId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_78982629-c58c-4211-9f4b-369bdcfef31f", "name": "output", "type": "Array", "value": []}]}}}, "metaId": "jadevpescp", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadevpescp", "from": "jade6qm5eg", "type": "jadeEvent", "metaId": "jadexixl9n", "runnable": true, "fromConnector": "E"}, {"name": "人工表单", "task": {"type": "AIPP_SMART_FORM", "imgUrl": "http://localhost:8001/api/jober/static/smart_form/6befc536-7e6d-48b5-8dcb-1c4d04ca4e92/form.png", "taskId": "1568509614c245a39ce53bda9c3c2ec1", "formName": "模型管理表单", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "models_6db2dcd7-94f1-4eb9-88d8-69e68e3fc990", "from": "Reference", "name": "models", "type": "Array", "value": ["output"], "isRequired": true, "referenceId": "output_78982629-c58c-4211-9f4b-369bdcfef31f", "referenceKey": "output", "referenceNode": "jadevpescp"}], "outputParams": [{"id": "output_14c32c0b-52d1-4d40-8b5d-b0470f3ddd9a", "name": "output", "type": "Object", "value": [{"id": "output_c2404fc9-56de-4cca-9706-0e63c49ad4d0", "name": "info", "type": "Object", "value": [{"id": "d270cf95-6f76-49bd-81b2-66692109034b", "name": "apiKey", "type": "String", "value": "String"}, {"id": "ae07dc15-b235-4139-ad89-9b6160988472", "name": "userId", "type": "String", "value": "String"}, {"id": "8bfa7821-ff6b-4e53-bfbc-213d68a613a2", "name": "baseUrl", "type": "String", "value": "String"}, {"id": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "name": "modelId", "type": "String", "value": "String"}, {"id": "62d172c1-b852-4b5d-97d8-aa303956aae0", "name": "isDefault", "type": "Integer", "value": "Integer"}, {"id": "ce53a197-f57a-4bfa-9343-b6f993b61883", "name": "modelName", "type": "String", "value": "String"}]}, {"id": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "name": "action", "type": "String", "value": "String"}]}]}}}, "type": "manualCheckNodeState", "metaId": "jade1osjbn", "runnable": true, "triggerMode": "manual"}, {"to": "jade1osjbn", "from": "jadevpescp", "type": "jadeEvent", "metaId": "jade419ps4", "runnable": true, "fromConnector": "E"}, {"name": "条件", "type": "conditionNodeCondition", "metaId": "jade0305an", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e", "type": "if", "runnable": true, "conditions": [{"id": "eaa48316-b65d-49f4-a9c2-0d1261cb4efe", "value": [{"id": "3288e216-4f89-473c-b6b9-e9d80e66fe0c", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "e63c3cba-9edc-4d94-af84-e8ad0c319aa8", "from": "Input", "name": "right", "type": "String", "value": "add", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "42754fab-4274-4e32-9811-9088eb80f691", "type": "if", "runnable": true, "conditions": [{"id": "7122796c-36ce-41ea-96be-0ce69cfe704b", "value": [{"id": "b9a64d7a-1ebc-40e1-a02b-2a793154b5fa", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "51b80568-1f7a-4cca-8c31-c42f41d5b63d", "from": "Input", "name": "right", "type": "String", "value": "delete", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "79592b84-07fd-4212-9f50-71479aa86b22", "type": "if", "runnable": true, "conditions": [{"id": "42692336-de84-4023-864e-3ee666da5d93", "value": [{"id": "14a9b304-09dd-45ce-a421-6aa91703a1f4", "from": "Reference", "name": "left", "type": "String", "value": ["output", "action"], "referenceId": "bed9c7b2-83dc-4b3e-9f9c-915e20663037", "referenceKey": "action", "referenceNode": "jade1osjbn"}, {"id": "557856d5-18c8-4a67-91d4-0cf4faec4060", "from": "Input", "name": "right", "type": "String", "value": "switch", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "50b171e2-b8b6-46cb-89a3-a6e0883ae710", "type": "else", "runnable": true, "conditions": [{"id": "90303c25-9b1b-4e87-8af8-e3c997331c38", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"to": "jade0305an", "from": "jade1osjbn", "type": "jadeEvent", "metaId": "jadezmsms0", "runnable": true, "fromConnector": "E"}, {"name": "添加模型", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelName"}, {"name": "baseUrl"}, {"name": "apiKey"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "77ee3e02-5870-4338-a8be-52c8ed42f3c7"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelName_216f7038-ade2-465a-9d24-9fb39d4af5e5", "from": "Reference", "name": "modelName", "type": "String", "value": ["output", "info", "modelName"], "isRequired": true, "description": "模型名称", "referenceId": "ce53a197-f57a-4bfa-9343-b6f993b61883", "referenceKey": "modelName", "referenceNode": "jade1osjbn"}, {"id": "baseUrl_49ee4206-7c9c-4311-b749-da274d3d14df", "from": "Reference", "name": "baseUrl", "type": "String", "value": ["output", "info", "baseUrl"], "isRequired": true, "description": "模型访问地址", "referenceId": "8bfa7821-ff6b-4e53-bfbc-213d68a613a2", "referenceKey": "baseUrl", "referenceNode": "jade1osjbn"}, {"id": "apiKey_d776d4db-4b49-4dee-b054-5b43d28c9c64", "from": "Reference", "name": "apiKey", "type": "String", "value": ["output", "info", "apiKey"], "isRequired": true, "description": "模型访问的 API Key", "referenceId": "d270cf95-6f76-49bd-81b2-66692109034b", "referenceKey": "apiKey", "referenceNode": "jade1osjbn"}, {"id": "userId_9a314ce2-d74b-471e-a7f8-ac5bf189f253", "from": "Reference", "name": "userId", "type": "String", "value": ["userId"], "isRequired": true, "description": "用户id", "referenceId": "userId", "referenceKey": "userId", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1", "name": "output", "type": "String", "value": []}]}}}, "metaId": "jadehyxese", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "删除模型", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelId"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "ff0a4b59-0be7-436f-8931-6a599ed9f8f3"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelId_e329ead9-4485-453a-8973-9047c1e06a9a", "from": "Reference", "name": "modelId", "type": "String", "value": ["output", "info", "modelId"], "isRequired": true, "description": "模型id", "referenceId": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "referenceKey": "modelId", "referenceNode": "jade1osjbn"}, {"id": "userId_47ee255d-9eeb-418c-aabc-8eb7871d0be5", "from": "Reference", "name": "userId", "type": "String", "value": ["output", "info", "userId"], "isRequired": true, "description": "用户id", "referenceId": "ae07dc15-b235-4139-ad89-9b6160988472", "referenceKey": "userId", "referenceNode": "jade1osjbn"}], "outputParams": [{"id": "output_786cf758-f9af-4959-83e7-251a01e04fb0", "name": "output", "type": "String", "value": []}]}}}, "metaId": "jadeas9z1s", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadehyxese", "from": "jade0305an", "type": "jadeEvent", "metaId": "jade2cfmdw", "runnable": true, "fromConnector": "dynamic-0|20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e"}, {"name": "切换默认模型", "type": "toolInvokeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "modelId"}, {"name": "userId"}], "return": {"type": "string"}, "uniqueName": "f76205df-1814-49b9-a473-c03ee99770b7"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "modelId_1312ce29-7bb9-4ac2-8cb9-e53a3f6b68c8", "from": "Reference", "name": "modelId", "type": "String", "value": ["output", "info", "modelId"], "isRequired": true, "description": "默认模型id", "referenceId": "a29201e7-7fe3-4f3f-834d-899a6cfbd79e", "referenceKey": "modelId", "referenceNode": "jade1osjbn"}, {"id": "userId_71b52489-3462-48ea-824c-ce04d99ba7e2", "from": "Reference", "name": "userId", "type": "String", "value": ["output", "info", "userId"], "isRequired": true, "description": "用户id", "referenceId": "ae07dc15-b235-4139-ad89-9b6160988472", "referenceKey": "userId", "referenceNode": "jade1osjbn"}], "outputParams": [{"id": "output_4113326b-5e9f-495e-8112-2ca9a0dca171", "name": "output", "type": "String", "value": []}]}}}, "metaId": "jade3q2qyw", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadeas9z1s", "from": "jade0305an", "type": "jadeEvent", "metaId": "jade31ntj1", "runnable": true, "fromConnector": "dynamic-1|42754fab-4274-4e32-9811-9088eb80f691"}, {"to": "jade3q2qyw", "from": "jade0305an", "type": "jadeEvent", "metaId": "jadedmxi6l", "runnable": true, "fromConnector": "dynamic-2|79592b84-07fd-4212-9f50-71479aa86b22"}, {"to": "jadesoux5i", "from": "jadehyxese", "type": "jadeEvent", "metaId": "jadejcuwis", "runnable": true, "fromConnector": "E"}, {"name": "删除模型结束", "type": "endNodeEnd", "metaId": "jade7dv633", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "104c12c3-804b-419f-a1c7-24b66516ed42", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "020da94c-e511-4ee9-8e2b-0ce5eb3103cb", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_786cf758-f9af-4959-83e7-251a01e04fb0", "referenceKey": "output", "referenceNode": "jadeas9z1s"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "0367a295-adad-4a19-a08e-ab04c37db3ac", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"name": "结束_2", "type": "endNodeEnd", "metaId": "jade1thrs1", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "9f2d49d0-240d-46bb-80cb-a4391ac0de69", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "0f777bfd-06e5-452a-b80e-509057551d72", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output"], "editable": true, "isRequired": true, "description": "", "referenceId": "output_4113326b-5e9f-495e-8112-2ca9a0dca171", "referenceKey": "output", "referenceNode": "jade3q2qyw"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "fac8c254-46c4-42b2-92f0-32990dcd0086", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"name": "退出表单结束", "type": "endNodeEnd", "metaId": "jadeifaftx", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "59007e69-9047-42d4-bfcd-3150e78f0215", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "cffd1f94-832a-4f23-b8d5-b72db4d521c1", "from": "Input", "name": "finalOutput", "type": "String", "value": "已退出,请重新开始对话来发起新的模型配置请求。", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "6f7d0c10-fe39-4dbe-8e72-0d5202e42a22", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jade7dv633", "from": "jadeas9z1s", "type": "jadeEvent", "metaId": "jadei8jdd4", "runnable": true, "fromConnector": "E"}, {"to": "jadeifaftx", "from": "jade0305an", "type": "jadeEvent", "metaId": "jadecg9j3o", "runnable": true, "fromConnector": "dynamic-999"}, {"to": "jade1thrs1", "from": "jade3q2qyw", "type": "jadeEvent", "metaId": "jadewcxlt5", "runnable": true, "fromConnector": "E"}], "metaId": "df3c7df760964d08b092f7c9d0eb9513", "status": "active", "version": "1.0.0", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}', 'system', '2025-04-18 12:12:24.826149') ON CONFLICT (definition_id) DO NOTHING; + +INSERT INTO "public"."flow_graph" ("id", "version", "tenant", "status", "name", "data", "created_by", "created_at", "updated_by", "updated_at", "previous", "is_deleted") VALUES ('df3c7df760964d08b092f7c9d0eb9513', '1.0.0', '31f20efc7e0848deab6a6bc10fc3021e', 'active', 'df3c7df760964d08b092f7c9d0eb9513', '{"id":"df3c7df760964d08b092f7c9d0eb9513","title":"df3c7df760964d08b092f7c9d0eb9513","source":"elsa","type":"jadeFlowGraph","tenant":"31f20efc7e0848deab6a6bc10fc3021e","setting":{"borderColor":"#047bfc","backColor":"whitesmoke","headColor":"steelblue","fontColor":"steelblue","captionfontColor":"whitesmoke","fontFace":"arial","captionfontFace":"arial black","fontSize":12,"captionfontSize":14,"fontStyle":"normal","captionfontStyle":"normal","fontWeight":"lighter","captionfontWeight":"lighter","hAlign":"center","vAlign":"top","captionhAlign":"center","lineHeight":1.5,"lineWidth":2,"captionlineHeight":1,"focusMargin":0,"focusBorderColor":"#047bfc","focusFontColor":"darkorange","focusBackColor":"whitesmoke","mouseInColor":"orange","mouseInBorderColor":"#047bfc","mouseInFontColor":"orange","mouseInBackColor":"whitesmoke","borderWidth":1,"focusBorderWidth":1,"globalAlpha":1,"backAlpha":0.15,"cornerRadius":4,"dashWidth":0,"autoText":false,"autoHeight":false,"autoWidth":false,"margin":25,"pad":10,"code":"","rotateDegree":0,"shadow":"","focusShadow":"","shadowData":"2px 2px 4px","outstanding":false,"pDock":"none","dockMode":"none","priority":0,"infoType":{"name":"none","next":"INFORMATION"},"progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"progressPercent":0.65,"showedProgress":false,"itemPad":[5,5,5,5],"itemScroll":{"x":0,"y":0},"scrollLock":{"x":false,"y":false},"resizeable":true,"selectable":true,"rotateAble":true,"editable":true,"moveable":true,"dragable":true,"visible":true,"deletable":true,"allowLink":true,"shared":false,"strikethrough":false,"underline":false,"numberedList":false,"bulletedList":false,"enableAnimation":false,"enableSocial":true,"emphasized":false,"bulletSpeed":1,"tag":{},"allNodeNumLimit":99,"sameTypeNodeNumLimit":19,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10},"pages":[{"x":-1225.7009556725914,"y":160.9007936507931,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":true,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.7000000000000001,"scaleY":0.7000000000000001,"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc","shapes":[{"x":-170.8928571428571,"y":32.5,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":false,"index":0,"width":360,"height":728,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","isVisible":true,"isRequired":true,"description":"这是用户输入的问题。","displayName":"用户问题","disableModifiable":true}],"config":[{"allowAdd":true}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]},{"id":"91138f09-b635-43df-95c6-1fe3d1745830","from":"Expand","name":"appConfig","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dadf","from":"Input","name":"appChatStyle","type":"String","value":"default"}]}],"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":2564.107142857142,"y":-145.1785714285714,"id":"jadesoux5i","pad":6,"bold":false,"text":"添加模型结束","type":"endNodeEnd","dirty":false,"index":1,"width":360,"height":313,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"54dab89c-5693-4082-baa7-12c648d812f7","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"ffad80c2-3f60-4d57-93b2-c2362a5dab9c","from":"Reference","name":"finalOutput","type":"String","value":["output"],"editable":true,"isRequired":true,"description":"","referenceId":"output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1","referenceKey":"output","referenceNode":"jadehyxese"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"c26bf2ca-75b3-4a6f-bc47-132c2e170895","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","mouseInBorderColor":"rgb(4, 123, 252)"},{"x":278.02238424402026,"y":428.19444444444423,"id":"jadevpescp","pad":6,"bold":false,"text":"获取用户模型列表","type":"toolInvokeNodeState","dirty":false,"index":2,"width":360,"height":297,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"userId"}],"return":{"type":"array"},"uniqueName":"16816279-411b-48e1-b754-191abd61dcd0"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"userId_f4725c4a-ac63-4b51-ae86-8c0baca9b71d","from":"Reference","name":"userId","type":"String","value":["userId"],"isRequired":true,"description":"用户id","referenceId":"userId","referenceKey":"userId","referenceNode":"_systemEnv"}],"outputParams":[{"id":"output_78982629-c58c-4211-9f4b-369bdcfef31f","name":"output","type":"Array","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":189.1071428571429,"y":396.5,"id":"jadexixl9n","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":3,"textX":0,"textY":0,"width":88.91524138687737,"hAlign":"center","height":180.19444444444423,"italic":false,"margin":20,"toShape":"jadevpescp","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":722.4668286884644,"y":289.30555555555554,"id":"jade1osjbn","pad":6,"bold":false,"text":"人工表单","type":"manualCheckNodeState","dirty":true,"index":4,"width":360,"height":489,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","imgUrl":"http://localhost:8001/api/jober/static/smart_form/6befc536-7e6d-48b5-8dcb-1c4d04ca4e92/form.png","taskId":"1568509614c245a39ce53bda9c3c2ec1","formName":"模型管理表单","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"models_6db2dcd7-94f1-4eb9-88d8-69e68e3fc990","name":"models","type":"Array","from":"Reference","isRequired":true,"referenceNode":"jadevpescp","referenceId":"output_78982629-c58c-4211-9f4b-369bdcfef31f","referenceKey":"output","value":["output"]}],"outputParams":[{"id":"output_14c32c0b-52d1-4d40-8b5d-b0470f3ddd9a","name":"output","type":"Object","value":[{"id":"output_c2404fc9-56de-4cca-9706-0e63c49ad4d0","name":"info","type":"Object","value":[{"id":"d270cf95-6f76-49bd-81b2-66692109034b","name":"apiKey","type":"String","value":"String"},{"id":"ae07dc15-b235-4139-ad89-9b6160988472","name":"userId","type":"String","value":"String"},{"id":"8bfa7821-ff6b-4e53-bfbc-213d68a613a2","name":"baseUrl","type":"String","value":"String"},{"id":"a29201e7-7fe3-4f3f-834d-899a6cfbd79e","name":"modelId","type":"String","value":"String"},{"id":"62d172c1-b852-4b5d-97d8-aa303956aae0","name":"isDefault","type":"Integer","value":"Integer"},{"id":"ce53a197-f57a-4bfa-9343-b6f993b61883","name":"modelName","type":"String","value":"String"}]},{"id":"bed9c7b2-83dc-4b3e-9f9c-915e20663037","name":"action","type":"String","value":"String"}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"manualCheckComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":638.0223842440203,"y":576.6944444444442,"id":"jade419ps4","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":5,"textX":0,"textY":0,"width":84.44444444444412,"hAlign":"center","height":-42.88888888888869,"italic":false,"margin":20,"toShape":"jade1osjbn","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadevpescp","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1150.8795271011613,"y":195.33730158730177,"id":"jade0305an","pad":6,"bold":false,"text":"条件","type":"conditionNodeCondition","dirty":false,"index":6,"width":600,"height":735,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e","type":"if","runnable":true,"conditions":[{"id":"eaa48316-b65d-49f4-a9c2-0d1261cb4efe","value":[{"id":"3288e216-4f89-473c-b6b9-e9d80e66fe0c","from":"Reference","name":"left","type":"String","value":["output","action"],"referenceId":"bed9c7b2-83dc-4b3e-9f9c-915e20663037","referenceKey":"action","referenceNode":"jade1osjbn"},{"id":"e63c3cba-9edc-4d94-af84-e8ad0c319aa8","from":"Input","name":"right","type":"String","value":"add","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"42754fab-4274-4e32-9811-9088eb80f691","type":"if","runnable":true,"conditions":[{"id":"7122796c-36ce-41ea-96be-0ce69cfe704b","value":[{"id":"b9a64d7a-1ebc-40e1-a02b-2a793154b5fa","from":"Reference","name":"left","type":"String","value":["output","action"],"referenceId":"bed9c7b2-83dc-4b3e-9f9c-915e20663037","referenceKey":"action","referenceNode":"jade1osjbn"},{"id":"51b80568-1f7a-4cca-8c31-c42f41d5b63d","from":"Input","name":"right","type":"String","value":"delete","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"79592b84-07fd-4212-9f50-71479aa86b22","type":"if","runnable":true,"conditions":[{"id":"42692336-de84-4023-864e-3ee666da5d93","value":[{"id":"14a9b304-09dd-45ce-a421-6aa91703a1f4","from":"Reference","name":"left","type":"String","value":["output","action"],"referenceId":"bed9c7b2-83dc-4b3e-9f9c-915e20663037","referenceKey":"action","referenceNode":"jade1osjbn"},{"id":"557856d5-18c8-4a67-91d4-0cf4faec4060","from":"Input","name":"right","type":"String","value":"switch","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"50b171e2-b8b6-46cb-89a3-a6e0883ae710","type":"else","runnable":true,"conditions":[{"id":"90303c25-9b1b-4e87-8af8-e3c997331c38","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1082.4668286884644,"y":533.8055555555555,"id":"jadezmsms0","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":7,"textX":0,"textY":0,"width":68.41269841269695,"hAlign":"center","height":29.031746031746252,"italic":false,"margin":20,"toShape":"jade0305an","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade1osjbn","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1975.165241386877,"y":-157.51984126984166,"id":"jadehyxese","pad":6,"bold":false,"text":"添加模型","type":"toolInvokeNodeState","dirty":false,"index":8,"width":360,"height":411,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"modelName"},{"name":"baseUrl"},{"name":"apiKey"},{"name":"userId"}],"return":{"type":"string"},"uniqueName":"77ee3e02-5870-4338-a8be-52c8ed42f3c7"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"modelName_216f7038-ade2-465a-9d24-9fb39d4af5e5","from":"Reference","name":"modelName","type":"String","value":["output","info","modelName"],"isRequired":true,"description":"模型名称","referenceId":"ce53a197-f57a-4bfa-9343-b6f993b61883","referenceKey":"modelName","referenceNode":"jade1osjbn"},{"id":"baseUrl_49ee4206-7c9c-4311-b749-da274d3d14df","from":"Reference","name":"baseUrl","type":"String","value":["output","info","baseUrl"],"isRequired":true,"description":"模型访问地址","referenceId":"8bfa7821-ff6b-4e53-bfbc-213d68a613a2","referenceKey":"baseUrl","referenceNode":"jade1osjbn"},{"id":"apiKey_d776d4db-4b49-4dee-b054-5b43d28c9c64","from":"Reference","name":"apiKey","type":"String","value":["output","info","apiKey"],"isRequired":true,"description":"模型访问的 API Key","referenceId":"d270cf95-6f76-49bd-81b2-66692109034b","referenceKey":"apiKey","referenceNode":"jade1osjbn"},{"id":"userId_9a314ce2-d74b-471e-a7f8-ac5bf189f253","from":"Reference","name":"userId","type":"String","value":["userId"],"isRequired":true,"description":"用户id","referenceId":"userId","referenceKey":"userId","referenceNode":"_systemEnv"}],"outputParams":[{"id":"output_3a2ca725-4c94-4ead-bfcd-ffb8ac5ee3b1","name":"output","type":"String","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1973.736669958303,"y":369.623015873016,"id":"jadeas9z1s","pad":6,"bold":false,"text":"删除模型","type":"toolInvokeNodeState","dirty":false,"index":9,"width":360,"height":335,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"modelId"},{"name":"userId"}],"return":{"type":"string"},"uniqueName":"ff0a4b59-0be7-436f-8931-6a599ed9f8f3"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"modelId_e329ead9-4485-453a-8973-9047c1e06a9a","from":"Reference","name":"modelId","type":"String","value":["output","info","modelId"],"isRequired":true,"description":"模型id","referenceId":"a29201e7-7fe3-4f3f-834d-899a6cfbd79e","referenceKey":"modelId","referenceNode":"jade1osjbn"},{"id":"userId_47ee255d-9eeb-418c-aabc-8eb7871d0be5","from":"Reference","name":"userId","type":"String","value":["output","info","userId"],"isRequired":true,"description":"用户id","referenceId":"ae07dc15-b235-4139-ad89-9b6160988472","referenceKey":"userId","referenceNode":"jade1osjbn"}],"outputParams":[{"id":"output_786cf758-f9af-4959-83e7-251a01e04fb0","name":"output","type":"String","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1739.953710973929,"y":381.97731623573924,"id":"jade2cfmdw","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":10,"textX":0,"textY":0,"width":235.21153041294792,"hAlign":"center","height":-333.9971575055809,"italic":false,"margin":20,"toShape":"jadehyxese","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0305an","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0|20e8e83b-31f2-4ba5-93b7-0920b2b7cc2e"},{"x":1966.593812815448,"y":741.0515873015875,"id":"jade3q2qyw","pad":6,"bold":false,"text":"切换默认模型","type":"toolInvokeNodeState","dirty":false,"index":11,"width":360,"height":335,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"modelId"},{"name":"userId"}],"return":{"type":"string"},"uniqueName":"f76205df-1814-49b9-a473-c03ee99770b7"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"modelId_1312ce29-7bb9-4ac2-8cb9-e53a3f6b68c8","from":"Reference","name":"modelId","type":"String","value":["output","info","modelId"],"isRequired":true,"description":"默认模型id","referenceId":"a29201e7-7fe3-4f3f-834d-899a6cfbd79e","referenceKey":"modelId","referenceNode":"jade1osjbn"},{"id":"userId_71b52489-3462-48ea-824c-ce04d99ba7e2","from":"Reference","name":"userId","type":"String","value":["output","info","userId"],"isRequired":true,"description":"用户id","referenceId":"ae07dc15-b235-4139-ad89-9b6160988472","referenceKey":"userId","referenceNode":"jade1osjbn"}],"outputParams":[{"id":"output_4113326b-5e9f-495e-8112-2ca9a0dca171","name":"output","type":"String","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"toolInvokeComponent","focusBackColor":"white","sourcePlatform":"","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1739.953710973929,"y":569.9773031567772,"id":"jade31ntj1","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":12,"textX":0,"textY":0,"width":233.7829589843741,"hAlign":"center","height":-32.85428728376121,"italic":false,"margin":20,"toShape":"jadeas9z1s","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0305an","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-1|42754fab-4274-4e32-9811-9088eb80f691"},{"x":1739.953710973929,"y":757.977268279545,"id":"jadedmxi6l","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":13,"textX":0,"textY":0,"width":226.64010184151903,"hAlign":"center","height":150.57431902204246,"italic":false,"margin":20,"toShape":"jade3q2qyw","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0305an","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-2|79592b84-07fd-4212-9f50-71479aa86b22"},{"x":2335.165241386877,"y":47.98015873015834,"id":"jadejcuwis","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":14,"textX":0,"textY":0,"width":228.9419014702653,"hAlign":"center","height":-36.658730158729725,"italic":false,"margin":20,"toShape":"jadesoux5i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadehyxese","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2633.736669958307,"y":322.4801587301588,"id":"jade7dv633","pad":6,"bold":false,"text":"删除模型结束","type":"endNodeEnd","dirty":false,"index":15,"width":360,"height":313,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"104c12c3-804b-419f-a1c7-24b66516ed42","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"020da94c-e511-4ee9-8e2b-0ce5eb3103cb","from":"Reference","name":"finalOutput","type":"String","value":["output"],"editable":true,"isRequired":true,"description":"","referenceId":"output_786cf758-f9af-4959-83e7-251a01e04fb0","referenceKey":"output","referenceNode":"jadeas9z1s"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"0367a295-adad-4a19-a08e-ab04c37db3ac","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2602.308098529733,"y":732.4801587301587,"id":"jade1thrs1","pad":6,"bold":false,"text":"结束_2","type":"endNodeEnd","dirty":false,"index":16,"width":360,"height":313,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"9f2d49d0-240d-46bb-80cb-a4391ac0de69","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"0f777bfd-06e5-452a-b80e-509057551d72","from":"Reference","name":"finalOutput","type":"String","value":["output"],"editable":true,"isRequired":true,"description":"","referenceId":"output_4113326b-5e9f-495e-8112-2ca9a0dca171","referenceKey":"output","referenceNode":"jade3q2qyw"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"fac8c254-46c4-42b2-92f0-32990dcd0086","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1863.736669958305,"y":1129.6230158730177,"id":"jadeifaftx","pad":6,"bold":false,"text":"退出表单结束","type":"endNodeEnd","dirty":false,"index":17,"width":360,"height":313,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"59007e69-9047-42d4-bfcd-3150e78f0215","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"cffd1f94-832a-4f23-b8d5-b72db4d521c1","from":"Input","name":"finalOutput","type":"String","value":"已退出,请重新开始对话来发起新的模型配置请求。","editable":true,"isRequired":true,"description":""}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"6f7d0c10-fe39-4dbe-8e72-0d5202e42a22","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2333.736669958303,"y":537.123015873016,"id":"jadei8jdd4","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":18,"textX":0,"textY":0,"width":300.0000000000041,"hAlign":"center","height":-58.142857142857224,"italic":false,"margin":20,"toShape":"jade7dv633","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeas9z1s","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1739.953710973929,"y":870.9773478432309,"id":"jadecg9j3o","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":19,"textX":0,"textY":0,"width":123.78295898437591,"hAlign":"center","height":415.14566802978686,"italic":false,"margin":20,"toShape":"jadeifaftx","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0305an","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":2326.593812815448,"y":908.5515873015875,"id":"jadewcxlt5","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":20,"textX":0,"textY":0,"width":275.71428571428487,"hAlign":"center","height":-19.571428571428783,"italic":false,"margin":20,"toShape":"jade1thrs1","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade3q2qyw","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"}]}],"enableText":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"]},"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"]},"version":"1.0.0"}', 'system', '2025-04-18 12:12:24.368006', 'system', '2025-04-18 12:12:24.831911', NULL, 'f') ON CONFLICT (id, version) DO NOTHING; + +INSERT INTO "public"."store_app" ("like_count", "download_count", "source", "icon", "app_category", "tool_name", "tool_unique_name") VALUES (0, 0, 'system', '', 'chatbot', '模型配置应用', '7a76cbd2-881d-469b-b2df-76abed7d0b61') ON CONFLICT ("tool_unique_name") DO NOTHING; + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('模型配置应用', '{"name":"模型配置应用","description":"当你想要配置模型的时候,请使用我!","manualIntervention":false,"parameters":{"type":"object","properties":{"aippId":{"description":"the aipp id of the waterFlow tool","default":"0b4fe5a430104edfbe0dc6cff0ebea19","type":"string"},"tenantId":{"description":"the tenant id of the waterFlow tool","default":"31f20efc7e0848deab6a6bc10fc3021e","type":"string"},"inputParams":{"type":"object","properties":{"Question":{"type":"String","description":"这是用户输入的问题。"}},"required":["Question"],"order":["Question"]},"version":{"description":"the aipp version of the waterFlow tool","default":"1.0.0","type":"string"}},"required":["tenantId","aippId","version","inputParams"]},"return":{"type":"object","properties":{}},"order":["tenantId","aippId","version","inputParams"]}', '{"FIT":{"fitableId":"water.flow.invoke","genericableId":"07b51bd246594c159d403164369ce1db"},"APP":{"aippId":"0b4fe5a430104edfbe0dc6cff0ebea19","appCategory":"chatbot","version":"1.0.0","appId":"cec6bfe7cb3a444f8a26a97ea513e501"}}', 'null', '7a76cbd2-881d-469b-b2df-76abed7d0b61', '1.0.0', 't', '7a76cbd2-881d-469b-b2df-76abed7d0b61', '7a76cbd2-881d-469b-b2df-76abed7d0b61', '7a76cbd2-881d-469b-b2df-76abed7d0b61') ON CONFLICT ("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('7a76cbd2-881d-469b-b2df-76abed7d0b61', 'APP') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('7a76cbd2-881d-469b-b2df-76abed7d0b61', 'APP_TYPE_B653EDB7EB5A49BE91ABCD2C5877C6AD') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; \ No newline at end of file diff --git a/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/aipp_custom_model_center.sql b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/schema/create_tables/aipp_custom_model_center.sql similarity index 91% rename from app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/aipp_custom_model_center.sql rename to app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/schema/create_tables/aipp_custom_model_center.sql index d997efdd20..a10552c7dd 100644 --- a/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/aipp_custom_model_center.sql +++ b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/schema/create_tables/aipp_custom_model_center.sql @@ -1,8 +1,8 @@ create table if not exists t_app_engine_model ( "id" bigserial primary key not null, - "created_at" timestamp(9) default current_timestamp not null, - "updated_at" timestamp(9) default current_timestamp not null, + "created_at" timestamp default current_timestamp not null, + "updated_at" timestamp default current_timestamp not null, "created_by" varchar(64) default 'system' not null, "updated_by" varchar(64) default 'system' not null, "model_id" varchar(255) not null, @@ -26,8 +26,8 @@ comment on column t_app_engine_model.type is '模型类型'; create table if not exists t_app_engine_user_model ( "id" bigserial primary key not null, - "created_at" timestamp(9) default current_timestamp not null, - "updated_at" timestamp(9) default current_timestamp not null, + "created_at" timestamp default current_timestamp not null, + "updated_at" timestamp default current_timestamp not null, "created_by" varchar(64) default 'system' not null, "updated_by" varchar(64) default 'system' not null, "user_id" varchar(255) not null, diff --git a/app-builder/plugins/aipp-custom-model-center/src/test/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenterTest.java b/app-builder/plugins/aipp-custom-model-center/src/test/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenterTest.java index 881ec7501c..87313679d5 100644 --- a/app-builder/plugins/aipp-custom-model-center/src/test/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenterTest.java +++ b/app-builder/plugins/aipp-custom-model-center/src/test/java/modelengine/fit/jade/aipp/model/service/impl/CustomAippModelCenterTest.java @@ -61,6 +61,7 @@ void shouldGetResultWhenFetchModelListGivenUserModelRepoHasData() { ModelAccessInfo targetModelAccessInfo = modelList.getModels().get(0); Assertions.assertEquals(modelPo.getName(), targetModelAccessInfo.getServiceName()); Assertions.assertEquals("tag1,user1", targetModelAccessInfo.getTag()); + Assertions.assertEquals(modelPo.getBaseUrl(), targetModelAccessInfo.getBaseUrl()); Mockito.verify(this.defaultModelCenter, Mockito.times(0)) .fetchModelList(Mockito.any(), Mockito.any(), Mockito.any()); } diff --git a/app-builder/plugins/aipp-document-extract-node/pom.xml b/app-builder/plugins/aipp-document-extract-node/pom.xml index c149dbd44b..419baafb37 100644 --- a/app-builder/plugins/aipp-document-extract-node/pom.xml +++ b/app-builder/plugins/aipp-document-extract-node/pom.xml @@ -19,9 +19,8 @@ aipp-service - org.fitframework.fel - tool-service - 3.5.0-M2.1 + modelengine.fit.jade.service + store-service modelengine.fit.jade.fel diff --git a/app-builder/plugins/aipp-document-extract-node/src/main/java/modelengine/fit/jade/aipp/document/extractor/TextExtractor.java b/app-builder/plugins/aipp-document-extract-node/src/main/java/modelengine/fit/jade/aipp/document/extractor/TextExtractor.java index 77446ef011..943b853c63 100644 --- a/app-builder/plugins/aipp-document-extract-node/src/main/java/modelengine/fit/jade/aipp/document/extractor/TextExtractor.java +++ b/app-builder/plugins/aipp-document-extract-node/src/main/java/modelengine/fit/jade/aipp/document/extractor/TextExtractor.java @@ -10,12 +10,12 @@ import modelengine.fel.tool.model.transfer.ToolData; import modelengine.fel.tool.service.ToolExecuteService; -import modelengine.fel.tool.service.ToolService; import modelengine.fit.jober.aipp.service.OperatorService.FileType; import modelengine.fitframework.annotation.Component; import modelengine.fitframework.util.CollectionUtils; import modelengine.fitframework.util.MapBuilder; import modelengine.jade.common.exception.ModelEngineException; +import modelengine.jade.store.service.ToolService; import java.util.List; import java.util.Map; diff --git a/app-builder/plugins/aipp-document-extract-node/src/test/java/modelengine/fit/jade/aipp/document/extractor/ExtractorTest.java b/app-builder/plugins/aipp-document-extract-node/src/test/java/modelengine/fit/jade/aipp/document/extractor/ExtractorTest.java index 78c96f6285..ecf9421b40 100644 --- a/app-builder/plugins/aipp-document-extract-node/src/test/java/modelengine/fit/jade/aipp/document/extractor/ExtractorTest.java +++ b/app-builder/plugins/aipp-document-extract-node/src/test/java/modelengine/fit/jade/aipp/document/extractor/ExtractorTest.java @@ -15,7 +15,7 @@ import modelengine.fel.tool.model.transfer.ToolData; import modelengine.fel.tool.service.ToolExecuteService; -import modelengine.fel.tool.service.ToolService; +import modelengine.jade.store.service.ToolService; import modelengine.jade.voice.service.VoiceService; import modelengine.fel.core.chat.ChatMessage; diff --git a/app-builder/plugins/aipp-parallel-tool/pom.xml b/app-builder/plugins/aipp-parallel-tool/pom.xml index 0f8f0268d0..d25329dc47 100644 --- a/app-builder/plugins/aipp-parallel-tool/pom.xml +++ b/app-builder/plugins/aipp-parallel-tool/pom.xml @@ -135,7 +135,7 @@ + todir="../../../build/plugins"/> diff --git a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequest.java b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequest.java index 74431294e9..4f4a5295ff 100644 --- a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequest.java +++ b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequest.java @@ -123,13 +123,10 @@ private boolean postUndoTask() { task.getIndex()); this.doingToolCallTasks.put(task.getIndex(), task); - Map toolArgs = task.getToolCall() - .getArgs() - .stream() - .collect(Collectors.toMap(Argument::getName, Argument::getValue)); + String jsonArgs = JSONObject.toJSONString(task.getToolCall().getArgs(), + SerializerFeature.WriteMapNullValue); this.complete(task, - JSONArray.parse(this.syncToolCall.call(task.getToolCall().getUniqueName(), - JSONObject.toJSONString(toolArgs, SerializerFeature.WriteMapNullValue)))); + JSONArray.parse(this.syncToolCall.call(task.getToolCall().getUniqueName(), jsonArgs))); } catch (Throwable ex) { this.setException(task, ex); } finally { diff --git a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/entities/ToolCall.java b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/entities/ToolCall.java index fd95131c07..d9b72258f9 100644 --- a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/entities/ToolCall.java +++ b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/entities/ToolCall.java @@ -11,7 +11,7 @@ import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; +import java.util.Map; /** * 并行工具调用信息。 @@ -25,6 +25,6 @@ @AllArgsConstructor public class ToolCall { private String uniqueName; - private List args; + private Map args; private String outputName; } diff --git a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/service/impl/ParallelToolServiceImpl.java b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/service/impl/ParallelToolServiceImpl.java index 17c148e2f6..2c9e246396 100644 --- a/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/service/impl/ParallelToolServiceImpl.java +++ b/app-builder/plugins/aipp-parallel-tool/src/main/java/modelengine/fit/jade/aipp/tool/parallel/service/impl/ParallelToolServiceImpl.java @@ -94,11 +94,11 @@ private void validateToolCall(ToolCall toolCall) { Validation.isTrue(StringUtils.isNotEmpty(toolCall.getUniqueName()), "The tool unique name should not be empty."); Validation.notNull(toolCall.getArgs(), "The tool args should not be null."); - toolCall.getArgs().forEach(arg -> { + toolCall.getArgs().entrySet().forEach(arg -> { Validation.notNull(arg, StringUtils.format("The tool arg should not be null. [toolUniqueName={0}]", toolCall.getUniqueName())); - Validation.isTrue(StringUtils.isNotEmpty(arg.getName()), + Validation.isTrue(StringUtils.isNotEmpty(arg.getKey()), StringUtils.format("The tool arg name should not be empty. [toolUniqueName={0}]", toolCall.getUniqueName())); }); diff --git a/app-builder/plugins/aipp-parallel-tool/src/main/resources/sql/data/aipp_parallel_tool.sql b/app-builder/plugins/aipp-parallel-tool/src/main/resources/sql/data/aipp_parallel_tool.sql new file mode 100644 index 0000000000..7ac95d1f12 --- /dev/null +++ b/app-builder/plugins/aipp-parallel-tool/src/main/resources/sql/data/aipp_parallel_tool.sql @@ -0,0 +1,12 @@ +INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "deploy_status", "is_builtin", "source", "icon") VALUES ('d44a239ed854ef94af0f032a526907e20ba8a56ebb4f851cc6956c0172a144e7', '并行', '{"uniqueness.groupId":"modelengine.fit.jade.plugin","pluginFullName":"aipp-parallel-tool-1.0.0-SNAPSHOT_1745841582889.jar","checksum":"42266c0ada1415feb0b689f288b4063b9406506ff7da16eca0332b91a01df20e","name":"并行","description":"并行执行工具","uniqueness.artifactId":"aipp-parallel-tool","type":"java"}', 'DEPLOYED', 't', '', NULL) ON CONFLICT ("plugin_id") DO NOTHING; + +INSERT INTO "public"."store_plugin_tool" ("tool_name", "plugin_id", "tool_unique_name") VALUES ('parallelToolDefault', 'd44a239ed854ef94af0f032a526907e20ba8a56ebb4f851cc6956c0172a144e7', '1d0c8164-1aea-4264-879f-2c7898d13fb9') ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; + +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('parallelTool', '{"name":"parallelTool","description":"用于并行执行工具","parameters":{"type":"object","properties":{"toolCalls":{"defaultValue":"","description":"并行调用的工具列表","name":"toolCalls","type":"array","items":{"type":"object","properties":{"uniqueName":{"type":"string"},"args":{"type":"object"},"outputName":{"type":"string"}}},"examples":"","required":[]},"config":{"defaultValue":"","description":"并行调用的配置","name":"config","type":"object","properties":{"concurrency":{"type":"integer"}},"examples":"","required":[]},"context":{"defaultValue":"","description":"调用时的上下文信息","name":"context","type":"object","examples":"","required":[]}},"required":["toolCalls"]},"order":["toolCalls","config","context"],"parameterExtensions":null,"return":{"type":"object","convertor":""}}', 'ParallelTool') ON CONFLICT ("definition_group_name", "name") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('1d0c8164-1aea-4264-879f-2c7898d13fb9', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('1d0c8164-1aea-4264-879f-2c7898d13fb9', 'PARALLELNODESTATE') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('1d0c8164-1aea-4264-879f-2c7898d13fb9', 'BASIC') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; + + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('parallelToolDefault', '{"name":"parallelTool","description":"用于并行执行工具","parameters":{"type":"object","properties":{"toolCalls":{"defaultValue":"","description":"并行调用的工具列表","name":"toolCalls","type":"array","items":{"type":"object","properties":{"uniqueName":{"type":"string"},"args":{"type":"object"},"outputName":{"type":"string"}}},"examples":"","required":[]},"config":{"defaultValue":"","description":"并行调用的配置","name":"config","type":"object","properties":{"concurrency":{"type":"integer"}},"examples":"","required":[]},"context":{"defaultValue":"","description":"调用时的上下文信息","name":"context","type":"object","examples":"","required":[]}},"required":["toolCalls"]},"order":["toolCalls","config","context"],"parameterExtensions":null,"return":{"type":"object","convertor":""}}', '{"FIT":{"genericableId":"modelengine.fit.jade.aipp.tool.parallel","fitableId":"default"}}', '{"tags":["FIT","BASIC","PARALLELNODESTATE"]}', '1d0c8164-1aea-4264-879f-2c7898d13fb9', '1.0.0', 't', 'ParallelToolImpl', 'parallelTool', 'ParallelTool') ON CONFLICT ("unique_name", "version") DO NOTHING; \ No newline at end of file diff --git a/app-builder/plugins/aipp-parallel-tool/src/test/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequestTest.java b/app-builder/plugins/aipp-parallel-tool/src/test/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequestTest.java index d26e5ff570..13a2025f97 100644 --- a/app-builder/plugins/aipp-parallel-tool/src/test/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequestTest.java +++ b/app-builder/plugins/aipp-parallel-tool/src/test/java/modelengine/fit/jade/aipp/tool/parallel/domain/BatchRequestTest.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -47,9 +48,9 @@ class BatchRequestTest { @Test void shouldCallExecutorByConcurrencyWhenPostGivenToolCall() { - List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new ArrayList<>()).build(), - ToolCall.builder().uniqueName("u2").args(new ArrayList<>()).build(), - ToolCall.builder().uniqueName("u3").args(new ArrayList<>()).build()); + List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new HashMap<>()).build(), + ToolCall.builder().uniqueName("u2").args(new HashMap<>()).build(), + ToolCall.builder().uniqueName("u3").args(new HashMap<>()).build()); Config config = Config.builder().concurrency(2).build(); Mockito.doNothing().when(this.taskExecutor).post(Mockito.any()); @@ -67,10 +68,10 @@ void shouldCallExecutorByConcurrencyWhenPostGivenToolCall() { @Test void shouldGetResultWhenAwaitGivenToolCallSuccessfully() { List toolCalls = - Arrays.asList(ToolCall.builder().uniqueName("u1").args(new ArrayList<>()).outputName("1").build(), + Arrays.asList(ToolCall.builder().uniqueName("u1").args(new HashMap<>()).outputName("1").build(), ToolCall.builder() .uniqueName("u2") - .args(Collections.singletonList(Argument.builder().name("a").value(1).build())) + .args(MapBuilder.get().put("a", 1).build()) .outputName("2") .build()); Config config = Config.builder().concurrency(1).build(); @@ -104,8 +105,8 @@ void shouldGetResultWhenAwaitGivenToolCallSuccessfully() { @Test void shouldThrowExceptionWhenAwaitGivenToolCallException() { - List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new ArrayList<>()).build(), - ToolCall.builder().uniqueName("u2").args(new ArrayList<>()).build()); + List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new HashMap<>()).build(), + ToolCall.builder().uniqueName("u2").args(new HashMap<>()).build()); Config config = Config.builder().concurrency(1).build(); Mockito.doAnswer((Answer) invocation -> { Runnable runnable = invocation.getArgument(0); @@ -130,8 +131,8 @@ void shouldThrowExceptionWhenAwaitGivenToolCallException() { @Test void shouldNotExecuteRemainToolWhenAwaitGivenInstanceNotRunning() { - List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new ArrayList<>()).build(), - ToolCall.builder().uniqueName("u2").args(new ArrayList<>()).build()); + List toolCalls = Arrays.asList(ToolCall.builder().uniqueName("u1").args(new HashMap<>()).build(), + ToolCall.builder().uniqueName("u2").args(new HashMap<>()).build()); Config config = Config.builder().concurrency(1).build(); Mockito.doAnswer((Answer) invocation -> { Runnable runnable = invocation.getArgument(0); diff --git a/app-builder/plugins/plugins-show-case-parent/aito-data/src/main/resources/sql/data/tr_t_wenjie.sql b/app-builder/plugins/plugins-show-case-parent/aito-data/src/main/resources/sql/data/tr_t_wenjie.sql new file mode 100644 index 0000000000..c8dceea14a --- /dev/null +++ b/app-builder/plugins/plugins-show-case-parent/aito-data/src/main/resources/sql/data/tr_t_wenjie.sql @@ -0,0 +1,59 @@ +INSERT INTO "public"."app_builder_app" ("id", "name", "create_by", "create_at", "update_by", "update_at", "config_id", "flow_graph_id", "tenant_id", "type", "version", "attributes", "state", "app_built_type", "app_category", "collection_usr_cnt", "is_deleted", "path", "app_type", "app_suite_id", "is_active", "status", "unique_name", "publish_at", "app_id") VALUES ('550177e8d0e34014a2d95988ef1c67c5', '问界试驾助手', 'Jade', '2025-04-19 09:08:09.532855', 'Jade', '2025-04-19 09:15:37.326128', '2aee7721a4fb46b3b0a789f9037b040d', '5711f3230eb94abdb168e61d2082d1d2', '31f20efc7e0848deab6a6bc10fc3021e', 'app', '1.0.0', '{"icon": "", "name": "问界试驾助手", "greeting": "", "store_id": "2a3141b2-8c83-40c2-b2b6-103746c125d3", "is_update": false, "description": "", "publishedUpdateLog": "", "publishedDescription": "问界试驾助手"}', 'active', 'workflow', 'chatbot', 0, 0, 'pTTfcIHbWg8gW0sK', '4db152b24f94473ab683b1acbfe3c865', 'dfe319109bc84f6793645e3483c029ca', 't', 'published', '2a3141b2-8c83-40c2-b2b6-103746c125d3', '2025-04-19 09:15:56', '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config" ("id", "form_id", "app_id", "tenant_id", "create_by", "create_at", "update_by", "update_at", "is_deleted") VALUES ('2aee7721a4fb46b3b0a789f9037b040d', 'b8986770a6ffef44bbf2a9f26d6fc1be', '550177e8d0e34014a2d95988ef1c67c5', '31f20efc7e0848deab6a6bc10fc3021e', 'Jade', '2025-04-19 09:08:09.532855', 'Jade', '2025-04-19 09:15:37.326128', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('a34e2263306c40d4ae094f3824f1a597', 'jadewdnjbq', '662b1e243b8e43788092b8bff6b7a5f6', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('08b052891c9c427cbbeb3691d417d1a4', 'jadewdnjbq', 'ae708d5828194845afc15402d2849b8b', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('900566e10af8443483de407ac06d8757', 'jade6qm5eg', 'b7a7c427418d4d8eb5e8f96165a1d6a6', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('457189dd226b4da49c1ba69de02d673a', 'jade0pg2ag', 'c383886d8a154b3f9e94950467d92432', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('b7f5c36e7bdc4d7daa4b52ca1235f71b', 'jadewdnjbq', '6290bce2ccf249239bc52866604a3920', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('c0ea9932b52f4c1b9190defa05e0dd13', 'jadewdnjbq', 'd8bd923772b247cf88bfcb7571059066', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('b1b212206a2b4b799a1882656b1caebe', 'jadewdnjbq', '01ec06bcc0e7483da79b7f1c55b7851e', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_config_property" ("id", "node_id", "form_property_id", "config_id", "is_deleted") VALUES ('16af6a6c453941e9a0ade975e1f5e08a', 'jadewdnjbq', 'ba6cd122de7d4e20a8e69702379a4b9f', '2aee7721a4fb46b3b0a789f9037b040d', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_flow_graph" ("id", "name", "create_by", "create_at", "update_by", "update_at", "appearance", "is_deleted") VALUES ('5711f3230eb94abdb168e61d2082d1d2', 'LLM模板', 'Jade', '2025-04-19 09:08:09.532855', 'Jade', '2025-04-19 09:15:37.326128', '{"id": "5711f3230eb94abdb168e61d2082d1d2", "type": "jadeFlowGraph", "pages": [{"x": -449.2228577394942, "y": 648.7782362023345, "id": "elsa-page:tvp1s6", "bold": false, "mode": "configuration", "text": "newFlowPage", "type": "jadeFlowPage", "dirty": true, "index": 0, "width": 1600, "hAlign": "left", "height": 800, "isPage": true, "italic": false, "scaleX": 0.7732920492783848, "scaleY": 0.7732920492783848, "shapes": [{"x": -1123.5461204767116, "y": 184.25814386932322, "id": "jade6qm5eg", "pad": 6, "bold": false, "text": "开始", "type": "startNodeStart", "dirty": false, "index": 0, "width": 360, "height": 226, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": false}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": true}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}], "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": false, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "startComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "mouseInBorderColor": "rgba(28,31,35,.08)"}, {"x": 819.1428754447782, "y": 139.13955828369046, "id": "jadewdnjbq", "pad": 6, "bold": false, "text": "大模型", "type": "llmNodeState", "dirty": false, "index": 1, "width": 360, "height": 344, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "31ba235d-1b26-4780-a7a7-32eca9500919", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "83653b54-dd04-4da9-957d-adb7c2728632", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "dd588a17-a69c-40c0-859a-d9930202a148", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "6c414e75-971e-403a-b2b1-c6850f128cc4", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "db5fdafa-4cbf-44ba-9cca-8a98f1f771f4", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "88f74d78-4711-4f81-a2e7-74d0034c5e88", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "35a710cf-1b79-4523-b16f-b50878d677fe", "from": "Input", "name": "template", "type": "String", "value": "如果用户未指定具体车型,则按照用户问题,结合工具提供的问界车型信息,给出简略概要类的回答。\n问题:{{query}} "}, {"id": "38fb27a1-71f4-4fcc-87d5-9d8a880bc04d", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "eee66922-4304-4209-89fc-b13ffa101630", "from": "Reference", "name": "query", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "a6865419-867c-4bfb-855c-f5c1876c965a", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "29d1707c-beaa-44c8-9eb2-08462880b031", "from": "Input", "name": "问界车型信息查询", "tags": ["FIT"], "type": "String", "value": "7cba6fdd-8c6d-410f-81dc-775ffe96902b", "version": "1.0.0"}]}, {"id": "308e2023-a8e9-486e-9784-8680addbb786", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "68f92923-d5da-42ce-8478-d7ac7d90664e", "from": "Input", "name": "systemPrompt", "type": "String", "value": "你是一个具备自主工具调用能力的智能体,可以展示中间的思考过程给用户。你的回答有两种情况:\n\n无需调用外部工具\n\n如果问题可通过已有对话历史或直接推理得到答案,直接输出最终结果,不需使用任何标签包装,也不显示详细思考过程。\n需要调用外部工具解决的复杂问题\n\n必须采用以下严格的标签体系输出,每个标签之间空一行,且仅展示真实的工具调用结果:\n...:展示你内部的思考过程。注意,这部分内容可以展示给用户,但仅限于描述思路,不应包含任何伪造的工具调用结果。\n...:描述你准备调用工具的原因和计划。此处仅说明你需要调用哪个工具以及原因,工具的名称对人类阅读要友好,切勿直接模拟或输出工具返回内容。\n...:当你真正调用某个工具后,等待工具反馈,然后将工具调用的返回结果做非常简略的摘要后放在此标签内,摘要字数在20字以内。绝对禁止在未获得真实工具反馈前预先构造。 标签内容。\n...:在获取所有真实工具调用结果后,将整合信息给出最终答案。\n**重要要求**:\n- **不要输出tool_call标签**。\n- **答案必须详细完整**,不仅仅是工具返回结果的简单总结,而是对结果进行深入分析和整合,并提供背景解释、推理过程和可行性分析。\n- **确保所有关键信息得到展开**,避免省略任何重要内容。\n- **如果适用,可以提供额外的解释、使用建议或应用场景,以增强回答的实用性**。\n- 请使用**标准 Markdown 语法**输出答案,保证语法完整,**不要拆分**列表结构。\n- **输出此标签后,不得追加任何其他内容或标签**。\n\n严格要求\n\n切勿在中间思考或工具调用计划中,提前生成伪造的 标签内容。必须在实际调用工具并获得反馈后,再以 标签展示真实结果,再生成 标签输出最终答案。\n如果历史对话中已包含真实的工具调用结果,应直接使用这些信息构造最终答案,避免重复调用或展示多余标签。\n在所有工具调用完成之前,不得输出 标签;只有在确认所有真实工具反馈后,才生成最终答案。"}, {"id": "78baad16-173f-4d70-a7cd-d1a2abc2f0d1", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "6d8d88d4-ebaa-4a57-9279-ae5b26654b5c", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "e1b4bea8-0028-49a1-9e7d-ec29cff215da", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "95d84d67-3198-415e-a63c-bc9a2da8d821", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "272c927a-9e25-48b6-a921-6a8ab20267a4", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "5782976c-0c1c-47b8-bcd5-8b6e22e52d39", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1324.8563982124583, "y": 33.988095238095184, "id": "jademzanbn", "pad": 6, "bold": false, "text": "车型提取", "type": "textExtractionNodeState", "dirty": false, "index": 2, "width": 360, "height": 334, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "分析试驾车型中...", "inputParams": [{"id": "extractParam_2ca71bb8-ea6e-4d08-8f24-16a6fc3b64b6", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_5d4f3012-93a0-4ebf-a9f1-17a9fe252651", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "272c927a-9e25-48b6-a921-6a8ab20267a4", "referenceKey": "llmOutput", "referenceNode": "jadewdnjbq"}, {"id": "desc_12bbcd63-44c5-48d3-88c6-8b3d48b7c431", "from": "Input", "name": "desc", "type": "String", "value": "车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "}, {"id": "outputSchema_77df0737-ef00-4e3b-864c-31402655c65c", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"}, {"id": "ed9f7d7e-4173-465b-a3f5-4d34345efa53", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "49a6dd9d-589f-4ca2-b66f-1b1951c5e886", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "582a3d24-d8d8-41c6-b9b7-277d5d994683", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_b5d5dd8c-1a39-450d-9851-130ff0aa3a78", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_eed12a01-ccac-4bbd-ae80-39cf2d25a06c", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_f20d73ee-6376-4c90-adf2-1386771aaabd", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_4828a286-e3fd-4803-9887-31517cc82520", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_c3ba42cf-cb22-46d4-bdcc-5d86204ad098", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_d7c69077-05bf-4c1b-8e38-735e03d984ad", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_51a356d1-f2ac-4f3b-b444-99a737100996", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "30bf32bc-b320-418e-ae62-b30d45b26255", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "37331645-54f7-4751-8d04-12ab9d74a8a7", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "from": "Input", "name": "carTypes", "type": "Array", "value": "", "description": "问界的车型列表,具体到型号"}]}, {"id": "success_e3a426e0-3d67-42e3-b985-1daa32a44d20", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "stageDesc": "分析试驾车型中...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "textExtractionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1179.1428754447782, "y": 311.13955828369046, "id": "jadevbcutd", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 3, "textX": 0, "textY": 0, "width": 145.71352276768016, "hAlign": "center", "height": -110.15146304559528, "italic": false, "margin": 20, "toShape": "jademzanbn", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadewdnjbq", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1938.9763707660359, "y": -16.95528828179343, "id": "jade0ibwv7", "pad": 6, "bold": false, "text": "判断车型列表长度", "type": "codeNodeState", "dirty": false, "index": 4, "width": 368, "height": 252, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "cab31f1f-5429-4f02-99b9-540e0d795357", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "bc929113-6c29-46b8-a1ad-8d53f2d9bb2e", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "66c7a482-6c7f-4698-807c-159aaba67485", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: dict) -> str:\n is_valid_res = args.get(''input'', {}).get(''success'', bool)\n\n if not is_valid_res:\n return ''empty''\n\n input_list = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n\n if len(input_list) == 0:\n return ''empty''\n elif len(input_list) == 1:\n return ''single''\n else:\n return ''multi''\n", "language": "python"}, {"id": "5368a24a-8782-4c08-9b79-58bfcd86f6e3", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "e6aea608-a116-4f5c-9554-1a704547665e", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "string", "description": ""}}}}], "outputParams": [{"id": "70793ed8-e5c9-4ede-9945-5db35626f68f", "from": "Expand", "name": "output", "type": "String", "value": ""}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "codeComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2330.285047191377, "y": 120.84717835822356, "id": "jadeh0ccb7", "pad": 6, "bold": false, "text": "条件", "type": "conditionNodeCondition", "dirty": false, "index": 5, "width": 600, "height": 284, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "23d6704f-527f-43d9-8621-d8577d71dfec", "type": "if", "runnable": true, "conditions": [{"id": "ddee81d4-a971-494c-99df-f94f37633531", "value": [{"id": "4d1e95b4-e9fd-431c-9d33-2b5c874dea02", "from": "Reference", "name": "left", "type": "String", "value": ["output"], "referenceId": "70793ed8-e5c9-4ede-9945-5db35626f68f", "referenceKey": "output", "referenceNode": "jade0ibwv7"}, {"id": "57b838c4-c03d-47f3-9f57-3f75d62898f2", "from": "Input", "name": "right", "type": "String", "value": "empty", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "6f8c44d7-ad5c-4004-95e0-e01c391be100", "type": "if", "runnable": true, "conditions": [{"id": "7fd27d06-ac26-4225-a927-2b0d4a773cd4", "value": [{"id": "0c2c78a1-6770-4f46-b36e-a30f3ce96b56", "from": "Reference", "name": "left", "type": "String", "value": ["output"], "referenceId": "70793ed8-e5c9-4ede-9945-5db35626f68f", "referenceKey": "output", "referenceNode": "jade0ibwv7"}, {"id": "082798c7-f2f6-40a8-bc73-edc9a174d0d3", "from": "Input", "name": "right", "type": "String", "value": "single", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "73eee09c-caeb-44c1-a54b-65b54d2f8db8", "type": "else", "runnable": true, "conditions": [{"id": "7f70dfc4-154a-48bb-849a-158e22eff435", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2306.976370766036, "y": 109.04471171820657, "id": "jadec676as", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 6, "textX": 0, "textY": 0, "width": 23.30867642534122, "hAlign": "center", "height": 153.802466640017, "italic": false, "margin": 20, "toShape": "jadeh0ccb7", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade0ibwv7", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 3095.528839751806, "y": 1087.2644560112249, "id": "jadejnmnp4", "pad": 6, "bold": false, "text": "选择需要了解的车型", "type": "intelligentFormNodeState", "dirty": false, "index": 7, "width": 360, "height": 236, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "83131a2d-781b-4756-a0c8-91ee865ffbae", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "from": "Input", "name": "car", "type": "String", "value": "car", "displayName": "我想深入了解的车型"}, {"id": "28da30c8-2e73-46d1-a1ad-85d8b0c734b9", "from": "Reference", "name": "car-options", "type": "Array", "value": ["output"], "referenceId": "504a2241-6962-4de4-901a-3df2b798eaa2", "referenceKey": "output", "referenceNode": "jadebz0q8j"}]}, {"id": "c554e101-37b5-4aa1-b9b3-bfcfb2423583", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "from": "Input", "name": "car", "type": "String", "value": "car", "options": {"from": "Reference", "type": "Array", "value": ["output"], "referenceId": "504a2241-6962-4de4-901a-3df2b798eaa2", "referenceKey": "output", "referenceNode": "jadebz0q8j"}, "renderType": "Radio", "displayName": "我想深入了解的车型"}]}}], "outputParams": [{"id": "b5220628-4334-42c5-9ac9-93d4fca29ac8", "name": "output", "type": "Object", "value": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "name": "car", "type": "String", "value": ""}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "intelligentFormComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3136.121745249294, "y": 285.2614783280843, "id": "jadee2orhp", "pad": 6, "bold": false, "text": "代码_1", "type": "codeNodeState", "dirty": false, "index": 8, "width": 368, "height": 252, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8f50ae1f-f0fc-4eac-889c-0a53274b9e12", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "11325c8d-0491-4f99-85d9-4df54a3048a5", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "32133c22-59bf-4b46-9eaa-d221a0032c23", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n list_car = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n return list_car[0]", "language": "python"}, {"id": "ff990b8b-cf0c-436d-a8e7-7d4863e8d396", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "9012d086-0ac4-4bcd-8455-e9b47e81c589", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "string", "description": ""}}}}], "outputParams": [{"id": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "from": "Expand", "name": "output", "type": "String", "value": ""}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "codeComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 756.4734101887923, "y": 1462.429481687775, "id": "jade3oqd4e", "pad": 6, "bold": false, "text": "大模型_2", "type": "llmNodeState", "dirty": false, "index": 9, "width": 360, "height": 344, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "263b7842-e631-454a-aca2-8d979a344865", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "7b6ccdee-5b0b-4411-8f21-b75fe7879f5f", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "357563a6-8a44-40b1-8bdf-d11330ea58bb", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "3668396b-2055-417b-bef3-974623bee721", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "0cdec945-916c-4d47-b14c-1059ce8189e3", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "8eb45463-3eb1-4543-ad3c-c8f4cb00a530", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "abf75403-ad7d-4364-9f33-92cfb306f000", "from": "Input", "name": "template", "type": "String", "value": "请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n问题:{{query}}"}, {"id": "7f267a0f-3a63-434f-a538-105229be8af6", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "b123d3e4-0570-42c6-a20b-6fbcb0235674", "from": "Reference", "name": "query", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "6a5694e0-5100-4697-bb4e-8b030ed89624", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "9eed644b-62ae-4fd2-b5da-c7b5a4244059", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "2dcf3b33-dd57-4bb6-ae66-f2484d44d2e6", "from": "Input", "name": "systemPrompt", "type": "String", "value": "角色:你是一个问界试驾助手。\n限制:对于涉及其他品牌车型的问题,不进行详细回答,建议用户提问关于问界车型的问题或是一般性的汽车知识问题。"}, {"id": "eed2b900-e733-4ca0-adbb-18d6a0b6082b", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "177986fa-db8d-4f92-a6f2-546b7232b94b", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "ca60a33d-41f2-4d36-bac2-19e8aa0cf6dc", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "98f2c414-2a1b-4d27-86aa-361a66973fd9", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "2e94082b-930d-4016-a180-a07bd0ad8e50", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "df7f2c19-3d1a-4531-a0a5-11d31286e6e8", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3641.1444929801864, "y": 259.58807973293915, "id": "jadeq2zedq", "pad": 6, "bold": false, "text": "确认试驾车型", "type": "intelligentFormNodeState", "dirty": false, "index": 10, "width": 360, "height": 236, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "9ac3705a-a49d-4240-87dc-7963533a46a5", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "from": "Reference", "name": "car", "type": "String", "value": ["output"], "displayName": "试驾车型", "referenceId": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "referenceKey": "output", "referenceNode": "jadee2orhp"}]}, {"id": "a4dfd48f-e998-4725-bb71-dfd0644785e2", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "from": "Reference", "name": "car", "type": "String", "value": ["output"], "options": {"from": "Reference", "value": [], "referenceId": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "referenceNode": "jademzanbn"}, "renderType": "Input", "displayName": "试驾车型", "referenceId": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "referenceKey": "output", "referenceNode": "jadee2orhp"}]}}], "outputParams": [{"id": "41e1e5aa-846e-48ce-92ea-07e4d61f6553", "name": "output", "type": "Object", "value": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "name": "car", "type": "String", "value": ""}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "intelligentFormComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3583.4751136945915, "y": 810.1291139444377, "id": "jadebywy1i", "pad": 6, "bold": false, "text": "大模型_1", "type": "llmNodeState", "dirty": false, "index": 11, "width": 360, "height": 344, "italic": false, "shadow": "0 2px 4px 0 rgba(0,0,0,.1)", "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "905a7ab5-9f27-4ed8-b126-2a41394d9fe4", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "d3ac3f4a-d699-4341-89c3-d1c4921c1d2e", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "316ae379-274d-4fda-af6b-2f28b0356e2f", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "9ab32d21-b81b-4f13-9b5c-cd849ee7d334", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "9ee1c339-c88d-4248-86d6-139b64b763cf", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "99c6709d-5907-49e5-8394-0d294e8a3053", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "9646d069-0d62-4da2-a6cd-709c2e6b2d06", "from": "Input", "name": "template", "type": "String", "value": "通过工具获取车型信息,详细介绍这个车型:{{query}} "}, {"id": "16b3b1ba-cefd-4353-a29f-8004670c684a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "4c02d0da-7879-4461-8090-960554b32128", "from": "Reference", "name": "query", "type": "String", "value": ["output", "car"], "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}]}, {"id": "c04281f5-6769-476f-8d7d-7d21aa714ca8", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "06142d65-2487-4a40-b592-ac4638ac527d", "from": "Input", "name": "问界车型信息查询", "tags": ["FIT"], "type": "String", "value": "7cba6fdd-8c6d-410f-81dc-775ffe96902b", "version": "1.0.0"}]}, {"id": "a188c341-d4c0-46a0-8a8c-7438b05a4b6c", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "14e1b376-64ab-4ce1-86d2-e3285cea971c", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "2308bdeb-ff69-4cd9-afd4-ba8132c5d939", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "7f9b538a-7d1b-442d-b45e-5041364cfd4d", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "0"}, {"id": "62639076-5d67-4e37-bb54-024bdac816ec", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "8f3747e9-318f-49de-9de8-9610cf0049fc", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "d2156da5-09a9-4c8f-bf1a-9c63c457d439", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "6714da5e-b514-4911-b04b-96e3b83c15d7", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "flowable", "autoHeight": true, "emphasized": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "focusShadow": "0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)", "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3455.528839751806, "y": 1205.2644560112249, "id": "jadew1opy5", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 12, "textX": 0, "textY": 0, "width": 127.94627394278541, "hAlign": "center", "height": -223.1353420667872, "italic": false, "margin": 20, "toShape": "jadebywy1i", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadejnmnp4", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4512.272758733045, "y": 788.3278204357666, "id": "jadez5nidp", "pad": 6, "bold": false, "text": "确认试驾车型2", "type": "intelligentFormNodeState", "dirty": false, "index": 13, "width": 360, "height": 236, "italic": false, "flowMeta": {"task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "2f92cbde-4e4b-4e7e-8307-18633cbb4352", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "displayName": "我要试驾车型", "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}, {"id": "76c41bdf-8383-4318-a667-fd61b42609ab", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "options": {"from": "Reference", "value": [], "referenceId": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "referenceNode": "jademzanbn"}, "renderType": "Input", "displayName": "我要试驾车型", "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}}], "outputParams": [{"id": "a8df0b85-ef18-4362-b94b-df12022d1e13", "name": "output", "type": "Object", "value": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "name": "car", "type": "String", "value": ""}]}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "manual"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "intelligentFormComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1427.020111275216, "y": 1032.5974446613131, "id": "jadetvqkdx", "pad": 6, "bold": false, "text": "车型提取2", "type": "textExtractionNodeState", "dirty": false, "index": 14, "width": 360, "height": 334, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "分析试驾车型中...", "inputParams": [{"id": "extractParam_db169c41-8428-4d04-9ca9-d5462638b9de", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_ed4e777e-3973-4dfe-a5cd-8b6997e3c16f", "from": "Reference", "name": "text", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}, {"id": "desc_c5827ce1-b1c7-4959-98a6-79736761bfd7", "from": "Input", "name": "desc", "type": "String", "value": "车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "}, {"id": "outputSchema_2dda5dc9-c4f7-4f4e-a3ac-60a5387d66c9", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"}, {"id": "6958ac49-541d-4768-b198-dfdf3767649a", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "fb995664-4eb1-49e2-80ba-b6d2991eedd5", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "755800ba-5209-4a3c-90b0-b4aa4bfd8fe2", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_6bb0ad3b-7bbf-4110-acbd-f4b103214374", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_392e3f83-1fe1-43b5-88ae-afb9999ee4ee", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_cde542de-39f0-4550-91dd-b1cf979f310d", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_153d40da-349c-466e-8780-6eefc42ae5c6", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_fb24ab56-f614-412b-b6d6-dfbbbc189365", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_6b354c48-ed57-44b3-91a4-5940425dc1dc", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_d7e7c55e-d25b-4e03-80f1-b9937979faf1", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "b65edebe-1855-4502-a72f-0a207fd6c99f", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "66248c5c-9f8f-4000-8597-e8d6b0d7b16d", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "eb21be95-d82b-49b3-9eaf-455f4e449410", "from": "Input", "name": "carTypes", "type": "Array", "value": "", "description": "问界的车型列表,具体到型号"}]}, {"id": "success_e1a7af2a-fde0-4e73-81c1-cced296e08e8", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "stageDesc": "分析试驾车型中...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "textExtractionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1912.2742953652482, "y": 796.1082161366603, "id": "jadez5f1df", "pad": 6, "bold": false, "text": "聚合车型列表", "type": "codeNodeState", "dirty": false, "index": 15, "width": 368, "height": 252, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "ee12101b-d717-4f49-9ff5-6d840676047a", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "b8d2fbbf-1152-4403-a6dc-8ae88d7ddec3", "from": "Reference", "name": "input1", "type": "Object", "value": ["output"], "referenceId": "30bf32bc-b320-418e-ae62-b30d45b26255", "referenceKey": "output", "referenceNode": "jademzanbn"}, {"id": "662c4a18-21e8-47e0-bce2-10c89cb51b63", "from": "Reference", "name": "input2", "type": "Object", "value": ["output"], "referenceId": "b65edebe-1855-4502-a72f-0a207fd6c99f", "referenceKey": "output", "referenceNode": "jadetvqkdx"}]}, {"id": "08e9c25a-e55c-4620-bcca-325d714160d5", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n if args[''input1'']:\n return args[''input1'']\n else:\n return args[''input2''] \n", "language": "python"}, {"id": "7df2184c-f82f-43ab-9a30-d695fae550ea", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "35868ea7-d973-4a6c-b9f2-340a06f9f112", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "object", "properties": {}, "description": ""}}}}], "outputParams": [{"id": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "from": "Expand", "name": "output", "type": "Object", "value": []}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "codeComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 1684.8563982124583, "y": 200.98809523809518, "id": "jadeanptlq", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 16, "textX": 0, "textY": 0, "width": 227.41789715278992, "hAlign": "center", "height": 721.1201208985651, "italic": false, "margin": 20, "toShape": "jadez5f1df", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jademzanbn", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1787.020111275216, "y": 1199.5974446613131, "id": "jade58w7a7", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 17, "textX": 0, "textY": 0, "width": 125.25418409003214, "hAlign": "center", "height": -277.48922852465284, "italic": false, "margin": 20, "toShape": "jadez5f1df", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadetvqkdx", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 2280.2742953652482, "y": 922.1082161366603, "id": "jadexs8po6", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 18, "textX": 0, "textY": 0, "width": -341.29792459921237, "hAlign": "center", "height": -813.0635044184537, "italic": false, "margin": 20, "toShape": "jade0ibwv7", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadez5f1df", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 2383.9637395101945, "y": 939.1301739903586, "id": "jadebz0q8j", "pad": 6, "bold": false, "text": "提取车型列表", "type": "codeNodeState", "dirty": false, "index": 19, "width": 368, "height": 252, "italic": false, "flowMeta": {"jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "ac8da121-58a3-4801-82f3-862533c0db2b", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "90d97368-2212-4176-ab52-55215cb5d6c6", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "5abd8220-a2e6-47a1-b3d7-61da5c91a7c8", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n return args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])", "language": "python"}, {"id": "d09bd08d-d2f7-47c2-a74b-a9f9fe899387", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "c57b604c-0ae0-4d32-bef3-30c14793ddf1", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "array", "description": ""}}}}], "outputParams": [{"id": "504a2241-6962-4de4-901a-3df2b798eaa2", "from": "Expand", "name": "output", "type": "Array", "value": ""}]}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "codeComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 2918.657887350822, "y": 344.64718910145405, "id": "jadep8j6qd", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 20, "textX": 0, "textY": 0, "width": -534.6941478406275, "hAlign": "center", "height": 720.4829848889045, "italic": false, "margin": 20, "toShape": "jadebz0q8j", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeh0ccb7", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 2751.9637395101945, "y": 1065.1301739903586, "id": "jader2ugw2", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 21, "textX": 0, "textY": 0, "width": 343.5651002416116, "hAlign": "center", "height": 140.1342820208663, "italic": false, "margin": 20, "toShape": "jadejnmnp4", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadebz0q8j", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 1585.717679196791, "y": 1882.192672636369, "id": "jadeaevz6w", "pad": 6, "bold": false, "text": "结束_1", "type": "endNodeEnd", "dirty": false, "index": 22, "width": 360, "height": 182, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "e42435d1-bd40-4a8f-a6da-6ce3b8c9e936", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "0cecce75-2752-4f0b-8c01-e55897f82eef", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "2e94082b-930d-4016-a180-a07bd0ad8e50", "referenceKey": "llmOutput", "referenceNode": "jade3oqd4e"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "f7b318fa-c166-4b2d-b72a-e6b90b8c7324", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 1116.4734101887923, "y": 1634.429481687775, "id": "jadewhsta6", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 23, "textX": 0, "textY": 0, "width": 469.2442690079988, "hAlign": "center", "height": 338.763190948594, "italic": false, "margin": 20, "toShape": "jadeaevz6w", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade3oqd4e", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4941.814604932744, "y": 1053.7568338252217, "id": "jadevgtszg", "pad": 6, "bold": false, "text": "结束_3", "type": "endNodeEnd", "dirty": false, "index": 24, "width": 360, "height": 182, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "10392a5e-479e-468d-9a87-bb85a2649cc2", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "b2ab53ef-8e29-4a95-9fed-be9894df8806", "from": "Input", "name": "out", "type": "String", "value": "您选择的试驾车型为:", "editable": true, "isRequired": true, "description": ""}, {"id": "31d34355-c6d3-459d-ac61-66eaf31f6fd4", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "editable": true, "isRequired": true, "description": "", "referenceId": "610f8341-46f0-4e26-a6b8-58be5d52f560", "referenceKey": "car", "referenceNode": "jadez5nidp"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "89dfe7b5-972a-4b2a-a2b4-86c9b851fe05", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 4872.272758733045, "y": 906.3278204357666, "id": "jade90v0rb", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 25, "textX": 0, "textY": 0, "width": 69.54184619969965, "hAlign": "center", "height": 238.42901338945512, "italic": false, "margin": 20, "toShape": "jadevgtszg", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadez5nidp", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4360.7236177911855, "y": 276.14527960371345, "id": "jade9wr70l", "pad": 6, "bold": false, "text": "结束_2", "type": "endNodeEnd", "dirty": false, "index": 26, "width": 360, "height": 182, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "bf589d2e-aff9-46f4-a266-4f3b6b4ea6e7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "9104e96f-26ec-46cf-88b4-1ff929f7b983", "from": "Input", "name": "out", "type": "String", "value": "您选择的试驾车型为:", "editable": true, "isRequired": true, "description": ""}, {"id": "7c278873-cea0-424b-b344-e22ab070aeb3", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "editable": true, "isRequired": true, "description": "", "referenceId": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "referenceKey": "car", "referenceNode": "jadeq2zedq"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "ab34cf0f-7fa9-424d-8b01-edc402fb163d", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3281.554641671145, "y": -72.84814124515464, "id": "jadehrizke", "pad": 6, "bold": false, "text": "结束", "type": "endNodeEnd", "dirty": false, "index": 27, "width": 360, "height": 182, "italic": false, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8fca4472-cb61-474b-97c8-be14566e998a", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "1e9b929c-bd6d-46c1-9b8c-23d1a0f7a7cc", "from": "Input", "name": "out", "type": "String", "value": "非常抱歉,不支持该车型的试驾。", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "8fcd715b-5e4c-488c-bcb7-ec5f4b5c83c2", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "triggerMode": "auto"}, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "deletable": true, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28,31,35,.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "endComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderWidth": 1, "mouseInBorderColor": "#B1B1B7"}, {"x": 2918.657887350822, "y": 241.64718520187574, "id": "jadeke55eo", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 28, "textX": 0, "textY": 0, "width": 362.896754320323, "hAlign": "center", "height": -223.49532644703038, "italic": false, "margin": 20, "toShape": "jadehrizke", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeh0ccb7", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-0|23d6704f-527f-43d9-8621-d8577d71dfec"}, {"x": -503.16041743745245, "y": 128.7548542937538, "id": "jade1tc9vb", "pad": 6, "bold": false, "text": "用户问题分类", "type": "llmNodeState", "dirty": false, "index": 29, "width": 360, "height": 344, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8e3a79d1-d39d-4815-9236-2e08bcbeb89d", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "d9ef67b3-a4e8-4b6c-b15b-0f55de543159", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "22a75a6e-c13a-45e7-9492-15613f79b1d8", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a8265476-b028-4b32-88a4-cd9e27609d86", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "2c6a7c43-a6d7-4850-a60f-43d615085630", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "80222643-ef96-41ca-b9f6-6eae7f267a69", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "d655ab25-92c4-4af2-a01a-3e7e22a84362", "from": "Input", "name": "template", "type": "String", "value": "\"请分析用户的问题,并判断其意图是否是与问界汽车或是问界试驾有关。\n 如果用户的问题涉及问界汽车信息的询问,(例如:“请介绍一下问界M5”、 “给我推荐一款问界汽车”),则返回 ‘问界汽车问题’。\n如果用户只是咨询试驾相关内容(例如:“我想试驾问界M5”、“我想试驾保时捷911”),则返回 ‘试驾汽车问题’。\n如果用户问题是关于**其他品牌**的汽车或是一般性闲聊(例如:“最近有什么有趣的事?”、“今天天气不错”)则返回 ‘其他问题’\"\n用户问题:{{input}}\n**重要:不要回答``问界汽车问题``、``试驾汽车问题``、``其他问题``以外的文字**"}, {"id": "cfe7e088-9f69-4671-a44a-4cd0f553920e", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "046e05a4-4aa1-4694-8427-634bd97596d0", "from": "Reference", "name": "input", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "248bb3d3-abcb-4072-8b14-360428994714", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "89b4068b-9926-4429-a334-9b6daefc11d0", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "267e88b8-1cdc-4312-a117-a96ea6c37514", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "3b9fafe7-20ac-4d59-be6c-1dd61b264e70", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}, {"id": "ed3e6fda-3a66-405c-b5d4-100822755cd8", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "d4ae45a4-74cc-498c-9e6e-69a1694614fb", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "2d7b57fb-c452-41d3-9fed-bed3289c64eb", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": -763.5461204767116, "y": 297.25814386932325, "id": "jadeknglms", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 30, "textX": 0, "textY": 0, "width": 260.38570303925917, "hAlign": "center", "height": 3.496710424430546, "italic": false, "margin": 20, "toShape": "jade1tc9vb", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade6qm5eg", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": -66.83537340229776, "y": 197.7460840672867, "id": "jade5pubt6", "pad": 6, "bold": false, "text": "条件_1", "type": "conditionNodeCondition", "dirty": false, "index": 31, "width": 600, "height": 284, "italic": false, "flowMeta": {"joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "407a4d42-fe33-427e-8553-9de19e9bb002", "type": "if", "runnable": true, "conditions": [{"id": "e6cc2561-bf7f-43e9-873e-675ef7fa1340", "value": [{"id": "146ae59a-df8a-4181-9dbc-466e6b81cdb9", "from": "Reference", "name": "left", "type": "String", "value": ["output", "llmOutput"], "referenceId": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "referenceKey": "llmOutput", "referenceNode": "jade1tc9vb"}, {"id": "5a2bd11a-bbff-4e14-81a7-8f5d747d9e61", "from": "Input", "name": "right", "type": "String", "value": "问界汽车问题", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "7fe7b5fa-c661-4ba5-9f87-4e8070496dd0", "type": "if", "runnable": true, "conditions": [{"id": "fc38c7be-06ac-41eb-a896-e6bb0f9129d0", "value": [{"id": "767ed731-7595-46a6-9448-dcac3635694a", "from": "Reference", "name": "left", "type": "String", "value": ["output", "llmOutput"], "referenceId": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "referenceKey": "llmOutput", "referenceNode": "jade1tc9vb"}, {"id": "1ce1f111-7dec-4fac-b6e5-fd02d5b78557", "from": "Input", "name": "right", "type": "String", "value": "试驾汽车问题", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "dc589e0f-3202-46e4-87f4-e550d54bbbc5", "type": "else", "runnable": true, "conditions": [{"id": "8d21bec4-7de8-4b35-9561-2e4374e42780", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "conditionComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": -143.16041743745245, "y": 300.7548542937538, "id": "jadey81zoy", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 32, "textX": 0, "textY": 0, "width": 76.32504403515469, "hAlign": "center", "height": 38.99122977353289, "italic": false, "margin": 20, "toShape": "jade5pubt6", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade1tc9vb", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 521.5373576131606, "y": 318.54609091093886, "id": "jade8w9r8o", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 33, "textX": 0, "textY": 0, "width": 297.6055178316176, "hAlign": "center", "height": -7.406532627248396, "italic": false, "margin": 20, "toShape": "jadewdnjbq", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade5pubt6", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-0|407a4d42-fe33-427e-8553-9de19e9bb002"}, {"x": 521.5373576131606, "y": 374.5460870922666, "id": "jadewp6fyj", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 34, "textX": 0, "textY": 0, "width": 905.4827536620555, "hAlign": "center", "height": 825.0513575690466, "italic": false, "margin": 20, "toShape": "jadetvqkdx", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade5pubt6", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-1|7fe7b5fa-c661-4ba5-9f87-4e8070496dd0"}, {"x": 521.5373576131606, "y": 421.54607877806717, "id": "jadeu36msk", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 35, "textX": 0, "textY": 0, "width": 234.93605257563172, "hAlign": "center", "height": 1212.883402909708, "italic": false, "margin": 20, "toShape": "jade3oqd4e", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade5pubt6", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-999"}, {"x": 4052.389624614043, "y": 653.9380776568371, "id": "jade9wm99z", "pad": 6, "bold": false, "text": "大模型_4", "type": "llmNodeState", "dirty": false, "index": 36, "width": 360, "height": 344, "italic": false, "flowMeta": {"jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "09e984b4-261d-4db4-9a79-bfdcab112355", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f7469288-be18-4f27-8e0c-985b6444c3cf", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "57eece3d-c599-444e-8ad8-b76f7b2594ca", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "96ab5533-d3d0-4018-a341-16667d0df21b", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "6ee7671a-c9e2-4ae7-9348-d0f4c548e025", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "4e9c98fc-6547-4fd0-a71e-e21b9cd52f41", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "e7cd99dd-0143-44a9-81ee-ccebcc8b6a2c", "from": "Input", "name": "template", "type": "String", "value": "通过工具查询车型的图片:{{query}} "}, {"id": "56e29f98-ae8e-478c-b02e-75b8149f090a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "77353774-8079-4f34-8672-44df471f2c2a", "from": "Reference", "name": "query", "type": "String", "value": ["output", "car"], "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}]}, {"id": "6dd50ee8-6572-43d1-a7da-97c88138bf5d", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "0"}, {"id": "3d26bc7c-3757-4b90-b803-f919123bd1dc", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "bf11ba32-9201-4618-8084-66c81e4a0608", "from": "Input", "name": "问界车型宣传图片", "tags": ["FIT"], "type": "String", "value": "e754f978-d236-44fa-aa55-e8eb5d8f8269", "version": "1.0.0"}]}, {"id": "e0ff690b-0e1c-4086-a299-94ab3cbfe5e5", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "267a67d4-c64c-488d-8263-7977c26b1c23", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "4cfaec42-099b-4ec4-ae23-a3f47d9db635", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "29377aa8-2eb4-4a99-99c5-19ef52f309a0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "c7836226-62a7-47e6-b4fa-705ed2acffed", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "a2263372-420d-404c-93c0-e4e0edd79f39", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, "hasError": false, "hideText": true, "moveable": true, "runnable": true, "backColor": "white", "container": "elsa-page:tvp1s6", "dashWidth": 0, "namespace": "jadeFlow", "autoHeight": true, "emphasized": false, "enableMask": false, "rotateAble": false, "borderColor": "rgba(28, 31, 35, 0.08)", "borderWidth": 1, "runningTask": 0, "triggerMode": "auto", "warningTask": 0, "cornerRadius": 8, "outlineColor": "rgba(74, 147, 255, 0.12)", "outlineWidth": 10, "completedTask": 0, "componentName": "llmComponent", "focusBackColor": "white", "sourcePlatform": "official", "enableAnimation": false, "focusBorderColor": "rgb(4, 123, 252)", "focusBorderWidth": 1, "mouseInBorderColor": "rgb(4, 123, 252)"}, {"x": 3943.4751136945915, "y": 982.1291139444377, "id": "jadepdt7pq", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 37, "textX": 0, "textY": 0, "width": 108.91451091945146, "hAlign": "center", "height": -156.19103628760058, "italic": false, "margin": 20, "toShape": "jade9wm99z", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadebywy1i", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4412.389624614043, "y": 825.9380776568371, "id": "jade28pvut", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 38, "textX": 0, "textY": 0, "width": 99.88313411900162, "hAlign": "center", "height": 80.38974277892953, "italic": false, "margin": 20, "toShape": "jadez5nidp", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jade9wm99z", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 2918.657887350822, "y": 297.6471616509573, "id": "jadeam5lo5", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": true, "index": 39, "textX": 0, "textY": 0, "width": 217.46385789847182, "hAlign": "center", "height": 113.61431667712702, "italic": false, "margin": 20, "toShape": "jadee2orhp", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeh0ccb7", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "dynamic-1|6f8c44d7-ad5c-4004-95e0-e01c391be100"}, {"x": 3504.121745249294, "y": 411.2614783280843, "id": "jade13kr3a", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 40, "textX": 0, "textY": 0, "width": 137.02274773089266, "hAlign": "center", "height": -33.67339859514516, "italic": false, "margin": 20, "toShape": "jadeq2zedq", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadee2orhp", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}, {"x": 4001.1444929801864, "y": 377.58807973293915, "id": "jadef977tc", "pad": 0, "bold": false, "text": "", "type": "jadeEvent", "dirty": false, "index": 41, "textX": 0, "textY": 0, "width": 359.579124810999, "hAlign": "center", "height": -10.442800129225702, "italic": false, "margin": 20, "toShape": "jade9wr70l", "endArrow": true, "hideText": true, "lineMode": {"type": "auto_curve"}, "runnable": true, "allowLink": false, "backColor": "white", "container": "elsa-page:tvp1s6", "fromShape": "jadeq2zedq", "lineWidth": 2, "namespace": "elsa", "beginArrow": false, "borderColor": "#B1B1B7", "borderWidth": 1, "curvePoint1": {"x": 0, "y": 0}, "curvePoint2": {"x": 0, "y": 0}, "brokenPoints": [], "endArrowSize": 4, "arrowEndPoint": {"x": 0, "y": 0}, "endArrowEmpty": false, "beginArrowSize": 4, "arrowBeginPoint": {"x": 0, "y": 0}, "beginArrowEmpty": false, "definedToConnector": "W", "mouseInBorderColor": "#B1B1B7", "allowSwitchLineMode": false, "definedFromConnector": "E"}], "vAlign": "top", "itemPad": [0, 0, 0, 0], "division": -1, "dockMode": "none", "fontFace": "arial", "fontSize": 18, "hideText": true, "moveable": true, "shapesAs": {}, "backColor": "#fbfbfc", "container": "elsa-page:tvp1s6", "dockAlign": "top", "fontColor": "#ECD0A7", "fontStyle": "normal", "itemSpace": 5, "namespace": "jadeFlow", "fontWeight": "bold", "itemScroll": {"x": 0, "y": 0}, "borderColor": "white", "focusBackColor": "#fbfbfc"}], "title": "5711f3230eb94abdb168e61d2082d1d2", "source": "elsa", "tenant": "31f20efc7e0848deab6a6bc10fc3021e", "setting": {"pad": 10, "tag": {}, "code": "", "pDock": "none", "hAlign": "center", "margin": 25, "shadow": "", "shared": false, "vAlign": "top", "itemPad": [5, 5, 5, 5], "visible": true, "autoText": false, "dockMode": "none", "dragable": true, "editable": true, "fontFace": "arial", "fontSize": 12, "infoType": {"name": "none", "next": "INFORMATION"}, "moveable": true, "priority": 0, "allowLink": true, "autoWidth": false, "backAlpha": 0.15, "backColor": "whitesmoke", "dashWidth": 0, "deletable": true, "fontColor": "steelblue", "fontStyle": "normal", "headColor": "steelblue", "lineWidth": 2, "underline": false, "autoHeight": false, "emphasized": false, "fontWeight": "lighter", "itemScroll": {"x": 0, "y": 0}, "lineHeight": 1.5, "resizeable": true, "rotateAble": true, "scrollLock": {"x": false, "y": false}, "selectable": true, "shadowData": "2px 2px 4px", "borderColor": "#047bfc", "borderWidth": 1, "bulletSpeed": 1, "focusMargin": 0, "focusShadow": "", "globalAlpha": 1, "outstanding": false, "bulletedList": false, "cornerRadius": 4, "enableSocial": true, "mouseInColor": "orange", "numberedList": false, "outlineColor": "rgba(74,147,255,0.12)", "outlineWidth": 10, "rotateDegree": 0, "captionhAlign": "center", "strikethrough": false, "focusBackColor": "whitesmoke", "focusFontColor": "darkorange", "progressStatus": {"name": "NONE", "next": "UNKNOWN", "color": "gray"}, "showedProgress": false, "allNodeNumLimit": 99, "captionfontFace": "arial black", "captionfontSize": 14, "enableAnimation": false, "progressPercent": 0.65, "captionfontColor": "whitesmoke", "captionfontStyle": "normal", "focusBorderColor": "#047bfc", "focusBorderWidth": 1, "mouseInBackColor": "whitesmoke", "mouseInFontColor": "orange", "captionfontWeight": "lighter", "captionlineHeight": 1, "mouseInBorderColor": "#047bfc", "sameTypeNodeNumLimit": 19}, "flowMeta": {"callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}, "enableText": false}', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('ae708d5828194845afc15402d2849b8b', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'ability', 'String', 'null', 'none', 'workflow', '能力配置', 1, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('b7a7c427418d4d8eb5e8f96165a1d6a6', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'chat', 'String', 'null', 'none', 'workflow', '聊天设置', 3, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('c383886d8a154b3f9e94950467d92432', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'memory', 'List', '["jade6qm5eg","memory"]', 'graph', 'chat', '多轮对话', 5, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('6290bce2ccf249239bc52866604a3920', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'opening', 'String', '"Hi~我是问界试驾助手,快来体验你喜欢的车型吧!"', 'input', 'chat', '开场白', 4, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('d8bd923772b247cf88bfcb7571059066', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'enterWorkflow', 'String', 'null', 'none', 'ability', '进入工作流编排', 2, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('01ec06bcc0e7483da79b7f1c55b7851e', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'inspiration', 'object', '{"category":[{"title":"root","id":"root","children":[]}],"inspirations":[{"name":"问界试驾预约","description":"试驾某一款问界车型","prompt":"我想试驾{{car}}","promptVarData":[{"key":"tsrvqc","var":"car","varType":"选择框","sourceType":"input","sourceInfo":"问界M5;问界M7;问界M9","multiple":false}],"category":null,"auto":false,"id":"rehmx8"},{"name":"问界车型咨询","description":"了解问界某一种车型的具体信息","prompt":"请介绍一下{{car}}的具体信息","promptVarData":[{"key":"hff2tk","var":"car","varType":"选择框","sourceType":"input","sourceInfo":"问界M5;问界新M5 增程 Max;问界M7;问界M7 Plus 五座四驱版","multiple":false}],"category":null,"auto":false,"id":"5u5v1u"}],"showInspiration":true}', 'input', 'chat', '创意灵感', 7, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('ba6cd122de7d4e20a8e69702379a4b9f', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'recommend', 'object', '{"showRecommend":false,"list":[]}', 'input', 'chat', '猜你想问', 6, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; +INSERT INTO "public"."app_builder_form_property" ("id", "form_id", "name", "data_type", "default_value", "data_from", "in_group", "description", "default_index", "is_deleted", "app_id") VALUES ('662b1e243b8e43788092b8bff6b7a5f6', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'workflow', 'String', 'null', 'none', 'null', '工作流编排', 0, 0, '550177e8d0e34014a2d95988ef1c67c5') ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."flow_definition" ("definition_id", "meta_id", "name", "tenant", "version", "status", "graph", "created_by", "created_at") VALUES ('803b4ba4d163472b81ad2330734e23b9', '5711f3230eb94abdb168e61d2082d1d2', '5711f3230eb94abdb168e61d2082d1d2', '31f20efc7e0848deab6a6bc10fc3021e', '1.0.0', 'active', '{"name": "5711f3230eb94abdb168e61d2082d1d2", "nodes": [{"name": "开始", "type": "startNodeStart", "metaId": "jade6qm5eg", "runnable": true, "inputParams": [{"id": "91138f09-b635-43df-95c6-1fe3d1745829", "from": "Expand", "name": "input", "type": "Object", "value": [{"id": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "from": "Input", "name": "Question", "type": "String", "value": "", "isVisible": true, "isRequired": true, "description": "这是用户输入的问题。", "displayName": "用户问题", "disableModifiable": true}], "config": [{"allowAdd": false}]}, {"id": "4a770dc6-e3c9-475d-84c7-48dacc74a5b6", "from": "Expand", "name": "memory", "type": "Object", "value": [{"id": "a7675623-7fc7-468c-8910-e73c70e5e468", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": true}, {"id": "cee9a31b-781c-4835-a616-ceed73be22f2", "from": "Input", "name": "type", "type": "String", "value": "ByConversationTurn"}, {"id": "69592622-4291-409d-9d65-9faea83db657", "from": "Input", "name": "value", "type": "Integer", "value": "3"}]}], "triggerMode": "auto"}, {"name": "大模型", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "31ba235d-1b26-4780-a7a7-32eca9500919", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "83653b54-dd04-4da9-957d-adb7c2728632", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "dd588a17-a69c-40c0-859a-d9930202a148", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "6c414e75-971e-403a-b2b1-c6850f128cc4", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "db5fdafa-4cbf-44ba-9cca-8a98f1f771f4", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "88f74d78-4711-4f81-a2e7-74d0034c5e88", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "35a710cf-1b79-4523-b16f-b50878d677fe", "from": "Input", "name": "template", "type": "String", "value": "如果用户未指定具体车型,则按照用户问题,结合工具提供的问界车型信息,给出简略概要类的回答。\n问题:{{query}} "}, {"id": "38fb27a1-71f4-4fcc-87d5-9d8a880bc04d", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "eee66922-4304-4209-89fc-b13ffa101630", "from": "Reference", "name": "query", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "a6865419-867c-4bfb-855c-f5c1876c965a", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "29d1707c-beaa-44c8-9eb2-08462880b031", "from": "Input", "name": "问界车型信息查询", "tags": ["FIT"], "type": "String", "value": "7cba6fdd-8c6d-410f-81dc-775ffe96902b", "version": "1.0.0"}]}, {"id": "308e2023-a8e9-486e-9784-8680addbb786", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "68f92923-d5da-42ce-8478-d7ac7d90664e", "from": "Input", "name": "systemPrompt", "type": "String", "value": "你是一个具备自主工具调用能力的智能体,可以展示中间的思考过程给用户。你的回答有两种情况:\n\n无需调用外部工具\n\n如果问题可通过已有对话历史或直接推理得到答案,直接输出最终结果,不需使用任何标签包装,也不显示详细思考过程。\n需要调用外部工具解决的复杂问题\n\n必须采用以下严格的标签体系输出,每个标签之间空一行,且仅展示真实的工具调用结果:\n...:展示你内部的思考过程。注意,这部分内容可以展示给用户,但仅限于描述思路,不应包含任何伪造的工具调用结果。\n...:描述你准备调用工具的原因和计划。此处仅说明你需要调用哪个工具以及原因,工具的名称对人类阅读要友好,切勿直接模拟或输出工具返回内容。\n...:当你真正调用某个工具后,等待工具反馈,然后将工具调用的返回结果做非常简略的摘要后放在此标签内,摘要字数在20字以内。绝对禁止在未获得真实工具反馈前预先构造。 标签内容。\n...:在获取所有真实工具调用结果后,将整合信息给出最终答案。\n**重要要求**:\n- **不要输出tool_call标签**。\n- **答案必须详细完整**,不仅仅是工具返回结果的简单总结,而是对结果进行深入分析和整合,并提供背景解释、推理过程和可行性分析。\n- **确保所有关键信息得到展开**,避免省略任何重要内容。\n- **如果适用,可以提供额外的解释、使用建议或应用场景,以增强回答的实用性**。\n- 请使用**标准 Markdown 语法**输出答案,保证语法完整,**不要拆分**列表结构。\n- **输出此标签后,不得追加任何其他内容或标签**。\n\n严格要求\n\n切勿在中间思考或工具调用计划中,提前生成伪造的 标签内容。必须在实际调用工具并获得反馈后,再以 标签展示真实结果,再生成 标签输出最终答案。\n如果历史对话中已包含真实的工具调用结果,应直接使用这些信息构造最终答案,避免重复调用或展示多余标签。\n在所有工具调用完成之前,不得输出 标签;只有在确认所有真实工具反馈后,才生成最终答案。"}, {"id": "78baad16-173f-4d70-a7cd-d1a2abc2f0d1", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "6d8d88d4-ebaa-4a57-9279-ae5b26654b5c", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "e1b4bea8-0028-49a1-9e7d-ec29cff215da", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "95d84d67-3198-415e-a63c-bc9a2da8d821", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "272c927a-9e25-48b6-a921-6a8ab20267a4", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "5782976c-0c1c-47b8-bcd5-8b6e22e52d39", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadewdnjbq", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "车型提取", "type": "textExtractionNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "分析试驾车型中...", "inputParams": [{"id": "extractParam_2ca71bb8-ea6e-4d08-8f24-16a6fc3b64b6", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_5d4f3012-93a0-4ebf-a9f1-17a9fe252651", "from": "Reference", "name": "text", "type": "String", "value": ["output", "llmOutput"], "referenceId": "272c927a-9e25-48b6-a921-6a8ab20267a4", "referenceKey": "llmOutput", "referenceNode": "jadewdnjbq"}, {"id": "desc_12bbcd63-44c5-48d3-88c6-8b3d48b7c431", "from": "Input", "name": "desc", "type": "String", "value": "车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "}, {"id": "outputSchema_77df0737-ef00-4e3b-864c-31402655c65c", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"}, {"id": "ed9f7d7e-4173-465b-a3f5-4d34345efa53", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "49a6dd9d-589f-4ca2-b66f-1b1951c5e886", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "582a3d24-d8d8-41c6-b9b7-277d5d994683", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_b5d5dd8c-1a39-450d-9851-130ff0aa3a78", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_eed12a01-ccac-4bbd-ae80-39cf2d25a06c", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_f20d73ee-6376-4c90-adf2-1386771aaabd", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_4828a286-e3fd-4803-9887-31517cc82520", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_c3ba42cf-cb22-46d4-bdcc-5d86204ad098", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_d7c69077-05bf-4c1b-8e38-735e03d984ad", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_51a356d1-f2ac-4f3b-b444-99a737100996", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "30bf32bc-b320-418e-ae62-b30d45b26255", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "37331645-54f7-4751-8d04-12ab9d74a8a7", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "from": "Input", "name": "carTypes", "type": "Array", "value": "", "description": "问界的车型列表,具体到型号"}]}, {"id": "success_e3a426e0-3d67-42e3-b985-1daa32a44d20", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "metaId": "jademzanbn", "runnable": true, "stageDesc": "分析试驾车型中...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, {"to": "jademzanbn", "from": "jadewdnjbq", "type": "jadeEvent", "metaId": "jadevbcutd", "runnable": true, "fromConnector": "E"}, {"name": "判断车型列表长度", "type": "codeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "cab31f1f-5429-4f02-99b9-540e0d795357", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "bc929113-6c29-46b8-a1ad-8d53f2d9bb2e", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "66c7a482-6c7f-4698-807c-159aaba67485", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: dict) -> str:\n is_valid_res = args.get(''input'', {}).get(''success'', bool)\n\n if not is_valid_res:\n return ''empty''\n\n input_list = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n\n if len(input_list) == 0:\n return ''empty''\n elif len(input_list) == 1:\n return ''single''\n else:\n return ''multi''\n", "language": "python"}, {"id": "5368a24a-8782-4c08-9b79-58bfcd86f6e3", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "e6aea608-a116-4f5c-9554-1a704547665e", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "string", "description": ""}}}}], "outputParams": [{"id": "70793ed8-e5c9-4ede-9945-5db35626f68f", "from": "Expand", "name": "output", "type": "String", "value": ""}]}}}, "metaId": "jade0ibwv7", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "条件", "type": "conditionNodeCondition", "metaId": "jadeh0ccb7", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "23d6704f-527f-43d9-8621-d8577d71dfec", "type": "if", "runnable": true, "conditions": [{"id": "ddee81d4-a971-494c-99df-f94f37633531", "value": [{"id": "4d1e95b4-e9fd-431c-9d33-2b5c874dea02", "from": "Reference", "name": "left", "type": "String", "value": ["output"], "referenceId": "70793ed8-e5c9-4ede-9945-5db35626f68f", "referenceKey": "output", "referenceNode": "jade0ibwv7"}, {"id": "57b838c4-c03d-47f3-9f57-3f75d62898f2", "from": "Input", "name": "right", "type": "String", "value": "empty", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "6f8c44d7-ad5c-4004-95e0-e01c391be100", "type": "if", "runnable": true, "conditions": [{"id": "7fd27d06-ac26-4225-a927-2b0d4a773cd4", "value": [{"id": "0c2c78a1-6770-4f46-b36e-a30f3ce96b56", "from": "Reference", "name": "left", "type": "String", "value": ["output"], "referenceId": "70793ed8-e5c9-4ede-9945-5db35626f68f", "referenceKey": "output", "referenceNode": "jade0ibwv7"}, {"id": "082798c7-f2f6-40a8-bc73-edc9a174d0d3", "from": "Input", "name": "right", "type": "String", "value": "single", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "73eee09c-caeb-44c1-a54b-65b54d2f8db8", "type": "else", "runnable": true, "conditions": [{"id": "7f70dfc4-154a-48bb-849a-158e22eff435", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"to": "jadeh0ccb7", "from": "jade0ibwv7", "type": "jadeEvent", "metaId": "jadec676as", "runnable": true, "fromConnector": "E"}, {"name": "选择需要了解的车型", "task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "83131a2d-781b-4756-a0c8-91ee865ffbae", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "from": "Input", "name": "car", "type": "String", "value": "car", "displayName": "我想深入了解的车型"}, {"id": "28da30c8-2e73-46d1-a1ad-85d8b0c734b9", "from": "Reference", "name": "car-options", "type": "Array", "value": ["output"], "referenceId": "504a2241-6962-4de4-901a-3df2b798eaa2", "referenceKey": "output", "referenceNode": "jadebz0q8j"}]}, {"id": "c554e101-37b5-4aa1-b9b3-bfcfb2423583", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "from": "Input", "name": "car", "type": "String", "value": "car", "options": {"from": "Reference", "type": "Array", "value": ["output"], "referenceId": "504a2241-6962-4de4-901a-3df2b798eaa2", "referenceKey": "output", "referenceNode": "jadebz0q8j"}, "renderType": "Radio", "displayName": "我想深入了解的车型"}]}}], "outputParams": [{"id": "b5220628-4334-42c5-9ac9-93d4fca29ac8", "name": "output", "type": "Object", "value": [{"id": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "name": "car", "type": "String", "value": ""}]}]}}}, "type": "intelligentFormNodeState", "metaId": "jadejnmnp4", "runnable": true, "triggerMode": "manual"}, {"name": "代码_1", "type": "codeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8f50ae1f-f0fc-4eac-889c-0a53274b9e12", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "11325c8d-0491-4f99-85d9-4df54a3048a5", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "32133c22-59bf-4b46-9eaa-d221a0032c23", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n list_car = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n return list_car[0]", "language": "python"}, {"id": "ff990b8b-cf0c-436d-a8e7-7d4863e8d396", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "9012d086-0ac4-4bcd-8455-e9b47e81c589", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "string", "description": ""}}}}], "outputParams": [{"id": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "from": "Expand", "name": "output", "type": "String", "value": ""}]}}}, "metaId": "jadee2orhp", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "大模型_2", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "263b7842-e631-454a-aca2-8d979a344865", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "7b6ccdee-5b0b-4411-8f21-b75fe7879f5f", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "357563a6-8a44-40b1-8bdf-d11330ea58bb", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "3668396b-2055-417b-bef3-974623bee721", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "0cdec945-916c-4d47-b14c-1059ce8189e3", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "8eb45463-3eb1-4543-ad3c-c8f4cb00a530", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "abf75403-ad7d-4364-9f33-92cfb306f000", "from": "Input", "name": "template", "type": "String", "value": "请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n问题:{{query}}"}, {"id": "7f267a0f-3a63-434f-a538-105229be8af6", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "b123d3e4-0570-42c6-a20b-6fbcb0235674", "from": "Reference", "name": "query", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "6a5694e0-5100-4697-bb4e-8b030ed89624", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "9eed644b-62ae-4fd2-b5da-c7b5a4244059", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "2dcf3b33-dd57-4bb6-ae66-f2484d44d2e6", "from": "Input", "name": "systemPrompt", "type": "String", "value": "角色:你是一个问界试驾助手。\n限制:对于涉及其他品牌车型的问题,不进行详细回答,建议用户提问关于问界车型的问题或是一般性的汽车知识问题。"}, {"id": "eed2b900-e733-4ca0-adbb-18d6a0b6082b", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "177986fa-db8d-4f92-a6f2-546b7232b94b", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "ca60a33d-41f2-4d36-bac2-19e8aa0cf6dc", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "98f2c414-2a1b-4d27-86aa-361a66973fd9", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "2e94082b-930d-4016-a180-a07bd0ad8e50", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "df7f2c19-3d1a-4531-a0a5-11d31286e6e8", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jade3oqd4e", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"name": "确认试驾车型", "task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "9ac3705a-a49d-4240-87dc-7963533a46a5", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "from": "Reference", "name": "car", "type": "String", "value": ["output"], "displayName": "试驾车型", "referenceId": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "referenceKey": "output", "referenceNode": "jadee2orhp"}]}, {"id": "a4dfd48f-e998-4725-bb71-dfd0644785e2", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "from": "Reference", "name": "car", "type": "String", "value": ["output"], "options": {"from": "Reference", "value": [], "referenceId": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "referenceNode": "jademzanbn"}, "renderType": "Input", "displayName": "试驾车型", "referenceId": "b2e1f031-2fc8-410a-b2ec-73157db9750e", "referenceKey": "output", "referenceNode": "jadee2orhp"}]}}], "outputParams": [{"id": "41e1e5aa-846e-48ce-92ea-07e4d61f6553", "name": "output", "type": "Object", "value": [{"id": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "name": "car", "type": "String", "value": ""}]}]}}}, "type": "intelligentFormNodeState", "metaId": "jadeq2zedq", "runnable": true, "triggerMode": "manual"}, {"name": "大模型_1", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "905a7ab5-9f27-4ed8-b126-2a41394d9fe4", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "d3ac3f4a-d699-4341-89c3-d1c4921c1d2e", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "316ae379-274d-4fda-af6b-2f28b0356e2f", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "9ab32d21-b81b-4f13-9b5c-cd849ee7d334", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "9ee1c339-c88d-4248-86d6-139b64b763cf", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "99c6709d-5907-49e5-8394-0d294e8a3053", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "9646d069-0d62-4da2-a6cd-709c2e6b2d06", "from": "Input", "name": "template", "type": "String", "value": "通过工具获取车型信息,详细介绍这个车型:{{query}} "}, {"id": "16b3b1ba-cefd-4353-a29f-8004670c684a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "4c02d0da-7879-4461-8090-960554b32128", "from": "Reference", "name": "query", "type": "String", "value": ["output", "car"], "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}]}, {"id": "c04281f5-6769-476f-8d7d-7d21aa714ca8", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "06142d65-2487-4a40-b592-ac4638ac527d", "from": "Input", "name": "问界车型信息查询", "tags": ["FIT"], "type": "String", "value": "7cba6fdd-8c6d-410f-81dc-775ffe96902b", "version": "1.0.0"}]}, {"id": "a188c341-d4c0-46a0-8a8c-7438b05a4b6c", "from": "Expand", "name": "workflows", "type": "Array", "value": []}, {"id": "14e1b376-64ab-4ce1-86d2-e3285cea971c", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "2308bdeb-ff69-4cd9-afd4-ba8132c5d939", "from": "input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "7f9b538a-7d1b-442d-b45e-5041364cfd4d", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "0"}, {"id": "62639076-5d67-4e37-bb54-024bdac816ec", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "8f3747e9-318f-49de-9de8-9610cf0049fc", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "d2156da5-09a9-4c8f-bf1a-9c63c457d439", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "6714da5e-b514-4911-b04b-96e3b83c15d7", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jadebywy1i", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadebywy1i", "from": "jadejnmnp4", "type": "jadeEvent", "metaId": "jadew1opy5", "runnable": true, "fromConnector": "E"}, {"name": "确认试驾车型2", "task": {"type": "AIPP_SMART_FORM", "taskId": "a910a3d38a4549eda1112beee008419d", "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "2f92cbde-4e4b-4e7e-8307-18633cbb4352", "from": "Expand", "name": "data", "type": "Object", "value": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "displayName": "我要试驾车型", "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}, {"id": "76c41bdf-8383-4318-a667-fd61b42609ab", "from": "Input", "name": "schema", "type": "Object", "value": {"parameters": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "options": {"from": "Reference", "value": [], "referenceId": "f903ce3c-173c-4010-8e14-8e18d64c9b27", "referenceNode": "jademzanbn"}, "renderType": "Input", "displayName": "我要试驾车型", "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}}], "outputParams": [{"id": "a8df0b85-ef18-4362-b94b-df12022d1e13", "name": "output", "type": "Object", "value": [{"id": "610f8341-46f0-4e26-a6b8-58be5d52f560", "name": "car", "type": "String", "value": ""}]}]}}}, "type": "intelligentFormNodeState", "metaId": "jadez5nidp", "runnable": true, "triggerMode": "manual"}, {"name": "车型提取2", "type": "textExtractionNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "extractParam"}, {"name": "memoryConfig"}, {"name": "memorySwitch"}, {"name": "histories"}], "return": {"type": "object"}, "uniqueName": "3bca6a3f-9623-4228-b120-1a5e0d41dc14"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"stageDesc": "分析试驾车型中...", "inputParams": [{"id": "extractParam_db169c41-8428-4d04-9ca9-d5462638b9de", "from": "Expand", "name": "extractParam", "type": "Object", "value": [{"id": "text_ed4e777e-3973-4dfe-a5cd-8b6997e3c16f", "from": "Reference", "name": "text", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}, {"id": "desc_c5827ce1-b1c7-4959-98a6-79736761bfd7", "from": "Input", "name": "desc", "type": "String", "value": "车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "}, {"id": "outputSchema_2dda5dc9-c4f7-4f4e-a3ac-60a5387d66c9", "from": "Input", "name": "outputSchema", "type": "String", "value": "{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"}, {"id": "6958ac49-541d-4768-b198-dfdf3767649a", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "fb995664-4eb1-49e2-80ba-b6d2991eedd5", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "755800ba-5209-4a3c-90b0-b4aa4bfd8fe2", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "temperature_6bb0ad3b-7bbf-4110-acbd-f4b103214374", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}]}, {"id": "memoryConfig_392e3f83-1fe1-43b5-88ae-afb9999ee4ee", "from": "Expand", "name": "memoryConfig", "type": "Object", "value": [{"id": "windowAlg_cde542de-39f0-4550-91dd-b1cf979f310d", "from": "Input", "name": "windowAlg", "type": "String", "value": "buffer_window"}, {"id": "serializeAlg_153d40da-349c-466e-8780-6eefc42ae5c6", "from": "Input", "name": "serializeAlg", "type": "String", "value": "full"}, {"id": "property_fb24ab56-f614-412b-b6d6-dfbbbc189365", "from": "Input", "name": "property", "type": "Integer", "value": "0"}]}, {"id": "memorySwitch_6b354c48-ed57-44b3-91a4-5940425dc1dc", "from": "Input", "name": "memorySwitch", "type": "Boolean", "value": false}, {"id": "histories_d7e7c55e-d25b-4e03-80f1-b9937979faf1", "from": "Reference", "name": "histories", "type": "Array", "value": ["memories"], "referenceId": "memories", "referenceKey": "memories", "referenceNode": "_systemEnv"}], "outputParams": [{"id": "b65edebe-1855-4502-a72f-0a207fd6c99f", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "66248c5c-9f8f-4000-8597-e8d6b0d7b16d", "from": "Expand", "name": "extractedParams", "type": "Object", "value": [{"id": "eb21be95-d82b-49b3-9eaf-455f4e449410", "from": "Input", "name": "carTypes", "type": "Array", "value": "", "description": "问界的车型列表,具体到型号"}]}, {"id": "success_e1a7af2a-fde0-4e73-81c1-cced296e08e8", "from": "Input", "name": "success", "type": "Boolean", "value": "Boolean"}]}], "enableStageDesc": true, "jadeNodeConfigChangeIgnored": false}}}, "metaId": "jadetvqkdx", "runnable": true, "stageDesc": "分析试驾车型中...", "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto", "enableStageDesc": true}, {"name": "聚合车型列表", "type": "codeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "ee12101b-d717-4f49-9ff5-6d840676047a", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "b8d2fbbf-1152-4403-a6dc-8ae88d7ddec3", "from": "Reference", "name": "input1", "type": "Object", "value": ["output"], "referenceId": "30bf32bc-b320-418e-ae62-b30d45b26255", "referenceKey": "output", "referenceNode": "jademzanbn"}, {"id": "662c4a18-21e8-47e0-bce2-10c89cb51b63", "from": "Reference", "name": "input2", "type": "Object", "value": ["output"], "referenceId": "b65edebe-1855-4502-a72f-0a207fd6c99f", "referenceKey": "output", "referenceNode": "jadetvqkdx"}]}, {"id": "08e9c25a-e55c-4620-bcca-325d714160d5", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n if args[''input1'']:\n return args[''input1'']\n else:\n return args[''input2''] \n", "language": "python"}, {"id": "7df2184c-f82f-43ab-9a30-d695fae550ea", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "35868ea7-d973-4a6c-b9f2-340a06f9f112", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "object", "properties": {}, "description": ""}}}}], "outputParams": [{"id": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "from": "Expand", "name": "output", "type": "Object", "value": []}]}}}, "metaId": "jadez5f1df", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadez5f1df", "from": "jademzanbn", "type": "jadeEvent", "metaId": "jadeanptlq", "runnable": true, "fromConnector": "E"}, {"to": "jadez5f1df", "from": "jadetvqkdx", "type": "jadeEvent", "metaId": "jade58w7a7", "runnable": true, "fromConnector": "E"}, {"to": "jade0ibwv7", "from": "jadez5f1df", "type": "jadeEvent", "metaId": "jadexs8po6", "runnable": true, "fromConnector": "E"}, {"name": "提取车型列表", "type": "codeNodeState", "jober": {"name": "", "type": "STORE_JOBER", "entity": {"params": [{"name": "args"}, {"name": "code"}, {"name": "language"}, {"name": "output"}], "return": {"type": "object"}, "uniqueName": "e147f301-957a-4335-a155-1e86d1a45ae5"}, "fitables": [], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "ac8da121-58a3-4801-82f3-862533c0db2b", "from": "Expand", "name": "args", "type": "Object", "value": [{"id": "90d97368-2212-4176-ab52-55215cb5d6c6", "from": "Reference", "name": "input", "type": "Object", "value": ["output"], "referenceId": "7f1994bf-3a0b-442a-9bbb-d0069949fbbd", "referenceKey": "output", "referenceNode": "jadez5f1df"}]}, {"id": "5abd8220-a2e6-47a1-b3d7-61da5c91a7c8", "from": "Input", "name": "code", "type": "String", "value": "async def main(args: Args) -> Output:\n return args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])", "language": "python"}, {"id": "d09bd08d-d2f7-47c2-a74b-a9f9fe899387", "from": "Input", "name": "language", "type": "String", "value": "python"}, {"id": "c57b604c-0ae0-4d32-bef3-30c14793ddf1", "from": "Input", "name": "output", "type": "Object", "value": {"properties": {"output": {"type": "array", "description": ""}}}}], "outputParams": [{"id": "504a2241-6962-4de4-901a-3df2b798eaa2", "from": "Expand", "name": "output", "type": "Array", "value": ""}]}}}, "metaId": "jadebz0q8j", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jadebz0q8j", "from": "jadeh0ccb7", "type": "jadeEvent", "metaId": "jadep8j6qd", "runnable": true, "fromConnector": "dynamic-999"}, {"to": "jadejnmnp4", "from": "jadebz0q8j", "type": "jadeEvent", "metaId": "jader2ugw2", "runnable": true, "fromConnector": "E"}, {"name": "结束_1", "type": "endNodeEnd", "metaId": "jadeaevz6w", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "e42435d1-bd40-4a8f-a6da-6ce3b8c9e936", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "0cecce75-2752-4f0b-8c01-e55897f82eef", "from": "Reference", "name": "finalOutput", "type": "String", "value": ["output", "llmOutput"], "editable": true, "isRequired": true, "description": "", "referenceId": "2e94082b-930d-4016-a180-a07bd0ad8e50", "referenceKey": "llmOutput", "referenceNode": "jade3oqd4e"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "f7b318fa-c166-4b2d-b72a-e6b90b8c7324", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadeaevz6w", "from": "jade3oqd4e", "type": "jadeEvent", "metaId": "jadewhsta6", "runnable": true, "fromConnector": "E"}, {"name": "结束_3", "type": "endNodeEnd", "metaId": "jadevgtszg", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "10392a5e-479e-468d-9a87-bb85a2649cc2", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "b2ab53ef-8e29-4a95-9fed-be9894df8806", "from": "Input", "name": "out", "type": "String", "value": "您选择的试驾车型为:", "editable": true, "isRequired": true, "description": ""}, {"id": "31d34355-c6d3-459d-ac61-66eaf31f6fd4", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "editable": true, "isRequired": true, "description": "", "referenceId": "610f8341-46f0-4e26-a6b8-58be5d52f560", "referenceKey": "car", "referenceNode": "jadez5nidp"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "89dfe7b5-972a-4b2a-a2b4-86c9b851fe05", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadevgtszg", "from": "jadez5nidp", "type": "jadeEvent", "metaId": "jade90v0rb", "runnable": true, "fromConnector": "E"}, {"name": "结束_2", "type": "endNodeEnd", "metaId": "jade9wr70l", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "bf589d2e-aff9-46f4-a266-4f3b6b4ea6e7", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "9104e96f-26ec-46cf-88b4-1ff929f7b983", "from": "Input", "name": "out", "type": "String", "value": "您选择的试驾车型为:", "editable": true, "isRequired": true, "description": ""}, {"id": "7c278873-cea0-424b-b344-e22ab070aeb3", "from": "Reference", "name": "car", "type": "String", "value": ["output", "car"], "editable": true, "isRequired": true, "description": "", "referenceId": "2c678112-4c6d-44b2-a1f6-3d4b8718b893", "referenceKey": "car", "referenceNode": "jadeq2zedq"}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "ab34cf0f-7fa9-424d-8b01-edc402fb163d", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"name": "结束", "type": "endNodeEnd", "metaId": "jadehrizke", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8fca4472-cb61-474b-97c8-be14566e998a", "from": "Expand", "name": "finalOutput", "type": "Object", "value": [{"id": "1e9b929c-bd6d-46c1-9b8c-23d1a0f7a7cc", "from": "Input", "name": "out", "type": "String", "value": "非常抱歉,不支持该车型的试驾。", "editable": true, "isRequired": true, "description": ""}], "editable": false, "isRequired": false, "referenceId": "", "referenceKey": "", "referenceNode": ""}, {"id": "8fcd715b-5e4c-488c-bcb7-ec5f4b5c83c2", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}], "outputParams": [{}]}}}, "runnable": true, "triggerMode": "auto"}, {"to": "jadehrizke", "from": "jadeh0ccb7", "type": "jadeEvent", "metaId": "jadeke55eo", "runnable": true, "fromConnector": "dynamic-0|23d6704f-527f-43d9-8621-d8577d71dfec"}, {"name": "用户问题分类", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "8e3a79d1-d39d-4815-9236-2e08bcbeb89d", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "d9ef67b3-a4e8-4b6c-b15b-0f55de543159", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "22a75a6e-c13a-45e7-9492-15613f79b1d8", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "a8265476-b028-4b32-88a4-cd9e27609d86", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "2c6a7c43-a6d7-4850-a60f-43d615085630", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "80222643-ef96-41ca-b9f6-6eae7f267a69", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "d655ab25-92c4-4af2-a01a-3e7e22a84362", "from": "Input", "name": "template", "type": "String", "value": "\"请分析用户的问题,并判断其意图是否是与问界汽车或是问界试驾有关。\n 如果用户的问题涉及问界汽车信息的询问,(例如:“请介绍一下问界M5”、 “给我推荐一款问界汽车”),则返回 ‘问界汽车问题’。\n如果用户只是咨询试驾相关内容(例如:“我想试驾问界M5”、“我想试驾保时捷911”),则返回 ‘试驾汽车问题’。\n如果用户问题是关于**其他品牌**的汽车或是一般性闲聊(例如:“最近有什么有趣的事?”、“今天天气不错”)则返回 ‘其他问题’\"\n用户问题:{{input}}\n**重要:不要回答``问界汽车问题``、``试驾汽车问题``、``其他问题``以外的文字**"}, {"id": "cfe7e088-9f69-4671-a44a-4cd0f553920e", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "046e05a4-4aa1-4694-8427-634bd97596d0", "from": "Reference", "name": "input", "type": "String", "value": ["Question"], "referenceId": "input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb", "referenceKey": "Question", "referenceNode": "jade6qm5eg"}]}]}, {"id": "248bb3d3-abcb-4072-8b14-360428994714", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "3"}, {"id": "89b4068b-9926-4429-a334-9b6daefc11d0", "from": "Expand", "name": "tools", "type": "Array", "value": []}, {"id": "267e88b8-1cdc-4312-a117-a96ea6c37514", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "3b9fafe7-20ac-4d59-be6c-1dd61b264e70", "from": "Input", "name": "enableLog", "type": "Boolean", "value": false}, {"id": "ed3e6fda-3a66-405c-b5d4-100822755cd8", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "d4ae45a4-74cc-498c-9e6e-69a1694614fb", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "2d7b57fb-c452-41d3-9fed-bed3289c64eb", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jade1tc9vb", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jade1tc9vb", "from": "jade6qm5eg", "type": "jadeEvent", "metaId": "jadeknglms", "runnable": true, "fromConnector": "E"}, {"name": "条件_1", "type": "conditionNodeCondition", "metaId": "jade5pubt6", "runnable": true, "triggerMode": "auto", "conditionParams": {"branches": [{"id": "407a4d42-fe33-427e-8553-9de19e9bb002", "type": "if", "runnable": true, "conditions": [{"id": "e6cc2561-bf7f-43e9-873e-675ef7fa1340", "value": [{"id": "146ae59a-df8a-4181-9dbc-466e6b81cdb9", "from": "Reference", "name": "left", "type": "String", "value": ["output", "llmOutput"], "referenceId": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "referenceKey": "llmOutput", "referenceNode": "jade1tc9vb"}, {"id": "5a2bd11a-bbff-4e14-81a7-8f5d747d9e61", "from": "Input", "name": "right", "type": "String", "value": "问界汽车问题", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "7fe7b5fa-c661-4ba5-9f87-4e8070496dd0", "type": "if", "runnable": true, "conditions": [{"id": "fc38c7be-06ac-41eb-a896-e6bb0f9129d0", "value": [{"id": "767ed731-7595-46a6-9448-dcac3635694a", "from": "Reference", "name": "left", "type": "String", "value": ["output", "llmOutput"], "referenceId": "e8bd45b4-2216-4eda-af81-d008bb6012a9", "referenceKey": "llmOutput", "referenceNode": "jade1tc9vb"}, {"id": "1ce1f111-7dec-4fac-b6e5-fd02d5b78557", "from": "Input", "name": "right", "type": "String", "value": "试驾汽车问题", "referenceId": "", "referenceKey": "", "referenceNode": ""}], "condition": "equal"}], "conditionRelation": "and"}, {"id": "dc589e0f-3202-46e4-87f4-e550d54bbbc5", "type": "else", "runnable": true, "conditions": [{"id": "8d21bec4-7de8-4b35-9561-2e4374e42780", "value": [], "condition": "true"}], "conditionRelation": "and"}], "jadeNodeConfigChangeIgnored": true}}, {"to": "jade5pubt6", "from": "jade1tc9vb", "type": "jadeEvent", "metaId": "jadey81zoy", "runnable": true, "fromConnector": "E"}, {"to": "jadewdnjbq", "from": "jade5pubt6", "type": "jadeEvent", "metaId": "jade8w9r8o", "runnable": true, "fromConnector": "dynamic-0|407a4d42-fe33-427e-8553-9de19e9bb002"}, {"to": "jadetvqkdx", "from": "jade5pubt6", "type": "jadeEvent", "metaId": "jadewp6fyj", "runnable": true, "fromConnector": "dynamic-1|7fe7b5fa-c661-4ba5-9f87-4e8070496dd0"}, {"to": "jade3oqd4e", "from": "jade5pubt6", "type": "jadeEvent", "metaId": "jadeu36msk", "runnable": true, "fromConnector": "dynamic-999"}, {"name": "大模型_4", "type": "llmNodeState", "jober": {"name": "", "type": "general_jober", "isAsync": "true", "fitables": ["modelengine.fit.jober.aipp.fitable.LLMComponent"], "converter": {"type": "mapping_converter", "entity": {"inputParams": [{"id": "09e984b4-261d-4db4-9a79-bfdcab112355", "from": "Input", "name": "model", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "f7469288-be18-4f27-8e0c-985b6444c3cf", "from": "Expand", "name": "accessInfo", "type": "Object", "value": [{"id": "57eece3d-c599-444e-8ad8-b76f7b2594ca", "from": "Input", "name": "serviceName", "type": "String", "value": "Qwen/Qwen2.5-72B-Instruct"}, {"id": "96ab5533-d3d0-4018-a341-16667d0df21b", "from": "Input", "name": "tag", "type": "String", "value": "SiliconFlow,Jade"}]}, {"id": "6ee7671a-c9e2-4ae7-9348-d0f4c548e025", "from": "Input", "name": "temperature", "type": "Number", "value": "0.3"}, {"id": "4e9c98fc-6547-4fd0-a71e-e21b9cd52f41", "from": "Expand", "name": "prompt", "type": "Object", "value": [{"id": "e7cd99dd-0143-44a9-81ee-ccebcc8b6a2c", "from": "Input", "name": "template", "type": "String", "value": "通过工具查询车型的图片:{{query}} "}, {"id": "56e29f98-ae8e-478c-b02e-75b8149f090a", "from": "Expand", "name": "variables", "type": "Object", "value": [{"id": "77353774-8079-4f34-8672-44df471f2c2a", "from": "Reference", "name": "query", "type": "String", "value": ["output", "car"], "referenceId": "input_1bb450c2-e4f9-471f-ac11-4760ed04461c", "referenceKey": "car", "referenceNode": "jadejnmnp4"}]}]}, {"id": "6dd50ee8-6572-43d1-a7da-97c88138bf5d", "from": "Input", "name": "maxMemoryRounds", "type": "Integer", "value": "0"}, {"id": "3d26bc7c-3757-4b90-b803-f919123bd1dc", "from": "Expand", "name": "tools", "type": "Array", "value": [{"id": "bf11ba32-9201-4618-8084-66c81e4a0608", "from": "Input", "name": "问界车型宣传图片", "tags": ["FIT"], "type": "String", "value": "e754f978-d236-44fa-aa55-e8eb5d8f8269", "version": "1.0.0"}]}, {"id": "e0ff690b-0e1c-4086-a299-94ab3cbfe5e5", "from": "Input", "name": "systemPrompt", "type": "String", "value": ""}, {"id": "267a67d4-c64c-488d-8263-7977c26b1c23", "from": "Input", "name": "enableLog", "type": "Boolean", "value": true}, {"id": "4cfaec42-099b-4ec4-ae23-a3f47d9db635", "from": "Expand", "name": "knowledgeBases", "type": "Array", "value": []}], "outputParams": [{"id": "29377aa8-2eb4-4a99-99c5-19ef52f309a0", "from": "Expand", "name": "output", "type": "Object", "value": [{"id": "c7836226-62a7-47e6-b4fa-705ed2acffed", "from": "Input", "name": "llmOutput", "type": "String", "value": "", "description": ""}, {"id": "a2263372-420d-404c-93c0-e4e0edd79f39", "from": "Input", "name": "reference", "type": "Array", "value": [], "description": ""}]}], "tempReference": {}}}}, "metaId": "jade9wm99z", "runnable": true, "joberFilter": {"type": "MINIMUM_SIZE_FILTER", "threshold": 1}, "triggerMode": "auto"}, {"to": "jade9wm99z", "from": "jadebywy1i", "type": "jadeEvent", "metaId": "jadepdt7pq", "runnable": true, "fromConnector": "E"}, {"to": "jadez5nidp", "from": "jade9wm99z", "type": "jadeEvent", "metaId": "jade28pvut", "runnable": true, "fromConnector": "E"}, {"to": "jadee2orhp", "from": "jadeh0ccb7", "type": "jadeEvent", "metaId": "jadeam5lo5", "runnable": true, "fromConnector": "dynamic-1|6f8c44d7-ad5c-4004-95e0-e01c391be100"}, {"to": "jadeq2zedq", "from": "jadee2orhp", "type": "jadeEvent", "metaId": "jade13kr3a", "runnable": true, "fromConnector": "E"}, {"to": "jade9wr70l", "from": "jadeq2zedq", "type": "jadeEvent", "metaId": "jadef977tc", "runnable": true, "fromConnector": "E"}], "metaId": "5711f3230eb94abdb168e61d2082d1d2", "status": "active", "version": "1.0.0", "callback": {"name": "通知回调", "type": "general_callback", "fitables": ["modelengine.fit.jober.fitable.FlowInfoCallback"]}, "enableOutputScope": true, "exceptionFitables": ["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler", "modelengine.fit.jober.fitable.FlowInfoException"]}', 'Jade', '2025-04-19 09:15:55.578319') ON CONFLICT (definition_id) DO NOTHING; + +INSERT INTO "public"."flow_graph" ("id", "version", "tenant", "status", "name", "data", "created_by", "created_at", "updated_by", "updated_at", "previous", "is_deleted") VALUES ('5711f3230eb94abdb168e61d2082d1d2', '1.0.0', '31f20efc7e0848deab6a6bc10fc3021e', 'active', '5711f3230eb94abdb168e61d2082d1d2', '{"id":"5711f3230eb94abdb168e61d2082d1d2","title":"5711f3230eb94abdb168e61d2082d1d2","source":"elsa","type":"jadeFlowGraph","tenant":"31f20efc7e0848deab6a6bc10fc3021e","setting":{"borderColor":"#047bfc","backColor":"whitesmoke","headColor":"steelblue","fontColor":"steelblue","captionfontColor":"whitesmoke","fontFace":"arial","captionfontFace":"arial black","fontSize":12,"captionfontSize":14,"fontStyle":"normal","captionfontStyle":"normal","fontWeight":"lighter","captionfontWeight":"lighter","hAlign":"center","vAlign":"top","captionhAlign":"center","lineHeight":1.5,"lineWidth":2,"captionlineHeight":1,"focusMargin":0,"focusBorderColor":"#047bfc","focusFontColor":"darkorange","focusBackColor":"whitesmoke","mouseInColor":"orange","mouseInBorderColor":"#047bfc","mouseInFontColor":"orange","mouseInBackColor":"whitesmoke","borderWidth":1,"focusBorderWidth":1,"globalAlpha":1,"backAlpha":0.15,"cornerRadius":4,"dashWidth":0,"autoText":false,"autoHeight":false,"autoWidth":false,"margin":25,"pad":10,"code":"","rotateDegree":0,"shadow":"","focusShadow":"","shadowData":"2px 2px 4px","outstanding":false,"pDock":"none","dockMode":"none","priority":0,"infoType":{"name":"none","next":"INFORMATION"},"progressStatus":{"name":"NONE","next":"UNKNOWN","color":"gray"},"progressPercent":0.65,"showedProgress":false,"itemPad":[5,5,5,5],"itemScroll":{"x":0,"y":0},"scrollLock":{"x":false,"y":false},"resizeable":true,"selectable":true,"rotateAble":true,"editable":true,"moveable":true,"dragable":true,"visible":true,"deletable":true,"allowLink":true,"shared":false,"strikethrough":false,"underline":false,"numberedList":false,"bulletedList":false,"enableAnimation":false,"enableSocial":true,"emphasized":false,"bulletSpeed":1,"tag":{},"allNodeNumLimit":99,"sameTypeNodeNumLimit":19,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10},"pages":[{"x":-449.2228577394942,"y":648.7782362023345,"id":"elsa-page:tvp1s6","bold":false,"mode":"configuration","text":"newFlowPage","type":"jadeFlowPage","dirty":true,"index":0,"width":1600,"hAlign":"left","height":800,"isPage":true,"italic":false,"scaleX":0.7732920492783848,"scaleY":0.7732920492783848,"vAlign":"top","itemPad":[0,0,0,0],"division":-1,"dockMode":"none","fontFace":"arial","fontSize":18,"hideText":true,"moveable":true,"shapesAs":{},"backColor":"#fbfbfc","container":"elsa-page:tvp1s6","dockAlign":"top","fontColor":"#ECD0A7","fontStyle":"normal","itemSpace":5,"namespace":"jadeFlow","fontWeight":"bold","itemScroll":{"x":0,"y":0},"borderColor":"white","focusBackColor":"#fbfbfc","shapes":[{"x":-1123.5461204767116,"y":184.25814386932322,"id":"jade6qm5eg","pad":6,"bold":false,"text":"开始","type":"startNodeStart","dirty":false,"index":0,"width":360,"height":226,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"inputParams":[{"id":"91138f09-b635-43df-95c6-1fe3d1745829","from":"Expand","name":"input","type":"Object","value":[{"id":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","from":"Input","name":"Question","type":"String","value":"","isVisible":true,"isRequired":true,"description":"这是用户输入的问题。","displayName":"用户问题","disableModifiable":true}],"config":[{"allowAdd":false}]},{"id":"4a770dc6-e3c9-475d-84c7-48dacc74a5b6","from":"Expand","name":"memory","type":"Object","value":[{"id":"a7675623-7fc7-468c-8910-e73c70e5e468","from":"Input","name":"memorySwitch","type":"Boolean","value":true},{"id":"cee9a31b-781c-4835-a616-ceed73be22f2","from":"Input","name":"type","type":"String","value":"ByConversationTurn"},{"id":"69592622-4291-409d-9d65-9faea83db657","from":"Input","name":"value","type":"Integer","value":"3"}]}],"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":false,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"startComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"mouseInBorderColor":"rgba(28,31,35,.08)"},{"x":819.1428754447782,"y":139.13955828369046,"id":"jadewdnjbq","pad":6,"bold":false,"text":"大模型","type":"llmNodeState","dirty":false,"index":1,"width":360,"height":344,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"31ba235d-1b26-4780-a7a7-32eca9500919","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"83653b54-dd04-4da9-957d-adb7c2728632","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"dd588a17-a69c-40c0-859a-d9930202a148","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"6c414e75-971e-403a-b2b1-c6850f128cc4","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"db5fdafa-4cbf-44ba-9cca-8a98f1f771f4","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"88f74d78-4711-4f81-a2e7-74d0034c5e88","from":"Expand","name":"prompt","type":"Object","value":[{"id":"35a710cf-1b79-4523-b16f-b50878d677fe","from":"Input","name":"template","type":"String","value":"如果用户未指定具体车型,则按照用户问题,结合工具提供的问界车型信息,给出简略概要类的回答。\n问题:{{query}} "},{"id":"38fb27a1-71f4-4fcc-87d5-9d8a880bc04d","from":"Expand","name":"variables","type":"Object","value":[{"id":"eee66922-4304-4209-89fc-b13ffa101630","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"a6865419-867c-4bfb-855c-f5c1876c965a","from":"Expand","name":"tools","type":"Array","value":[{"id":"29d1707c-beaa-44c8-9eb2-08462880b031","from":"Input","name":"问界车型信息查询","tags":["FIT"],"type":"String","value":"7cba6fdd-8c6d-410f-81dc-775ffe96902b","version":"1.0.0"}]},{"id":"308e2023-a8e9-486e-9784-8680addbb786","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"68f92923-d5da-42ce-8478-d7ac7d90664e","from":"Input","name":"systemPrompt","type":"String","value":"你是一个具备自主工具调用能力的智能体,可以展示中间的思考过程给用户。你的回答有两种情况:\n\n无需调用外部工具\n\n如果问题可通过已有对话历史或直接推理得到答案,直接输出最终结果,不需使用任何标签包装,也不显示详细思考过程。\n需要调用外部工具解决的复杂问题\n\n必须采用以下严格的标签体系输出,每个标签之间空一行,且仅展示真实的工具调用结果:\n...:展示你内部的思考过程。注意,这部分内容可以展示给用户,但仅限于描述思路,不应包含任何伪造的工具调用结果。\n...:描述你准备调用工具的原因和计划。此处仅说明你需要调用哪个工具以及原因,工具的名称对人类阅读要友好,切勿直接模拟或输出工具返回内容。\n...:当你真正调用某个工具后,等待工具反馈,然后将工具调用的返回结果做非常简略的摘要后放在此标签内,摘要字数在20字以内。绝对禁止在未获得真实工具反馈前预先构造。 标签内容。\n...:在获取所有真实工具调用结果后,将整合信息给出最终答案。\n**重要要求**:\n- **不要输出tool_call标签**。\n- **答案必须详细完整**,不仅仅是工具返回结果的简单总结,而是对结果进行深入分析和整合,并提供背景解释、推理过程和可行性分析。\n- **确保所有关键信息得到展开**,避免省略任何重要内容。\n- **如果适用,可以提供额外的解释、使用建议或应用场景,以增强回答的实用性**。\n- 请使用**标准 Markdown 语法**输出答案,保证语法完整,**不要拆分**列表结构。\n- **输出此标签后,不得追加任何其他内容或标签**。\n\n严格要求\n\n切勿在中间思考或工具调用计划中,提前生成伪造的 标签内容。必须在实际调用工具并获得反馈后,再以 标签展示真实结果,再生成 标签输出最终答案。\n如果历史对话中已包含真实的工具调用结果,应直接使用这些信息构造最终答案,避免重复调用或展示多余标签。\n在所有工具调用完成之前,不得输出 标签;只有在确认所有真实工具反馈后,才生成最终答案。"},{"id":"78baad16-173f-4d70-a7cd-d1a2abc2f0d1","from":"input","name":"enableLog","type":"Boolean","value":true},{"id":"6d8d88d4-ebaa-4a57-9279-ae5b26654b5c","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"e1b4bea8-0028-49a1-9e7d-ec29cff215da","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"95d84d67-3198-415e-a63c-bc9a2da8d821","from":"Expand","name":"output","type":"Object","value":[{"id":"272c927a-9e25-48b6-a921-6a8ab20267a4","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"5782976c-0c1c-47b8-bcd5-8b6e22e52d39","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1324.8563982124583,"y":33.988095238095184,"id":"jademzanbn","pad":6,"bold":false,"text":"车型提取","type":"textExtractionNodeState","dirty":false,"index":2,"width":360,"height":334,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"extractParam"},{"name":"memoryConfig"},{"name":"memorySwitch"},{"name":"histories"}],"return":{"type":"object"},"uniqueName":"3bca6a3f-9623-4228-b120-1a5e0d41dc14"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"stageDesc":"分析试驾车型中...","inputParams":[{"id":"extractParam_2ca71bb8-ea6e-4d08-8f24-16a6fc3b64b6","from":"Expand","name":"extractParam","type":"Object","value":[{"id":"text_5d4f3012-93a0-4ebf-a9f1-17a9fe252651","from":"Reference","name":"text","type":"String","value":["output","llmOutput"],"referenceId":"272c927a-9e25-48b6-a921-6a8ab20267a4","referenceKey":"llmOutput","referenceNode":"jadewdnjbq"},{"id":"desc_12bbcd63-44c5-48d3-88c6-8b3d48b7c431","from":"Input","name":"desc","type":"String","value":"车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "},{"id":"outputSchema_77df0737-ef00-4e3b-864c-31402655c65c","from":"Input","name":"outputSchema","type":"String","value":"{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"},{"id":"ed9f7d7e-4173-465b-a3f5-4d34345efa53","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"49a6dd9d-589f-4ca2-b66f-1b1951c5e886","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"582a3d24-d8d8-41c6-b9b7-277d5d994683","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"temperature_b5d5dd8c-1a39-450d-9851-130ff0aa3a78","from":"Input","name":"temperature","type":"Number","value":"0.3"}]},{"id":"memoryConfig_eed12a01-ccac-4bbd-ae80-39cf2d25a06c","from":"Expand","name":"memoryConfig","type":"Object","value":[{"id":"windowAlg_f20d73ee-6376-4c90-adf2-1386771aaabd","from":"Input","name":"windowAlg","type":"String","value":"buffer_window"},{"id":"serializeAlg_4828a286-e3fd-4803-9887-31517cc82520","from":"Input","name":"serializeAlg","type":"String","value":"full"},{"id":"property_c3ba42cf-cb22-46d4-bdcc-5d86204ad098","from":"Input","name":"property","type":"Integer","value":"0"}]},{"id":"memorySwitch_d7c69077-05bf-4c1b-8e38-735e03d984ad","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"histories_51a356d1-f2ac-4f3b-b444-99a737100996","from":"Reference","name":"histories","type":"Array","value":["memories"],"referenceId":"memories","referenceKey":"memories","referenceNode":"_systemEnv"}],"outputParams":[{"id":"30bf32bc-b320-418e-ae62-b30d45b26255","from":"Expand","name":"output","type":"Object","value":[{"id":"37331645-54f7-4751-8d04-12ab9d74a8a7","from":"Expand","name":"extractedParams","type":"Object","value":[{"id":"f903ce3c-173c-4010-8e14-8e18d64c9b27","from":"Input","name":"carTypes","type":"Array","value":"","description":"问界的车型列表,具体到型号"}]},{"id":"success_e3a426e0-3d67-42e3-b985-1daa32a44d20","from":"Input","name":"success","type":"Boolean","value":"Boolean"}]}],"enableStageDesc":true,"jadeNodeConfigChangeIgnored":false}}},"stageDesc":"分析试驾车型中...","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","enableStageDesc":true},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"textExtractionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1179.1428754447782,"y":311.13955828369046,"id":"jadevbcutd","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":3,"textX":0,"textY":0,"width":145.71352276768016,"hAlign":"center","height":-110.15146304559528,"italic":false,"margin":20,"toShape":"jademzanbn","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadewdnjbq","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1938.9763707660359,"y":-16.95528828179343,"id":"jade0ibwv7","pad":6,"bold":false,"text":"判断车型列表长度","type":"codeNodeState","dirty":false,"index":4,"width":368,"height":252,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"args"},{"name":"code"},{"name":"language"},{"name":"output"}],"return":{"type":"object"},"uniqueName":"e147f301-957a-4335-a155-1e86d1a45ae5"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"cab31f1f-5429-4f02-99b9-540e0d795357","from":"Expand","name":"args","type":"Object","value":[{"id":"bc929113-6c29-46b8-a1ad-8d53f2d9bb2e","from":"Reference","name":"input","type":"Object","value":["output"],"referenceId":"7f1994bf-3a0b-442a-9bbb-d0069949fbbd","referenceKey":"output","referenceNode":"jadez5f1df"}]},{"id":"66c7a482-6c7f-4698-807c-159aaba67485","from":"Input","name":"code","type":"String","value":"async def main(args: dict) -> str:\n is_valid_res = args.get(''input'', {}).get(''success'', bool)\n\n if not is_valid_res:\n return ''empty''\n\n input_list = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n\n if len(input_list) == 0:\n return ''empty''\n elif len(input_list) == 1:\n return ''single''\n else:\n return ''multi''\n","language":"python"},{"id":"5368a24a-8782-4c08-9b79-58bfcd86f6e3","from":"Input","name":"language","type":"String","value":"python"},{"id":"e6aea608-a116-4f5c-9554-1a704547665e","from":"Input","name":"output","type":"Object","value":{"properties":{"output":{"type":"string","description":""}}}}],"outputParams":[{"id":"70793ed8-e5c9-4ede-9945-5db35626f68f","from":"Expand","name":"output","type":"String","value":""}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"codeComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2330.285047191377,"y":120.84717835822356,"id":"jadeh0ccb7","pad":6,"bold":false,"text":"条件","type":"conditionNodeCondition","dirty":false,"index":5,"width":600,"height":284,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"23d6704f-527f-43d9-8621-d8577d71dfec","type":"if","runnable":true,"conditions":[{"id":"ddee81d4-a971-494c-99df-f94f37633531","value":[{"id":"4d1e95b4-e9fd-431c-9d33-2b5c874dea02","from":"Reference","name":"left","type":"String","value":["output"],"referenceId":"70793ed8-e5c9-4ede-9945-5db35626f68f","referenceKey":"output","referenceNode":"jade0ibwv7"},{"id":"57b838c4-c03d-47f3-9f57-3f75d62898f2","from":"Input","name":"right","type":"String","value":"empty","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"6f8c44d7-ad5c-4004-95e0-e01c391be100","type":"if","runnable":true,"conditions":[{"id":"7fd27d06-ac26-4225-a927-2b0d4a773cd4","value":[{"id":"0c2c78a1-6770-4f46-b36e-a30f3ce96b56","from":"Reference","name":"left","type":"String","value":["output"],"referenceId":"70793ed8-e5c9-4ede-9945-5db35626f68f","referenceKey":"output","referenceNode":"jade0ibwv7"},{"id":"082798c7-f2f6-40a8-bc73-edc9a174d0d3","from":"Input","name":"right","type":"String","value":"single","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"73eee09c-caeb-44c1-a54b-65b54d2f8db8","type":"else","runnable":true,"conditions":[{"id":"7f70dfc4-154a-48bb-849a-158e22eff435","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2306.976370766036,"y":109.04471171820657,"id":"jadec676as","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":6,"textX":0,"textY":0,"width":23.30867642534122,"hAlign":"center","height":153.802466640017,"italic":false,"margin":20,"toShape":"jadeh0ccb7","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade0ibwv7","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":3095.528839751806,"y":1087.2644560112249,"id":"jadejnmnp4","pad":6,"bold":false,"text":"选择需要了解的车型","type":"intelligentFormNodeState","dirty":false,"index":7,"width":360,"height":236,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","taskId":"a910a3d38a4549eda1112beee008419d","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"83131a2d-781b-4756-a0c8-91ee865ffbae","from":"Expand","name":"data","type":"Object","value":[{"id":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","from":"Input","name":"car","type":"String","value":"car","displayName":"我想深入了解的车型"},{"id":"28da30c8-2e73-46d1-a1ad-85d8b0c734b9","name":"car-options","from":"Reference","referenceNode":"jadebz0q8j","referenceId":"504a2241-6962-4de4-901a-3df2b798eaa2","referenceKey":"output","value":["output"],"type":"Array"}]},{"id":"c554e101-37b5-4aa1-b9b3-bfcfb2423583","from":"Input","name":"schema","type":"Object","value":{"parameters":[{"id":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","from":"Input","name":"car","type":"String","value":"car","options":{"from":"Reference","type":"Array","value":["output"],"referenceId":"504a2241-6962-4de4-901a-3df2b798eaa2","referenceKey":"output","referenceNode":"jadebz0q8j"},"renderType":"Radio","displayName":"我想深入了解的车型"}]}}],"outputParams":[{"id":"b5220628-4334-42c5-9ac9-93d4fca29ac8","name":"output","type":"Object","value":[{"id":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","name":"car","type":"String","value":""}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"intelligentFormComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3136.121745249294,"y":285.2614783280843,"id":"jadee2orhp","pad":6,"bold":false,"text":"代码_1","type":"codeNodeState","dirty":false,"index":8,"width":368,"height":252,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"args"},{"name":"code"},{"name":"language"},{"name":"output"}],"return":{"type":"object"},"uniqueName":"e147f301-957a-4335-a155-1e86d1a45ae5"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"8f50ae1f-f0fc-4eac-889c-0a53274b9e12","from":"Expand","name":"args","type":"Object","value":[{"id":"11325c8d-0491-4f99-85d9-4df54a3048a5","from":"Reference","name":"input","type":"Object","value":["output"],"referenceId":"7f1994bf-3a0b-442a-9bbb-d0069949fbbd","referenceKey":"output","referenceNode":"jadez5f1df"}]},{"id":"32133c22-59bf-4b46-9eaa-d221a0032c23","from":"Input","name":"code","type":"String","value":"async def main(args: Args) -> Output:\n list_car = args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])\n return list_car[0]","language":"python"},{"id":"ff990b8b-cf0c-436d-a8e7-7d4863e8d396","from":"Input","name":"language","type":"String","value":"python"},{"id":"9012d086-0ac4-4bcd-8455-e9b47e81c589","from":"Input","name":"output","type":"Object","value":{"properties":{"output":{"type":"string","description":""}}}}],"outputParams":[{"id":"b2e1f031-2fc8-410a-b2ec-73157db9750e","from":"Expand","name":"output","type":"String","value":""}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"codeComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":756.4734101887923,"y":1462.429481687775,"id":"jade3oqd4e","pad":6,"bold":false,"text":"大模型_2","type":"llmNodeState","dirty":false,"index":9,"width":360,"height":344,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"263b7842-e631-454a-aca2-8d979a344865","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"7b6ccdee-5b0b-4411-8f21-b75fe7879f5f","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"357563a6-8a44-40b1-8bdf-d11330ea58bb","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"3668396b-2055-417b-bef3-974623bee721","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"0cdec945-916c-4d47-b14c-1059ce8189e3","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"8eb45463-3eb1-4543-ad3c-c8f4cb00a530","from":"Expand","name":"prompt","type":"Object","value":[{"id":"abf75403-ad7d-4364-9f33-92cfb306f000","from":"Input","name":"template","type":"String","value":"请按照以下步骤生成您的回复:\n1. 递归地将问题分解为更小的问题。\n2. 对于每个原子问题,从上下文和对话历史记录中选择最相关的信息。\n3. 使用所选信息生成回复草稿。\n4. 删除回复草稿中的重复内容。\n5. 在调整后生成最终答案,以提高准确性和相关性。\n6. 请注意,只需要回复最终答案。\n-------------------------------------\n问题:{{query}}"},{"id":"7f267a0f-3a63-434f-a538-105229be8af6","from":"Expand","name":"variables","type":"Object","value":[{"id":"b123d3e4-0570-42c6-a20b-6fbcb0235674","from":"Reference","name":"query","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"6a5694e0-5100-4697-bb4e-8b030ed89624","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"9eed644b-62ae-4fd2-b5da-c7b5a4244059","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"2dcf3b33-dd57-4bb6-ae66-f2484d44d2e6","from":"Input","name":"systemPrompt","type":"String","value":"角色:你是一个问界试驾助手。\n限制:对于涉及其他品牌车型的问题,不进行详细回答,建议用户提问关于问界车型的问题或是一般性的汽车知识问题。"},{"id":"eed2b900-e733-4ca0-adbb-18d6a0b6082b","from":"input","name":"enableLog","type":"Boolean","value":true},{"id":"177986fa-db8d-4f92-a6f2-546b7232b94b","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"ca60a33d-41f2-4d36-bac2-19e8aa0cf6dc","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"98f2c414-2a1b-4d27-86aa-361a66973fd9","from":"Expand","name":"output","type":"Object","value":[{"id":"2e94082b-930d-4016-a180-a07bd0ad8e50","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"df7f2c19-3d1a-4531-a0a5-11d31286e6e8","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3641.1444929801864,"y":259.58807973293915,"id":"jadeq2zedq","pad":6,"bold":false,"text":"确认试驾车型","type":"intelligentFormNodeState","dirty":false,"index":10,"width":360,"height":236,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","taskId":"a910a3d38a4549eda1112beee008419d","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"9ac3705a-a49d-4240-87dc-7963533a46a5","from":"Expand","name":"data","type":"Object","value":[{"id":"2c678112-4c6d-44b2-a1f6-3d4b8718b893","from":"Reference","name":"car","type":"String","value":["output"],"displayName":"试驾车型","referenceId":"b2e1f031-2fc8-410a-b2ec-73157db9750e","referenceKey":"output","referenceNode":"jadee2orhp"}]},{"id":"a4dfd48f-e998-4725-bb71-dfd0644785e2","from":"Input","name":"schema","type":"Object","value":{"parameters":[{"id":"2c678112-4c6d-44b2-a1f6-3d4b8718b893","from":"Reference","name":"car","type":"String","value":["output"],"options":{"from":"Reference","value":[],"referenceId":"f903ce3c-173c-4010-8e14-8e18d64c9b27","referenceNode":"jademzanbn"},"renderType":"Input","displayName":"试驾车型","referenceId":"b2e1f031-2fc8-410a-b2ec-73157db9750e","referenceKey":"output","referenceNode":"jadee2orhp"}]}}],"outputParams":[{"id":"41e1e5aa-846e-48ce-92ea-07e4d61f6553","name":"output","type":"Object","value":[{"id":"2c678112-4c6d-44b2-a1f6-3d4b8718b893","name":"car","type":"String","value":""}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"intelligentFormComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3583.4751136945915,"y":810.1291139444377,"id":"jadebywy1i","pad":6,"bold":false,"text":"大模型_1","type":"llmNodeState","dirty":false,"index":11,"width":360,"height":344,"italic":false,"shadow":"0 2px 4px 0 rgba(0,0,0,.1)","flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"905a7ab5-9f27-4ed8-b126-2a41394d9fe4","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"d3ac3f4a-d699-4341-89c3-d1c4921c1d2e","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"316ae379-274d-4fda-af6b-2f28b0356e2f","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"9ab32d21-b81b-4f13-9b5c-cd849ee7d334","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"9ee1c339-c88d-4248-86d6-139b64b763cf","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"99c6709d-5907-49e5-8394-0d294e8a3053","from":"Expand","name":"prompt","type":"Object","value":[{"id":"9646d069-0d62-4da2-a6cd-709c2e6b2d06","from":"Input","name":"template","type":"String","value":"通过工具获取车型信息,详细介绍这个车型:{{query}} "},{"id":"16b3b1ba-cefd-4353-a29f-8004670c684a","from":"Expand","name":"variables","type":"Object","value":[{"id":"4c02d0da-7879-4461-8090-960554b32128","from":"Reference","name":"query","type":"String","value":["output","car"],"referenceId":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","referenceKey":"car","referenceNode":"jadejnmnp4"}]}]},{"id":"c04281f5-6769-476f-8d7d-7d21aa714ca8","from":"Expand","name":"tools","type":"Array","value":[{"id":"06142d65-2487-4a40-b592-ac4638ac527d","from":"Input","name":"问界车型信息查询","tags":["FIT"],"type":"String","value":"7cba6fdd-8c6d-410f-81dc-775ffe96902b","version":"1.0.0"}]},{"id":"a188c341-d4c0-46a0-8a8c-7438b05a4b6c","from":"Expand","name":"workflows","type":"Array","value":[]},{"id":"14e1b376-64ab-4ce1-86d2-e3285cea971c","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"2308bdeb-ff69-4cd9-afd4-ba8132c5d939","from":"input","name":"enableLog","type":"Boolean","value":true},{"id":"7f9b538a-7d1b-442d-b45e-5041364cfd4d","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"0"},{"id":"62639076-5d67-4e37-bb54-024bdac816ec","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"8f3747e9-318f-49de-9de8-9610cf0049fc","from":"Expand","name":"output","type":"Object","value":[{"id":"d2156da5-09a9-4c8f-bf1a-9c63c457d439","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"6714da5e-b514-4911-b04b-96e3b83c15d7","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"flowable","autoHeight":true,"emphasized":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"focusShadow":"0 0 1px rgba(0,0,0,.3),0 4px 14px rgba(0,0,0,.1)","runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3455.528839751806,"y":1205.2644560112249,"id":"jadew1opy5","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":12,"textX":0,"textY":0,"width":127.94627394278541,"hAlign":"center","height":-223.1353420667872,"italic":false,"margin":20,"toShape":"jadebywy1i","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadejnmnp4","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4512.272758733045,"y":788.3278204357666,"id":"jadez5nidp","pad":6,"bold":false,"text":"确认试驾车型2","type":"intelligentFormNodeState","dirty":false,"index":13,"width":360,"height":236,"italic":false,"flowMeta":{"task":{"type":"AIPP_SMART_FORM","taskId":"a910a3d38a4549eda1112beee008419d","converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"2f92cbde-4e4b-4e7e-8307-18633cbb4352","from":"Expand","name":"data","type":"Object","value":[{"id":"610f8341-46f0-4e26-a6b8-58be5d52f560","from":"Reference","name":"car","type":"String","value":["output","car"],"displayName":"我要试驾车型","referenceId":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","referenceKey":"car","referenceNode":"jadejnmnp4"}]},{"id":"76c41bdf-8383-4318-a667-fd61b42609ab","from":"Input","name":"schema","type":"Object","value":{"parameters":[{"id":"610f8341-46f0-4e26-a6b8-58be5d52f560","from":"Reference","name":"car","type":"String","value":["output","car"],"options":{"from":"Reference","value":[],"referenceId":"f903ce3c-173c-4010-8e14-8e18d64c9b27","referenceNode":"jademzanbn"},"renderType":"Input","displayName":"我要试驾车型","referenceId":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","referenceKey":"car","referenceNode":"jadejnmnp4"}]}}],"outputParams":[{"id":"a8df0b85-ef18-4362-b94b-df12022d1e13","name":"output","type":"Object","value":[{"id":"610f8341-46f0-4e26-a6b8-58be5d52f560","name":"car","type":"String","value":""}]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"manual"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"intelligentFormComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1427.020111275216,"y":1032.5974446613131,"id":"jadetvqkdx","pad":6,"bold":false,"text":"车型提取2","type":"textExtractionNodeState","dirty":false,"index":14,"width":360,"height":334,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"extractParam"},{"name":"memoryConfig"},{"name":"memorySwitch"},{"name":"histories"}],"return":{"type":"object"},"uniqueName":"3bca6a3f-9623-4228-b120-1a5e0d41dc14"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"stageDesc":"分析试驾车型中...","inputParams":[{"id":"extractParam_db169c41-8428-4d04-9ca9-d5462638b9de","from":"Expand","name":"extractParam","type":"Object","value":[{"id":"text_ed4e777e-3973-4dfe-a5cd-8b6997e3c16f","from":"Reference","name":"text","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"},{"id":"desc_c5827ce1-b1c7-4959-98a6-79736761bfd7","from":"Input","name":"desc","type":"String","value":"车型必须在该范围内:问界新M5 增程 Max,问界新M5 增程 Max RS,问界新M5 纯电 Max,问界M7 Ultra 五座后驱版,问界M7 Ultra 五座四驱版,问界M7 Ultra 六座后驱版,问界M7 Ultra 六座四驱版,问界M7 Pro 五座后驱版,问界M7 Pro 五座四驱版,问界M7 Pro 六座后驱版,问界M7 Pro 六座四驱版,问界M9 增程 Max 六座版,问界M9 增程 Ultra 六座版,问界M9 纯电 Max 六座版,问界M9 纯电 Ultra 六座版,问界M9 增程 Max 五座版,问界M9 增程 Ultra 五座版,问界M9 纯电 Ultra 五座版 "},{"id":"outputSchema_2dda5dc9-c4f7-4f4e-a3ac-60a5387d66c9","from":"Input","name":"outputSchema","type":"String","value":"{\"type\":\"object\",\"properties\":{\"carTypes\":{\"type\":\"array\",\"description\":\"问界的车型列表,具体到型号\"}}}"},{"id":"6958ac49-541d-4768-b198-dfdf3767649a","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"fb995664-4eb1-49e2-80ba-b6d2991eedd5","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"755800ba-5209-4a3c-90b0-b4aa4bfd8fe2","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"temperature_6bb0ad3b-7bbf-4110-acbd-f4b103214374","from":"Input","name":"temperature","type":"Number","value":"0.3"}]},{"id":"memoryConfig_392e3f83-1fe1-43b5-88ae-afb9999ee4ee","from":"Expand","name":"memoryConfig","type":"Object","value":[{"id":"windowAlg_cde542de-39f0-4550-91dd-b1cf979f310d","from":"Input","name":"windowAlg","type":"String","value":"buffer_window"},{"id":"serializeAlg_153d40da-349c-466e-8780-6eefc42ae5c6","from":"Input","name":"serializeAlg","type":"String","value":"full"},{"id":"property_fb24ab56-f614-412b-b6d6-dfbbbc189365","from":"Input","name":"property","type":"Integer","value":"0"}]},{"id":"memorySwitch_6b354c48-ed57-44b3-91a4-5940425dc1dc","from":"Input","name":"memorySwitch","type":"Boolean","value":false},{"id":"histories_d7e7c55e-d25b-4e03-80f1-b9937979faf1","from":"Reference","name":"histories","type":"Array","value":["memories"],"referenceId":"memories","referenceKey":"memories","referenceNode":"_systemEnv"}],"outputParams":[{"id":"b65edebe-1855-4502-a72f-0a207fd6c99f","from":"Expand","name":"output","type":"Object","value":[{"id":"66248c5c-9f8f-4000-8597-e8d6b0d7b16d","from":"Expand","name":"extractedParams","type":"Object","value":[{"id":"eb21be95-d82b-49b3-9eaf-455f4e449410","from":"Input","name":"carTypes","type":"Array","value":"","description":"问界的车型列表,具体到型号"}]},{"id":"success_e1a7af2a-fde0-4e73-81c1-cced296e08e8","from":"Input","name":"success","type":"Boolean","value":"Boolean"}]}],"enableStageDesc":true,"jadeNodeConfigChangeIgnored":false}}},"stageDesc":"分析试驾车型中...","joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","enableStageDesc":true},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"textExtractionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1912.2742953652482,"y":796.1082161366603,"id":"jadez5f1df","pad":6,"bold":false,"text":"聚合车型列表","type":"codeNodeState","dirty":false,"index":15,"width":368,"height":252,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"args"},{"name":"code"},{"name":"language"},{"name":"output"}],"return":{"type":"object"},"uniqueName":"e147f301-957a-4335-a155-1e86d1a45ae5"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ee12101b-d717-4f49-9ff5-6d840676047a","from":"Expand","name":"args","type":"Object","value":[{"id":"b8d2fbbf-1152-4403-a6dc-8ae88d7ddec3","from":"Reference","name":"input1","type":"Object","value":["output"],"referenceId":"30bf32bc-b320-418e-ae62-b30d45b26255","referenceKey":"output","referenceNode":"jademzanbn"},{"id":"662c4a18-21e8-47e0-bce2-10c89cb51b63","from":"Reference","name":"input2","type":"Object","value":["output"],"referenceId":"b65edebe-1855-4502-a72f-0a207fd6c99f","referenceKey":"output","referenceNode":"jadetvqkdx"}]},{"id":"08e9c25a-e55c-4620-bcca-325d714160d5","from":"Input","name":"code","type":"String","value":"async def main(args: Args) -> Output:\n if args[''input1'']:\n return args[''input1'']\n else:\n return args[''input2''] \n","language":"python"},{"id":"7df2184c-f82f-43ab-9a30-d695fae550ea","from":"Input","name":"language","type":"String","value":"python"},{"id":"35868ea7-d973-4a6c-b9f2-340a06f9f112","from":"Input","name":"output","type":"Object","value":{"properties":{"output":{"type":"object","description":"","properties":{}}}}}],"outputParams":[{"id":"7f1994bf-3a0b-442a-9bbb-d0069949fbbd","from":"Expand","name":"output","type":"Object","value":[]}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"codeComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":1684.8563982124583,"y":200.98809523809518,"id":"jadeanptlq","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":16,"textX":0,"textY":0,"width":227.41789715278992,"hAlign":"center","height":721.1201208985651,"italic":false,"margin":20,"toShape":"jadez5f1df","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jademzanbn","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1787.020111275216,"y":1199.5974446613131,"id":"jade58w7a7","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":17,"textX":0,"textY":0,"width":125.25418409003214,"hAlign":"center","height":-277.48922852465284,"italic":false,"margin":20,"toShape":"jadez5f1df","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadetvqkdx","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2280.2742953652482,"y":922.1082161366603,"id":"jadexs8po6","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":18,"textX":0,"textY":0,"width":-341.29792459921237,"hAlign":"center","height":-813.0635044184537,"italic":false,"margin":20,"toShape":"jade0ibwv7","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadez5f1df","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2383.9637395101945,"y":939.1301739903586,"id":"jadebz0q8j","pad":6,"bold":false,"text":"提取车型列表","type":"codeNodeState","dirty":false,"index":19,"width":368,"height":252,"italic":false,"flowMeta":{"jober":{"name":"","type":"STORE_JOBER","entity":{"params":[{"name":"args"},{"name":"code"},{"name":"language"},{"name":"output"}],"return":{"type":"object"},"uniqueName":"e147f301-957a-4335-a155-1e86d1a45ae5"},"fitables":[],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"ac8da121-58a3-4801-82f3-862533c0db2b","from":"Expand","name":"args","type":"Object","value":[{"id":"90d97368-2212-4176-ab52-55215cb5d6c6","from":"Reference","name":"input","type":"Object","value":["output"],"referenceId":"7f1994bf-3a0b-442a-9bbb-d0069949fbbd","referenceKey":"output","referenceNode":"jadez5f1df"}]},{"id":"5abd8220-a2e6-47a1-b3d7-61da5c91a7c8","from":"Input","name":"code","type":"String","value":"async def main(args: Args) -> Output:\n return args.get(''input'', {}).get(''extractedParams'', {}).get(''carTypes'', [])","language":"python"},{"id":"d09bd08d-d2f7-47c2-a74b-a9f9fe899387","from":"Input","name":"language","type":"String","value":"python"},{"id":"c57b604c-0ae0-4d32-bef3-30c14793ddf1","from":"Input","name":"output","type":"Object","value":{"properties":{"output":{"type":"array","description":""}}}}],"outputParams":[{"id":"504a2241-6962-4de4-901a-3df2b798eaa2","from":"Expand","name":"output","type":"Array","value":""}]}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"codeComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":2918.657887350822,"y":344.64718910145405,"id":"jadep8j6qd","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":20,"textX":0,"textY":0,"width":-534.6941478406275,"hAlign":"center","height":720.4829848889045,"italic":false,"margin":20,"toShape":"jadebz0q8j","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeh0ccb7","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":2751.9637395101945,"y":1065.1301739903586,"id":"jader2ugw2","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":21,"textX":0,"textY":0,"width":343.5651002416116,"hAlign":"center","height":140.1342820208663,"italic":false,"margin":20,"toShape":"jadejnmnp4","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadebz0q8j","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":1585.717679196791,"y":1882.192672636369,"id":"jadeaevz6w","pad":6,"bold":false,"text":"结束_1","type":"endNodeEnd","dirty":false,"index":22,"width":360,"height":182,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"e42435d1-bd40-4a8f-a6da-6ce3b8c9e936","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"0cecce75-2752-4f0b-8c01-e55897f82eef","from":"Reference","name":"finalOutput","type":"String","value":["output","llmOutput"],"editable":true,"isRequired":true,"description":"","referenceId":"2e94082b-930d-4016-a180-a07bd0ad8e50","referenceKey":"llmOutput","referenceNode":"jade3oqd4e"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"f7b318fa-c166-4b2d-b72a-e6b90b8c7324","from":"Input","name":"enableLog","type":"Boolean","value":false}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":1116.4734101887923,"y":1634.429481687775,"id":"jadewhsta6","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":23,"textX":0,"textY":0,"width":469.2442690079988,"hAlign":"center","height":338.763190948594,"italic":false,"margin":20,"toShape":"jadeaevz6w","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade3oqd4e","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4941.814604932744,"y":1053.7568338252217,"id":"jadevgtszg","pad":6,"bold":false,"text":"结束_3","type":"endNodeEnd","dirty":false,"index":24,"width":360,"height":182,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"10392a5e-479e-468d-9a87-bb85a2649cc2","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"b2ab53ef-8e29-4a95-9fed-be9894df8806","from":"Input","name":"out","type":"String","value":"您选择的试驾车型为:","editable":true,"isRequired":true,"description":""},{"id":"31d34355-c6d3-459d-ac61-66eaf31f6fd4","from":"Reference","name":"car","type":"String","value":["output","car"],"editable":true,"isRequired":true,"description":"","referenceId":"610f8341-46f0-4e26-a6b8-58be5d52f560","referenceKey":"car","referenceNode":"jadez5nidp"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"89dfe7b5-972a-4b2a-a2b4-86c9b851fe05","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":4872.272758733045,"y":906.3278204357666,"id":"jade90v0rb","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":25,"textX":0,"textY":0,"width":69.54184619969965,"hAlign":"center","height":238.42901338945512,"italic":false,"margin":20,"toShape":"jadevgtszg","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadez5nidp","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4360.7236177911855,"y":276.14527960371345,"id":"jade9wr70l","pad":6,"bold":false,"text":"结束_2","type":"endNodeEnd","dirty":false,"index":26,"width":360,"height":182,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"bf589d2e-aff9-46f4-a266-4f3b6b4ea6e7","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"9104e96f-26ec-46cf-88b4-1ff929f7b983","from":"Input","name":"out","type":"String","value":"您选择的试驾车型为:","editable":true,"isRequired":true,"description":""},{"id":"7c278873-cea0-424b-b344-e22ab070aeb3","from":"Reference","name":"car","type":"String","value":["output","car"],"editable":true,"isRequired":true,"description":"","referenceId":"2c678112-4c6d-44b2-a1f6-3d4b8718b893","referenceKey":"car","referenceNode":"jadeq2zedq"}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"ab34cf0f-7fa9-424d-8b01-edc402fb163d","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3281.554641671145,"y":-72.84814124515464,"id":"jadehrizke","pad":6,"bold":false,"text":"结束","type":"endNodeEnd","dirty":false,"index":27,"width":360,"height":182,"italic":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.aipp.fitable.AippFlowEndCallback"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"8fca4472-cb61-474b-97c8-be14566e998a","from":"Expand","name":"finalOutput","type":"Object","value":[{"id":"1e9b929c-bd6d-46c1-9b8c-23d1a0f7a7cc","from":"Input","name":"out","type":"String","value":"非常抱歉,不支持该车型的试驾。","editable":true,"isRequired":true,"description":""}],"editable":false,"isRequired":false,"referenceId":"","referenceKey":"","referenceNode":""},{"id":"8fcd715b-5e4c-488c-bcb7-ec5f4b5c83c2","from":"Input","name":"enableLog","type":"Boolean","value":true}],"outputParams":[{}]}}},"triggerMode":"auto"},"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"deletable":true,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28,31,35,.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74,147,255,0.12)","outlineWidth":10,"completedTask":0,"componentName":"endComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderWidth":1,"mouseInBorderColor":"#B1B1B7"},{"x":2918.657887350822,"y":241.64718520187574,"id":"jadeke55eo","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":28,"textX":0,"textY":0,"width":362.896754320323,"hAlign":"center","height":-223.49532644703038,"italic":false,"margin":20,"toShape":"jadehrizke","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeh0ccb7","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0|23d6704f-527f-43d9-8621-d8577d71dfec"},{"x":-503.16041743745245,"y":128.7548542937538,"id":"jade1tc9vb","pad":6,"bold":false,"text":"用户问题分类","type":"llmNodeState","dirty":false,"index":29,"width":360,"height":344,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"8e3a79d1-d39d-4815-9236-2e08bcbeb89d","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"d9ef67b3-a4e8-4b6c-b15b-0f55de543159","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"22a75a6e-c13a-45e7-9492-15613f79b1d8","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"a8265476-b028-4b32-88a4-cd9e27609d86","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"2c6a7c43-a6d7-4850-a60f-43d615085630","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"80222643-ef96-41ca-b9f6-6eae7f267a69","from":"Expand","name":"prompt","type":"Object","value":[{"id":"d655ab25-92c4-4af2-a01a-3e7e22a84362","from":"Input","name":"template","type":"String","value":"\"请分析用户的问题,并判断其意图是否是与问界汽车或是问界试驾有关。\n 如果用户的问题涉及问界汽车信息的询问,(例如:“请介绍一下问界M5”、 “给我推荐一款问界汽车”),则返回 ‘问界汽车问题’。\n如果用户只是咨询试驾相关内容(例如:“我想试驾问界M5”、“我想试驾保时捷911”),则返回 ‘试驾汽车问题’。\n如果用户问题是关于**其他品牌**的汽车或是一般性闲聊(例如:“最近有什么有趣的事?”、“今天天气不错”)则返回 ‘其他问题’\"\n用户问题:{{input}}\n**重要:不要回答``问界汽车问题``、``试驾汽车问题``、``其他问题``以外的文字**"},{"id":"cfe7e088-9f69-4671-a44a-4cd0f553920e","from":"Expand","name":"variables","type":"Object","value":[{"id":"046e05a4-4aa1-4694-8427-634bd97596d0","from":"Reference","name":"input","type":"String","value":["Question"],"referenceId":"input_ae2ffd6e-2b9e-4e73-9d7f-0e661ec3dbdb","referenceKey":"Question","referenceNode":"jade6qm5eg"}]}]},{"id":"248bb3d3-abcb-4072-8b14-360428994714","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"3"},{"id":"89b4068b-9926-4429-a334-9b6daefc11d0","from":"Expand","name":"tools","type":"Array","value":[]},{"id":"267e88b8-1cdc-4312-a117-a96ea6c37514","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"3b9fafe7-20ac-4d59-be6c-1dd61b264e70","from":"Input","name":"enableLog","type":"Boolean","value":false},{"id":"ed3e6fda-3a66-405c-b5d4-100822755cd8","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"d4ae45a4-74cc-498c-9e6e-69a1694614fb","from":"Expand","name":"output","type":"Object","value":[{"id":"e8bd45b4-2216-4eda-af81-d008bb6012a9","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"2d7b57fb-c452-41d3-9fed-bed3289c64eb","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":-763.5461204767116,"y":297.25814386932325,"id":"jadeknglms","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":30,"textX":0,"textY":0,"width":260.38570303925917,"hAlign":"center","height":3.496710424430546,"italic":false,"margin":20,"toShape":"jade1tc9vb","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade6qm5eg","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":-66.83537340229776,"y":197.7460840672867,"id":"jade5pubt6","pad":6,"bold":false,"text":"条件_1","type":"conditionNodeCondition","dirty":false,"index":31,"width":600,"height":284,"italic":false,"flowMeta":{"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto","conditionParams":{"branches":[{"id":"407a4d42-fe33-427e-8553-9de19e9bb002","type":"if","runnable":true,"conditions":[{"id":"e6cc2561-bf7f-43e9-873e-675ef7fa1340","value":[{"id":"146ae59a-df8a-4181-9dbc-466e6b81cdb9","from":"Reference","name":"left","type":"String","value":["output","llmOutput"],"referenceId":"e8bd45b4-2216-4eda-af81-d008bb6012a9","referenceKey":"llmOutput","referenceNode":"jade1tc9vb"},{"id":"5a2bd11a-bbff-4e14-81a7-8f5d747d9e61","from":"Input","name":"right","type":"String","value":"问界汽车问题","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"7fe7b5fa-c661-4ba5-9f87-4e8070496dd0","type":"if","runnable":true,"conditions":[{"id":"fc38c7be-06ac-41eb-a896-e6bb0f9129d0","value":[{"id":"767ed731-7595-46a6-9448-dcac3635694a","from":"Reference","name":"left","type":"String","value":["output","llmOutput"],"referenceId":"e8bd45b4-2216-4eda-af81-d008bb6012a9","referenceKey":"llmOutput","referenceNode":"jade1tc9vb"},{"id":"1ce1f111-7dec-4fac-b6e5-fd02d5b78557","from":"Input","name":"right","type":"String","value":"试驾汽车问题","referenceId":"","referenceKey":"","referenceNode":""}],"condition":"equal"}],"conditionRelation":"and"},{"id":"dc589e0f-3202-46e4-87f4-e550d54bbbc5","type":"else","runnable":true,"conditions":[{"id":"8d21bec4-7de8-4b35-9561-2e4374e42780","value":[],"condition":"true"}],"conditionRelation":"and"}],"jadeNodeConfigChangeIgnored":true}},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"conditionComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":-143.16041743745245,"y":300.7548542937538,"id":"jadey81zoy","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":32,"textX":0,"textY":0,"width":76.32504403515469,"hAlign":"center","height":38.99122977353289,"italic":false,"margin":20,"toShape":"jade5pubt6","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade1tc9vb","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":521.5373576131606,"y":318.54609091093886,"id":"jade8w9r8o","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":33,"textX":0,"textY":0,"width":297.6055178316176,"hAlign":"center","height":-7.406532627248396,"italic":false,"margin":20,"toShape":"jadewdnjbq","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade5pubt6","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-0|407a4d42-fe33-427e-8553-9de19e9bb002"},{"x":521.5373576131606,"y":374.5460870922666,"id":"jadewp6fyj","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":34,"textX":0,"textY":0,"width":905.4827536620555,"hAlign":"center","height":825.0513575690466,"italic":false,"margin":20,"toShape":"jadetvqkdx","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade5pubt6","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-1|7fe7b5fa-c661-4ba5-9f87-4e8070496dd0"},{"x":521.5373576131606,"y":421.54607877806717,"id":"jadeu36msk","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":35,"textX":0,"textY":0,"width":234.93605257563172,"hAlign":"center","height":1212.883402909708,"italic":false,"margin":20,"toShape":"jade3oqd4e","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade5pubt6","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-999"},{"x":4052.389624614043,"y":653.9380776568371,"id":"jade9wm99z","pad":6,"bold":false,"text":"大模型_4","type":"llmNodeState","dirty":false,"index":36,"width":360,"height":344,"italic":false,"flowMeta":{"jober":{"name":"","type":"general_jober","isAsync":"true","fitables":["modelengine.fit.jober.aipp.fitable.LLMComponent"],"converter":{"type":"mapping_converter","entity":{"inputParams":[{"id":"09e984b4-261d-4db4-9a79-bfdcab112355","from":"Input","name":"model","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"f7469288-be18-4f27-8e0c-985b6444c3cf","from":"Expand","name":"accessInfo","type":"Object","value":[{"id":"57eece3d-c599-444e-8ad8-b76f7b2594ca","from":"Input","name":"serviceName","type":"String","value":"Qwen/Qwen2.5-72B-Instruct"},{"id":"96ab5533-d3d0-4018-a341-16667d0df21b","from":"Input","name":"tag","type":"String","value":"SiliconFlow,Jade"}]},{"id":"6ee7671a-c9e2-4ae7-9348-d0f4c548e025","from":"Input","name":"temperature","type":"Number","value":"0.3"},{"id":"4e9c98fc-6547-4fd0-a71e-e21b9cd52f41","from":"Expand","name":"prompt","type":"Object","value":[{"id":"e7cd99dd-0143-44a9-81ee-ccebcc8b6a2c","from":"Input","name":"template","type":"String","value":"通过工具查询车型的图片:{{query}} "},{"id":"56e29f98-ae8e-478c-b02e-75b8149f090a","from":"Expand","name":"variables","type":"Object","value":[{"id":"77353774-8079-4f34-8672-44df471f2c2a","from":"Reference","name":"query","type":"String","value":["output","car"],"referenceId":"input_1bb450c2-e4f9-471f-ac11-4760ed04461c","referenceKey":"car","referenceNode":"jadejnmnp4"}]}]},{"id":"6dd50ee8-6572-43d1-a7da-97c88138bf5d","from":"Input","name":"maxMemoryRounds","type":"Integer","value":"0"},{"id":"3d26bc7c-3757-4b90-b803-f919123bd1dc","from":"Expand","name":"tools","type":"Array","value":[{"id":"bf11ba32-9201-4618-8084-66c81e4a0608","from":"Input","name":"问界车型宣传图片","tags":["FIT"],"type":"String","value":"e754f978-d236-44fa-aa55-e8eb5d8f8269","version":"1.0.0"}]},{"id":"e0ff690b-0e1c-4086-a299-94ab3cbfe5e5","from":"Input","name":"systemPrompt","type":"String","value":""},{"id":"267a67d4-c64c-488d-8263-7977c26b1c23","from":"Input","name":"enableLog","type":"Boolean","value":true},{"id":"4cfaec42-099b-4ec4-ae23-a3f47d9db635","from":"Expand","name":"knowledgeBases","type":"Array","value":[]}],"outputParams":[{"id":"29377aa8-2eb4-4a99-99c5-19ef52f309a0","from":"Expand","name":"output","type":"Object","value":[{"id":"c7836226-62a7-47e6-b4fa-705ed2acffed","from":"Input","name":"llmOutput","type":"String","value":"","description":""},{"id":"a2263372-420d-404c-93c0-e4e0edd79f39","from":"Input","name":"reference","type":"Array","value":[],"description":""}]}],"tempReference":{}}}},"joberFilter":{"type":"MINIMUM_SIZE_FILTER","threshold":1},"triggerMode":"auto"},"hasError":false,"hideText":true,"moveable":true,"runnable":true,"backColor":"white","container":"elsa-page:tvp1s6","dashWidth":0,"namespace":"jadeFlow","autoHeight":true,"emphasized":false,"enableMask":false,"rotateAble":false,"borderColor":"rgba(28, 31, 35, 0.08)","borderWidth":1,"runningTask":0,"triggerMode":"auto","warningTask":0,"cornerRadius":8,"outlineColor":"rgba(74, 147, 255, 0.12)","outlineWidth":10,"completedTask":0,"componentName":"llmComponent","focusBackColor":"white","sourcePlatform":"official","enableAnimation":false,"focusBorderColor":"rgb(4, 123, 252)","focusBorderWidth":1,"mouseInBorderColor":"rgb(4, 123, 252)"},{"x":3943.4751136945915,"y":982.1291139444377,"id":"jadepdt7pq","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":37,"textX":0,"textY":0,"width":108.91451091945146,"hAlign":"center","height":-156.19103628760058,"italic":false,"margin":20,"toShape":"jade9wm99z","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadebywy1i","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4412.389624614043,"y":825.9380776568371,"id":"jade28pvut","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":38,"textX":0,"textY":0,"width":99.88313411900162,"hAlign":"center","height":80.38974277892953,"italic":false,"margin":20,"toShape":"jadez5nidp","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jade9wm99z","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":2918.657887350822,"y":297.6471616509573,"id":"jadeam5lo5","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":true,"index":39,"textX":0,"textY":0,"width":217.46385789847182,"hAlign":"center","height":113.61431667712702,"italic":false,"margin":20,"toShape":"jadee2orhp","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeh0ccb7","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"dynamic-1|6f8c44d7-ad5c-4004-95e0-e01c391be100"},{"x":3504.121745249294,"y":411.2614783280843,"id":"jade13kr3a","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":40,"textX":0,"textY":0,"width":137.02274773089266,"hAlign":"center","height":-33.67339859514516,"italic":false,"margin":20,"toShape":"jadeq2zedq","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadee2orhp","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"},{"x":4001.1444929801864,"y":377.58807973293915,"id":"jadef977tc","pad":0,"bold":false,"text":"","type":"jadeEvent","dirty":false,"index":41,"textX":0,"textY":0,"width":359.579124810999,"hAlign":"center","height":-10.442800129225702,"italic":false,"margin":20,"toShape":"jade9wr70l","endArrow":true,"hideText":true,"lineMode":{"type":"auto_curve"},"runnable":true,"allowLink":false,"backColor":"white","container":"elsa-page:tvp1s6","fromShape":"jadeq2zedq","lineWidth":2,"namespace":"elsa","beginArrow":false,"borderColor":"#B1B1B7","borderWidth":1,"curvePoint1":{"x":0,"y":0},"curvePoint2":{"x":0,"y":0},"brokenPoints":[],"endArrowSize":4,"arrowEndPoint":{"x":0,"y":0},"endArrowEmpty":false,"beginArrowSize":4,"arrowBeginPoint":{"x":0,"y":0},"beginArrowEmpty":false,"definedToConnector":"W","mouseInBorderColor":"#B1B1B7","allowSwitchLineMode":false,"definedFromConnector":"E"}]}],"enableText":false,"flowMeta":{"callback":{"name":"通知回调","type":"general_callback","fitables":["modelengine.fit.jober.fitable.FlowInfoCallback"]},"enableOutputScope":true,"exceptionFitables":["modelengine.fit.jober.aipp.fitable.AippFlowExceptionHandler","modelengine.fit.jober.fitable.FlowInfoException"]},"version":"1.0.0"}', 'Jade', '2025-04-19 09:15:55.131413', 'Jade', '2025-04-19 09:15:55.585765', NULL, 'f') ON CONFLICT (id, version) DO NOTHING; + +INSERT INTO "public"."task_new" ("id", "name", "version", "template_id", "tenant_id", "attributes", "created_by", "created_at", "updated_by", "updated_at", "is_deleted") VALUES ('e785d5f9abaa4c5581612a2448c08441', '问界试驾助手', '1.0.0', 'dfe319109bc84f6793645e3483c029ca', '31f20efc7e0848deab6a6bc10fc3021e', '{"app_id": "550177e8d0e34014a2d95988ef1c67c5", "version": "1.0.0", "aipp_type": "NORMAL", "meta_icon": "", "publish_at": "2025-04-19T09:15:56.022104797", "description": "", "meta_status": "active", "unique_name": "2a3141b2-8c83-40c2-b2b6-103746c125d3", "flow_config_id": "5711f3230eb94abdb168e61d2082d1d2", "flow_definition_id": "803b4ba4d163472b81ad2330734e23b9", "publish_update_log": "", "publish_description": "问界试驾助手"}', 'Jade', '2025-04-19 09:15:55.552466', 'Jade', '2025-04-19 09:15:56.035246', 0) ON CONFLICT (id) DO NOTHING; + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('问界试驾助手', '{"name":"问界试驾助手","description":"","manualIntervention":false,"parameters":{"type":"object","properties":{"aippId":{"description":"the aipp id of the waterFlow tool","default":"dfe319109bc84f6793645e3483c029ca","type":"string"},"tenantId":{"description":"the tenant id of the waterFlow tool","default":"31f20efc7e0848deab6a6bc10fc3021e","type":"string"},"inputParams":{"type":"object","properties":{"Question":{"type":"String","description":"这是用户输入的问题。"}},"required":["Question"],"order":["Question"]},"version":{"description":"the aipp version of the waterFlow tool","default":"1.0.0","type":"string"}},"required":["tenantId","aippId","version","inputParams"]},"return":{"type":"object","properties":{}},"order":["tenantId","aippId","version","inputParams"]}', '{"FIT":{"fitableId":"water.flow.invoke","genericableId":"07b51bd246594c159d403164369ce1db"},"APP":{"aippId":"dfe319109bc84f6793645e3483c029ca","appCategory":"chatbot","version":"1.0.0","appId":"550177e8d0e34014a2d95988ef1c67c5"}}', 'null', '2a3141b2-8c83-40c2-b2b6-103746c125d3', '1.0.0', 't', '2a3141b2-8c83-40c2-b2b6-103746c125d3', '2a3141b2-8c83-40c2-b2b6-103746c125d3', '2a3141b2-8c83-40c2-b2b6-103746c125d3') ON CONFLICT ("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_app" ("source", "icon", "app_category", "tool_name", "tool_unique_name") VALUES ('system', '', 'chatbot', '问界试驾助手', '2a3141b2-8c83-40c2-b2b6-103746c125d3') ON CONFLICT ("tool_unique_name") DO NOTHING; + +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('2a3141b2-8c83-40c2-b2b6-103746c125d3', '{"name":"2a3141b2-8c83-40c2-b2b6-103746c125d3","description":"","manualIntervention":false,"parameters":{"type":"object","properties":{"aippId":{"description":"the aipp id of the waterFlow tool","default":"dfe319109bc84f6793645e3483c029ca","type":"string"},"tenantId":{"description":"the tenant id of the waterFlow tool","default":"31f20efc7e0848deab6a6bc10fc3021e","type":"string"},"inputParams":{"type":"object","properties":{"Question":{"type":"String","description":"这是用户输入的问题。"}},"required":["Question"],"order":["Question"]},"version":{"description":"the aipp version of the waterFlow tool","default":"1.0.0","type":"string"}},"required":["tenantId","aippId","version","inputParams"]},"return":{"type":"object","properties":{}},"order":["tenantId","aippId","version","inputParams"]}', '2a3141b2-8c83-40c2-b2b6-103746c125d3') ON CONFLICT ("definition_group_name", "name") DO NOTHING; + +INSERT INTO "public"."store_definition_group" ("name", "summary", "description", "extensions") VALUES ('2a3141b2-8c83-40c2-b2b6-103746c125d3', 'no summary', 'no desc', 'null') ON CONFLICT ("name") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('2a3141b2-8c83-40c2-b2b6-103746c125d3', 'APP') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('2a3141b2-8c83-40c2-b2b6-103746c125d3', 'APP_TYPE_4DB152B24F94473AB683B1ACBFE3C865') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; + +INSERT INTO "public"."store_tool_group" ("name", "definition_group_name", "summary", "description", "extensions") VALUES ('2a3141b2-8c83-40c2-b2b6-103746c125d3', '2a3141b2-8c83-40c2-b2b6-103746c125d3', 'no summary', 'no desc', 'null') ON CONFLICT ("name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('问界产品信息查询', '{"name":"问界产品信息查询","description":"用于查询问界产品信息查询","parameters":{"type":"object","properties":{"args":{"defaultValue":"","description":"预留参数,当前传入空字符串即可","name":"args","type":"string","examples":"","required":true}},"required":["args"]},"order":["args"],"parameterExtensions":null,"return":{"type":"string","convertor":""}}', 'Wenjie') ON CONFLICT ("definition_group_name", "name") DO NOTHING; +INSERT INTO "public"."store_definition" ("name", "schema", "definition_group_name") VALUES ('问界产品宣传图', '{"name":"问界产品宣传图","description":"用于查询问界产品宣传图","parameters":{"type":"object","properties":{"carType":{"defaultValue":"","description":"车的型号","name":"carType","type":"string","examples":"","required":true}},"required":["carType"]},"order":["carType"],"parameterExtensions":null,"return":{"type":"array","items":{"type":"object"},"convertor":""}}', 'Wenjie') ON CONFLICT ("definition_group_name", "name") DO NOTHING; + +INSERT INTO "public"."store_definition_group" ("name", "summary", "description", "extensions") VALUES ('Wenjie', '', '', '{}') ON CONFLICT ("name") DO NOTHING; + +INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "deploy_status", "is_builtin", "source", "icon") VALUES ('90468c455f728a6082d1b1546547748f3afe55e99d27b5e44acc9bd3935e6f13', 'wenjie-cars', '{"uniqueness.groupId":"modelengine.fit.jade.plugin","pluginFullName":"wenjie-data-1.0.0-SNAPSHOT_1745053576903.jar","checksum":"8313760d85dcd3382bf5a2da18251ecd0e1d272626d0054e6df17d37b916c4bb","name":"wenjie-cars","description":"问界的车型介绍","uniqueness.artifactId":"wenjie-data","type":"java"}', 'DEPLOYED', 'f', '', NULL) ON CONFLICT ("plugin_id") DO NOTHING; + +INSERT INTO "public"."store_plugin_tool" ("tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES ('问界车型信息查询', '90468c455f728a6082d1b1546547748f3afe55e99d27b5e44acc9bd3935e6f13', '7cba6fdd-8c6d-410f-81dc-775ffe96902b', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; +INSERT INTO "public"."store_plugin_tool" ("tool_name", "plugin_id", "tool_unique_name", "source", "icon") VALUES ('问界车型宣传图片', '90468c455f728a6082d1b1546547748f3afe55e99d27b5e44acc9bd3935e6f13', 'e754f978-d236-44fa-aa55-e8eb5d8f8269', '', NULL) ON CONFLICT ("plugin_id", "tool_unique_name") DO NOTHING; + +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('e754f978-d236-44fa-aa55-e8eb5d8f8269', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; +INSERT INTO "public"."store_tag" ("tool_unique_name", "name") VALUES ('7cba6fdd-8c6d-410f-81dc-775ffe96902b', 'FIT') ON CONFLICT ("tool_unique_name", "name") DO NOTHING; + +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('问界车型信息查询', '{"name":"问界车型信息查询","description":"用于查询问界的车型信息","parameters":{"type":"object","properties":{"args":{"examples":"","defaultValue":"","name":"args","description":"预留参数,当前传入空字符串即可","type":"string","required":false}},"required":["args"]},"return":{"convertor":"","examples":"","name":"","description":"问界车型介绍","type":"string"},"order":["args"]}', '{"FIT":{"genericableId":"modelengine.jober.aipp.AITO.describe","fitableId":"default"}}', '{"tags":["FIT"]}', '7cba6fdd-8c6d-410f-81dc-775ffe96902b', '1.0.0', 't', 'WenjieImpl', '问界产品信息查询', 'Wenjie') ON CONFLICT ("unique_name", "version") DO NOTHING; +INSERT INTO "public"."store_tool" ("name", "schema", "runnables", "extensions", "unique_name", "version", "is_latest", "group_name", "definition_name", "definition_group_name") VALUES ('问界车型宣传图片', '{"name":"问界车型宣传图片","description":"用于查询问界车型宣传图片","parameters":{"type":"object","properties":{"carType":{"examples":"","defaultValue":"","name":"carType","description":"车的型号","type":"string","required":false}},"required":["carType"]},"return":{"convertor":"","examples":"","name":"","description":"问界车型的宣传图片的访问地址","type":"array","items":{"type":"object"}},"order":["carType"]}', '{"FIT":{"genericableId":"modelengine.jober.aipp.AITO.url","fitableId":"default"}}', '{"tags":["FIT"]}', 'e754f978-d236-44fa-aa55-e8eb5d8f8269', '1.0.0', 't', 'WenjieImpl', '问界产品宣传图', 'Wenjie') ON CONFLICT ("unique_name", "version") DO NOTHING; + +INSERT INTO "public"."store_tool_group" ("name", "definition_group_name", "summary", "description", "extensions") VALUES ('WenjieImpl', 'Wenjie', '', '', '{}') ON CONFLICT ("name") DO NOTHING; diff --git a/app-builder/plugins/tool-waterflow/pom.xml b/app-builder/plugins/tool-waterflow/pom.xml index d7f20bc3c9..ef474f3dbe 100644 --- a/app-builder/plugins/tool-waterflow/pom.xml +++ b/app-builder/plugins/tool-waterflow/pom.xml @@ -28,11 +28,6 @@ - - org.fitframework.fel - tool-service - 3.5.0-M2.1 - modelengine.fit.jade.service store-service diff --git a/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProvider.java b/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProvider.java index 3b91b88590..8211ea7d73 100644 --- a/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProvider.java +++ b/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProvider.java @@ -12,7 +12,6 @@ import modelengine.fel.core.tool.ToolInfo; import modelengine.fel.core.tool.ToolProvider; import modelengine.fel.tool.model.transfer.ToolData; -import modelengine.fel.tool.service.ToolService; import modelengine.fit.jade.tool.SyncToolCall; import modelengine.fit.jober.aipp.constants.AippConst; import modelengine.fitframework.annotation.Component; @@ -23,6 +22,7 @@ import modelengine.fitframework.util.StringUtils; import modelengine.fitframework.util.UuidUtils; import modelengine.jade.carver.tool.waterflow.invoker.ToolInvoker; +import modelengine.jade.store.service.ToolService; import java.util.List; import java.util.Map; diff --git a/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/invoker/ToolInvokerDecorator.java b/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/invoker/ToolInvokerDecorator.java index 7ebde7b245..a32b8d7507 100644 --- a/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/invoker/ToolInvokerDecorator.java +++ b/app-builder/plugins/tool-waterflow/src/main/java/modelengine/jade/carver/tool/waterflow/invoker/ToolInvokerDecorator.java @@ -6,8 +6,8 @@ package modelengine.jade.carver.tool.waterflow.invoker; -import modelengine.fel.core.tool.ToolCall; import modelengine.fel.tool.model.transfer.ToolData; +import modelengine.fel.core.tool.ToolCall; import java.util.Map; diff --git a/app-builder/plugins/tool-waterflow/src/test/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProviderTest.java b/app-builder/plugins/tool-waterflow/src/test/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProviderTest.java index d6c469eb71..7b046036ef 100644 --- a/app-builder/plugins/tool-waterflow/src/test/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProviderTest.java +++ b/app-builder/plugins/tool-waterflow/src/test/java/modelengine/jade/carver/tool/waterflow/WaterFlowToolProviderTest.java @@ -11,9 +11,9 @@ import modelengine.fel.core.tool.ToolInfo; import modelengine.fel.tool.Tool; import modelengine.fel.tool.model.transfer.ToolData; -import modelengine.fel.tool.service.ToolService; import modelengine.fitframework.util.MapBuilder; import modelengine.jade.carver.tool.waterflow.invoker.ToolInvoker; +import modelengine.jade.store.service.ToolService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; diff --git a/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml b/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml index e2a62fd7f9..6a861ad0e9 100644 --- a/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml +++ b/app-builder/waterflow/java/waterflow-service/src/main/resources/application.yml @@ -16,6 +16,6 @@ jane: scheduleRate: 10000 maxCount: 0 isNeedFlowCallbackAdapt: false - contextExpiredDays: 7 + contextExpiredDays: 1 distributed-lock-provider: databaseDistributedLockProvider \ No newline at end of file diff --git a/app-engine/frontend/.gitignore b/app-engine/frontend/.gitignore index 2a170a8d36..eaa23fd74f 100644 --- a/app-engine/frontend/.gitignore +++ b/app-engine/frontend/.gitignore @@ -1,3 +1,8 @@ package-lock.json node_modules/ build/ +packages/ +plugins/* +!plugins/manifest.json +!plugins/plugin.sh +!plugins/plugin.js \ No newline at end of file diff --git a/app-engine/frontend/package.json b/app-engine/frontend/package.json index 3d9c1682f5..f216cbbe4f 100644 --- a/app-engine/frontend/package.json +++ b/app-engine/frontend/package.json @@ -6,8 +6,11 @@ "scripts": { "start": "webpack serve --progress --profile --mode development --host localhost --port 3310 --config webpack.dev.js", "start:single": "webpack serve --config webpack.dev.single.js", - "build:prod": "webpack --mode production --config webpack.prod.js", - "build:single": "webpack --mode production --config webpack.prod.single.js" + "start:plugin": "bash ./plugins/plugin.sh && npm run start", + "build:prod": "npm run build:plugin:prod && webpack --mode production --config webpack.prod.js", + "build:single": "npm run build:plugin:single && webpack --mode production --config webpack.prod.single.js", + "build:plugin:prod": "bash ./plugins/plugin.sh prod", + "build:plugin:single": "bash ./plugins/plugin.sh prod spa" }, "license": "ISC", "devDependencies": { diff --git a/app-engine/frontend/plugins/manifest.json b/app-engine/frontend/plugins/manifest.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/app-engine/frontend/plugins/manifest.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/app-engine/frontend/plugins/plugin.js b/app-engine/frontend/plugins/plugin.js new file mode 100644 index 0000000000..45a17a8bd2 --- /dev/null +++ b/app-engine/frontend/plugins/plugin.js @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * This file is a part of the ModelEngine Project. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const fs = require('fs'); + +function ensure(fileName) { + if (!fs.existsSync(fileName)) { + fs.writeFileSync(fileName, '[]', { encoding: 'utf8' }); + } +} + +function read(fileName) { + return fs.readFileSync(fileName, 'utf8'); +} + +function write(fileName, content, appendContent = '') { + if (!appendContent) return; + try { + const array = JSON.parse(content || '[]'); + const appendItem = JSON.parse(appendContent); + + // 不存在时新增,存在时更新 + const found = array.find(item => item.name === appendItem.name); + if (!found) { + array.push(appendItem) + } else { + Object.assign(found, appendItem); + } + + fs.writeFileSync(fileName, JSON.stringify(array, null, 2), { encoding: 'utf8' }); + } catch (error) { + console.error(error); + } +} + +function main() { + const args = process.argv.slice(2); + const content = args[0] || ''; + const fileName = args[1] || './manifest.json'; + + ensure(fileName); + write(fileName, read(fileName), content); +} + +main(); \ No newline at end of file diff --git a/app-engine/frontend/plugins/plugin.sh b/app-engine/frontend/plugins/plugin.sh new file mode 100644 index 0000000000..0481cae64e --- /dev/null +++ b/app-engine/frontend/plugins/plugin.sh @@ -0,0 +1,97 @@ +#!/bin/bash +#set -x + +ENV=$1 +MODE=$2 +PLUGIN_NAME=$3 +CODE_URL=$4 +CODE_BRANCH=$5 + +PLUGIN_URL="" +PLUGIN_ICON="" +PLUGIN_DEV_URL="http://localhost:3099/#/chat?sidebar=0" +if [ "$MODE" = "spa" ]; then + PLUGIN_PROD_URL="/apps/appengine/plugins/$PLUGIN_NAME/index.html#/chat?sidebar=0" +else + PLUGIN_PROD_URL="/plugins/$PLUGIN_NAME/index.html#/chat?sidebar=0" +fi +WORKSPACE=packages + +cd_workspace() { + if [ ! -d $WORKSPACE ]; then + mkdir $WORKSPACE + fi + cd ./$WORKSPACE + pwd +} + +# 下载插件代码,或者更新代码 +clone_repo() { +# if [ -d $PLUGIN_NAME ]; then +# cd $PLUGIN_NAME +# git pull +# cd .. +# else +# git clone $CODE_URL --branch $CODE_BRANCH $PLUGIN_NAME +# cd $PLUGIN_NAME +# fi + cd $PLUGIN_NAME +} + +# 安装插件依赖 +install_dep() { + npm cache clean -f + npm install --legacy-peer-deps --registry=https://registry.npmmirror.com +} + +# 本地运行 +run_start() { + PLUGIN_URL=$PLUGIN_DEV_URL + + if [[ $MODE = "spa" ]]; then + start cmd /k "npm run start:spa" + else + start cmd /k "npm run start" + fi + + PLUGIN_ICON="" +} + +# 生产构建 +run_build() { + PLUGIN_URL=$PLUGIN_PROD_URL + + if [[ $MODE = "spa" ]]; then + npm run build:single + else + npm run build + fi + + PLUGIN_ICON="/apps/appengine/plugins/$PLUGIN_NAME/icon.jpg" +} + +# 安装插件 +install_plugin() { + rm -rf ../../plugins/$PLUGIN_NAME + cp -r ./dist ../../plugins/$PLUGIN_NAME +} + +# 更新插件数据 +update_plugin_meta() { + node ../../plugins/plugin.js "{\"name\":\"$PLUGIN_NAME\",\"icon\":\"$PLUGIN_ICON\",\"url\":\"$PLUGIN_URL\"}" ../../plugins/manifest.json +} + +echo "Start download and build plugin pathobot with mode:$MODE" + +cd_workspace +clone_repo +install_dep + +if [[ $ENV == "prod" ]]; then + run_build + install_plugin +else + run_start +fi + +update_plugin_meta \ No newline at end of file diff --git a/app-engine/frontend/proxy.conf.json b/app-engine/frontend/proxy.conf.json index d34e0f3578..80c4441626 100644 --- a/app-engine/frontend/proxy.conf.json +++ b/app-engine/frontend/proxy.conf.json @@ -4,7 +4,7 @@ "secure": false, "changeOrigin": true, "pathRewrite": { - "^/api/jober": "/api/jober", + "^/api/jober": "", "^/api": "" } }, @@ -18,14 +18,14 @@ }, "/aippApi": { "target": "http://{ip}:{port}", - "pathRewrite": { "^/aippApi": "/api/jober/v1/api" }, + "pathRewrite": { "^/aippApi": "/v1/api" }, "secure": false, "changeOrigin": true }, "/modelApi": { "target": "http://{ip}:{port}", "pathRewrite": { - "^/modelApi": "/api" + "^/modelApi": "/" }, "secure": false, "changeOrigin": true diff --git a/app-engine/frontend/src/components/layout/index.tsx b/app-engine/frontend/src/components/layout/index.tsx index c6de93bb5a..3e9f00030a 100644 --- a/app-engine/frontend/src/components/layout/index.tsx +++ b/app-engine/frontend/src/components/layout/index.tsx @@ -25,7 +25,7 @@ import { Provider } from 'react-redux'; import { Icons, KnowledgeIcons } from '../icons/index'; import store from '@/store/store'; import { setSpaClassName } from '@/shared/utils/common'; -import { getUser, getOmsUser, getRole } from '../../pages/helper'; +import { getUser, getOmsUser, getRole, getChatPluginList } from '../../pages/helper'; import './style.scoped.scss'; const { Content, Sider } = Layout; @@ -113,6 +113,7 @@ const AppLayout: React.FC = () => { getOmsUser(); getRole(); } + getChatPluginList(); }, []) return ( diff --git a/app-engine/frontend/src/components/timeLine/index.tsx b/app-engine/frontend/src/components/timeLine/index.tsx index 1360a41811..ab64b9587c 100644 --- a/app-engine/frontend/src/components/timeLine/index.tsx +++ b/app-engine/frontend/src/components/timeLine/index.tsx @@ -4,69 +4,229 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import React, { useEffect, useState } from 'react'; -import { Drawer, Timeline, Empty } from 'antd'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +import { Button, Drawer, Tag, Timeline } from 'antd'; import { useParams } from 'react-router-dom'; import { CloseOutlined } from '@ant-design/icons'; -import { getVersion } from '@/shared/http/aipp'; -import { useTranslation } from "react-i18next"; -import tagImg from '@/assets/images/ai/tag.png'; +import { Message } from '@/shared/utils/message'; +import { getAppInfo, getVersion, resetApp } from '@/shared/http/aipp'; +import { useTranslation } from 'react-i18next'; +import { useAppDispatch } from '@/store/hook'; +import { setIsReadOnly } from '@/store/common/common'; +import { RenderContext } from '@/pages/aippIndex/context'; + +const PAGE_SIZE = 10; const TimeLineFc = (props) => { - const { open, setOpen, type = '' } = props; + const { open, setOpen, type = '', updateAippCallBack } = props; const [timeList, setTimeList] = useState([]); const { tenantId, appId } = useParams(); const { t } = useTranslation(); + const [selectedAppId, setSelectedAppId] = useState(appId); + const dispatch = useAppDispatch(); + const [page, setPage] = useState(1); + const [loading, setLoading] = useState(false); + const scrollRef = useRef(); + const hasMoreRef = useRef(true); + const currentAppInfo = useRef(null); + const { renderRef, elsaReadOnlyRef } = useContext(RenderContext); - useEffect(() => { - open && getVersion(tenantId, appId, type).then(res => { + const fetchData = async (currentPage: number) => { + if (!open || loading || !hasMoreRef.current) return; + setLoading(true); + try { + const res = await getVersion(tenantId, appId, type, PAGE_SIZE * (currentPage - 1), PAGE_SIZE); if (res.code === 0) { - setTimeList(res.data); + const newItems = res.data.results || []; + setTimeList((prev) => { + const newIds = new Set(prev.map((item) => item.id)); + const filteredNewItems = newItems.filter((item) => !newIds.has(item.id)); + return [...prev, ...filteredNewItems]; + }); + if (newItems.length < PAGE_SIZE) { + hasMoreRef.current = false; + } else { + setPage(currentPage + 1); + } } - }) + } finally { + setLoading(false); + } + }; + + const getCurrentApp = async (tenantId: string, appId: string) => { + const res: any = await getAppInfo(tenantId, appId); + if (res.code === 0) { + currentAppInfo.current = res.data; + } + }; + + useEffect(() => { + if (open) { + dispatch(setIsReadOnly(true)); + setTimeList([]); + setPage(1); + hasMoreRef.current = true; + window.agent?.readOnly(); + + Promise.all([ + getCurrentApp(tenantId, appId), // 刷新当前应用数据 + fetchData(1) // 加载历史版本 + ]).catch(console.error); + } }, [open]); - const descProcess = (str) => { - if (!str || str === 'null') { - return ''; + + useEffect(() => { + const handleScroll = () => { + const container = scrollRef.current; + if (!container) return; + const { scrollTop, scrollHeight, clientHeight } = container; + if (scrollTop + clientHeight >= scrollHeight - 50) { + fetchData(page); + } + }; + + const container = scrollRef.current; + container?.addEventListener('scroll', handleScroll); + return () => container?.removeEventListener('scroll', handleScroll); + }, [page, open, hasMoreRef.current]); + + + const descProcess = (str) => (!str || str === 'null' ? '' : str); + + const handleRecover = () => { + if (appId !== selectedAppId) { + resetApp(tenantId, appId, selectedAppId, { + 'Content-Type': 'application/json' + }).then(res => { + if (res.code === 0) { + Message({ type: 'success', content: t('resetSucceed') }); + currentAppInfo.current = res.data; + handleClose(); + } + }); } - return str; + }; + + const handleItemClick = (timeItem) => { + setSelectedAppId(timeItem.id); + updateAippCallBack( + { + flowGraph: timeItem.flowGraph, + configFormProperties: timeItem.configFormProperties + } + ); + renderRef.current = false; + elsaReadOnlyRef.current = true; + }; + + const handleClose = () => { + dispatch(setIsReadOnly(false)); + setSelectedAppId(appId); + setTimeList([]); + setPage(1); + hasMoreRef.current = true; + updateAippCallBack(currentAppInfo.current); + renderRef.current = false; + elsaReadOnlyRef.current = false; + setOpen(false); } - return <> + + useEffect(() => { + return () => { + // 组件卸载时自动重置 + dispatch(setIsReadOnly(false)); + }; + }, [dispatch]); + + return ( setOpen(false)} + onClose={handleClose} open={open} - footer={null} - extra={ - setOpen(false)} /> - }> -
-
- - {t('cannotRevertVersion')} + mask={false} + footer={ +
+ +
- {timeList.length > 0 ? + } + extra={} + > +
+ {timeList.length > 0 ? ( - { timeList.map(timeItem => ( - -
-
{timeItem.appVersion}
-
{descProcess(timeItem.publishedDescription)}
-
{timeItem.publishedBy}
-
{timeItem.publishedAt}
-
-
- )) } -
: -
- } + +
handleItemClick(currentAppInfo.current)} + > +
{t('currentDraft')}
+
+
+ {timeList.map((timeItem, index) => { + const isSelected = timeItem.id === selectedAppId; + const isLatest = index === 0; + return ( + +
handleItemClick(timeItem)} + > +
+ {timeItem.version} + {isLatest && + + {t('latest')} + + } +
+
{descProcess(timeItem.publishedDescription)}
+
{timeItem.updateBy}
+
{timeItem.updateAt}
+
+
+ ); + })} + {loading &&
{t('loading')}...
} + {!hasMoreRef.current &&
{t('noMore')}
} + + ) : null}
- + ); }; - export default TimeLineFc; diff --git a/app-engine/frontend/src/locale/en_US.json b/app-engine/frontend/src/locale/en_US.json index d2f2f5ef57..25f36390d5 100644 --- a/app-engine/frontend/src/locale/en_US.json +++ b/app-engine/frontend/src/locale/en_US.json @@ -223,7 +223,7 @@ "gotIt": "Got It", "successReleased": "Application released", "versionTip": "Invalid version format", - "releaseTip": "The version to be released will overwrite the historical version and cannot be rolled back", + "releaseTip": "The version to be released will overwrite the historical version", "releaseApplication": "Release Application", "releaseTip2": "Release the application only after it passes the debugging", "versionName": "Version", @@ -233,6 +233,8 @@ "testTip2": "Debug the workflow to ensure it runs properly before release.", "debug": "Debug", "plsEnterRequiredItem": "Set mandatory parameters", + "expandConfig": "Expand config", + "collapseConfig": "Collapse config", "flowChangeWarningContent": "The workflow configuration items have been modified. Debug the workflow again. The workflow can be released once it passes the debugging", "noMoreTips": "Do not remind me again", "plsEnterString": "Enter a string", @@ -951,5 +953,13 @@ "reorganizeNodes": "Organize nodes", "zoomOut": "Zoom Out", "zoomIn": "Zoom In", - "formItemFieldTypeCannotBeEmpty": "Field type is required" + "formItemFieldTypeCannotBeEmpty": "Field type is required", + "addParallelTask": "Add Parallel Tasks", + "recover": "recover", + "exit": "exit", + "resetSucceed": "Reset Succeed", + "currentDraft": "Current Draft", + "loading": "Loading", + "noMore": "You've reached the end", + "latest": "latest" } diff --git a/app-engine/frontend/src/locale/i18n.ts b/app-engine/frontend/src/locale/i18n.ts index e3599aa0e9..c8bb2f0047 100644 --- a/app-engine/frontend/src/locale/i18n.ts +++ b/app-engine/frontend/src/locale/i18n.ts @@ -7,15 +7,27 @@ import i18n from 'i18next'; import { initReactI18next } from "react-i18next"; import en from './en_US.json'; import zh from './zh_CN.json'; +import coreEn from '@fit-elsa/elsa-core/locales/en.json'; +import coreZh from '@fit-elsa/elsa-core/locales/zh.json'; +import reactEn from '@fit-elsa/elsa-react/locales/en.json'; +import reactZh from '@fit-elsa/elsa-react/locales/zh.json'; + +const mergeTranslations = (...translationObjects) => { + // 从右向左合并(右侧优先级更高) + return translationObjects + .filter(obj => obj) // 过滤掉假值(undefined/null + .reduce((merged, current) => ({ ...merged, ...current }), {}); +}; const resources = { en: { - translation: en + translation: mergeTranslations(coreEn, reactEn, en) // 优先级顺序: en > reactEn > coreEn }, zh: { - translation: zh + translation: mergeTranslations(coreZh, reactZh, zh) // 优先级顺序: zh > reactZh > coreZh } }; + const getCookie = (cname) => { const name = `${cname}=`; const ca = document.cookie.split(';'); diff --git a/app-engine/frontend/src/locale/zh_CN.json b/app-engine/frontend/src/locale/zh_CN.json index ad18b80221..505435b206 100644 --- a/app-engine/frontend/src/locale/zh_CN.json +++ b/app-engine/frontend/src/locale/zh_CN.json @@ -203,6 +203,8 @@ "knowledgeCreated": "恭喜您,知识库已完成创建", "enterNameRule": "输入字符长度范围:1 - 64", "expandArrange": "展开编排区", + "expandConfig": "展开配置区", + "collapseConfig": "收起配置区", "flowChangeWarningContent": "工作流配置项已变更,请重新调试,调试成功后即可发布", "debugAlert": "工具流暂不支持调试历史记录", "consumer": "自定义", @@ -306,7 +308,7 @@ "successReleased": "发布应用成功", "successReleased2": "发布工具流成功", "releaseApplication": "发布应用", - "releaseTip": "新版本将覆盖历史版本,并不可回退", + "releaseTip": "新版本将覆盖历史版本", "releaseTip2": "请调试应用,确认无误后发布", "releaseToolFlow": "发布工具流", "versionName": "版本名称", @@ -951,5 +953,13 @@ "reorganizeNodes": "整理节点", "zoomOut": "缩小", "zoomIn": "放大", - "formItemFieldTypeCannotBeEmpty": "表单项类型不能为空" + "formItemFieldTypeCannotBeEmpty": "表单项类型不能为空", + "addParallelTask": "添加并行任务", + "recover": "恢复", + "exit": "退出", + "resetSucceed": "恢复应用成功", + "currentDraft": "当前草稿", + "loading": "加载中", + "noMore": "已显示全部", + "latest": "最新" } diff --git a/app-engine/frontend/src/pages/addFlow/components/addflow-header.tsx b/app-engine/frontend/src/pages/addFlow/components/addflow-header.tsx index b6eff9a28c..2a161db9ab 100644 --- a/app-engine/frontend/src/pages/addFlow/components/addflow-header.tsx +++ b/app-engine/frontend/src/pages/addFlow/components/addflow-header.tsx @@ -37,7 +37,7 @@ import timeImg from '@/assets/images/ai/time.png'; const AddHeader = (props) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const { handleDebugClick, workFlow, types, saveTime } = props; + const { handleDebugClick, workFlow, types, saveTime, updateAippCallBack } = props; const { appInfo, setFlowInfo } = useContext(FlowContext); const [open, setOpen] = useState(false); const [imgPath, setImgPath] = useState(''); @@ -162,7 +162,12 @@ const AddHeader = (props) => { appInfo={appInfo} /> {/* 工具流发布历史信息弹窗 */} - +
)} }; diff --git a/app-engine/frontend/src/pages/addFlow/components/elsa-stage.tsx b/app-engine/frontend/src/pages/addFlow/components/elsa-stage.tsx index b5dddd602c..56fc7ff7b8 100644 --- a/app-engine/frontend/src/pages/addFlow/components/elsa-stage.tsx +++ b/app-engine/frontend/src/pages/addFlow/components/elsa-stage.tsx @@ -22,7 +22,7 @@ import { getAddFlowConfig, getEvaluateConfig } from '@/shared/http/appBuilder'; import { useAppDispatch, useAppSelector } from '@/store/hook'; import { setAppInfo, setValidateInfo } from '@/store/appInfo/appInfo'; import { setTestStatus, setTestTime } from '@/store/flowTest/flowTest'; -import { FlowContext } from '../../aippIndex/context'; +import { FlowContext, RenderContext } from '../../aippIndex/context'; import CreateTestSet from '../../appDetail/evaluate/testSet/createTestset/createTestSet'; import AddSearch from '../../configForm/configUi/components/add-search'; import { configMap } from '../config'; @@ -69,9 +69,11 @@ const Stage = (props) => { const [knowledgeConfigId, setKnowledgeConfigId] = useState(''); const { CONFIGS } = configMap[process.env.NODE_ENV]; const { type, appInfo, setFlowInfo } = useContext(FlowContext); + const { renderRef, elsaReadOnlyRef } = useContext(RenderContext); const testStatus = useAppSelector((state) => state.flowTestStore.testStatus); const appValidateInfo = useAppSelector((state) => state.appStore.validateInfo); const choseNodeId = useAppSelector((state) => state.appStore.choseNodeId); + const pluginList = useAppSelector((state) => state.chatCommonStore.pluginList); const { tenantId, appId } = useParams(); const testStatusRef = useRef(); const modelCallback = useRef(); @@ -79,7 +81,6 @@ const Stage = (props) => { const pluginCallback = useRef(); const formCallback = useRef(); const currentApp = useRef(); - const render = useRef(false); const currentChange = useRef(false); const modalRef = useRef(); const openModalRef = useRef(); @@ -91,16 +92,16 @@ const Stage = (props) => { const connectKnowledgeEvent = useRef(); const dispatch = useAppDispatch(); useEffect(() => { - if (appInfo.name && !render.current) { - render.current = true; + if (appInfo.name && !renderRef.current) { + renderRef.current = true; currentApp.current = JSON.parse(JSON.stringify(appInfo)); window.agent = null; - setElsaData(); + setElsaData(elsaReadOnlyRef.current); } }, [appInfo]); useEffect(() => { return () => { - render.current = false; + renderRef.current = false; window.agent = null; dispatch(setTestStatus(null)); } @@ -112,14 +113,38 @@ const Stage = (props) => { return null; } const realAppId = getQueryString('appId'); + const updateConfigs = () => { + const startNodeIndex = CONFIGS.findIndex(item => item.node === 'startNodeStart'); + if (startNodeIndex === -1) { + return; + } + const startNode = CONFIGS[startNodeIndex]; + if (!startNode.appConfig?.appChatStyle?.options) { + return; + } + // 生成 pluginOptions 并去重 + const existingValues = new Set( + startNode.appConfig.appChatStyle.options.map(opt => opt.value) + ); + const pluginOptions = pluginList + .filter(plugin => !existingValues.has(plugin.name)) + .map(plugin => ({ + value: plugin.name, + label: plugin.chineseName ?? plugin.name, + image: plugin.icon, + })); + + startNode.appConfig.appChatStyle.options.push(...pluginOptions); + }; // 编辑工作流 - function setElsaData() { + function setElsaData(readOnly: boolean) { let graphData = appInfo.flowGraph?.appearance || {}; const stageDom = document.getElementById('stage'); let data = JSON.parse(JSON.stringify(graphData)); let configIndex = CONFIGS.findIndex(item => item.node === 'llmNodeState'); CONFIGS[configIndex].params.tenantId = tenantId; CONFIGS[configIndex].params.appId = appId; + updateConfigs(); setSpinning && setSpinning(true); const flow = types === 'evaluate' ? JadeFlow.evaluate(stageDom, tenantId, realAppId, data, false, CONFIGS, i18n) @@ -211,7 +236,6 @@ const Stage = (props) => { agent.listen('SELECT_KNOWLEDGE_BASE_GROUP', (event) => { connectKnowledgeEvent.current = event; connectKnowledgeRef.current.openModal(); - event.onSelect(groupId, knowledgeConfigId); setGroupId(event.selectedGroupId); setKnowledgeConfigId(event.selectedKnowledgeConfigId); }); @@ -221,6 +245,15 @@ const Stage = (props) => { setShowTools(true); setModalTypes('loop'); }); + // 并行节点 + agent.onParallelSelect(({ onSelect }) => { + pluginCallback.current = onSelect; + setShowTools(true); + setModalTypes('parallel'); + }); + if (readOnly) { + agent.readOnly(); + } }).catch(() => { setSpinning && setSpinning(false); }); @@ -270,13 +303,17 @@ const Stage = (props) => { let obj = {}; let uniqueName = ''; let loopObj = {}; + let parallelObj = {}; item.forEach((e) => { obj = e.schema; uniqueName = e.uniqueName; loopObj = e; + parallelObj = e; }); if (modalTypes === 'loop') { pluginCallback.current(loopObj); + } else if (modalTypes === 'parallel') { + pluginCallback.current(parallelObj); } else if (modalTypes === 'llmTool') { pluginCallback.current(uniqueName); } else { @@ -293,6 +330,9 @@ const Stage = (props) => { // 数据实时保存 const handleChange = useCallback(debounce((id) => elsaChange(id), 2000), []); function elsaChange(id: any) { + if (elsaReadOnlyRef.current) { + return; + } let graphChangeData = window.agent.serialize(); currentApp.current.flowGraph.appearance = graphChangeData; updateAppRunningFlow(id); diff --git a/app-engine/frontend/src/pages/addFlow/index.tsx b/app-engine/frontend/src/pages/addFlow/index.tsx index f3d07dda53..689e79c1ef 100644 --- a/app-engine/frontend/src/pages/addFlow/index.tsx +++ b/app-engine/frontend/src/pages/addFlow/index.tsx @@ -36,8 +36,16 @@ import './styles/index.scss'; const AddFlow = (props) => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const { type, appInfo, addFlowRef, - showFlowChangeWarning, setShowFlowChangeWarning, saveTime, setSaveTime } = props; + const { + type, + appInfo, + addFlowRef, + showFlowChangeWarning, + setShowFlowChangeWarning, + saveTime, + setSaveTime, + updateAippCallBack, + } = props; const [dragData, setDragData] = useState([]); const [evaluateData, setEvaluateData] = useState([]); const [flowInfo, setFlowInfo] = useState({}); @@ -49,6 +57,7 @@ const AddFlow = (props) => { const { tenantId, appId } = useParams(); const [evaluateType, setEvaluateType] = useState('waterFlow'); const readOnly = useAppSelector((state) => state.chatCommonStore.readOnly); + const preview = useAppSelector((state) => state.commonStore.isReadOnly); const appRef = useRef(null); const flowIdRef = useRef(null); const elsaRunningCtl = useRef(null); @@ -134,7 +143,7 @@ const AddFlow = (props) => {
{/* 工具流header */} - {!type && + {(!type || workFlow) && { setShowDebug={setShowDebug} workFlow={workFlow} types={evaluateType} + updateAippCallBack={updateAippCallBack} />} {/* 调试抽屉弹窗 */} { {/* 左侧菜单 */}
{ - showMenu ? ( - - ) : ( - -
- -
-
+ !preview && ( + showMenu ? ( + + ) : ( + +
+ +
+
) + ) } {/* elsa编排组件 */} {}, setFlowInfo: (params:any) => {} }); + +export const RenderContext = createContext({ + renderRef: { current: false } as React.MutableRefObject, + elsaReadOnlyRef: { current: false } as React.MutableRefObject +}) diff --git a/app-engine/frontend/src/pages/aippIndex/index.tsx b/app-engine/frontend/src/pages/aippIndex/index.tsx index e6bccae356..5c1aa0bb53 100644 --- a/app-engine/frontend/src/pages/aippIndex/index.tsx +++ b/app-engine/frontend/src/pages/aippIndex/index.tsx @@ -4,22 +4,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import React, { useEffect, useState, useRef, useCallback } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Spin } from 'antd'; import { useParams } from 'react-router-dom'; import AddFlow from '../addFlow'; import ConfigForm from '../configForm'; import CommonChat from '../chatPreview/chatComminPage'; import ChoreographyHead from '../components/header'; -import { getAppInfo } from '@/shared/http/aipp'; -import { updateFormInfo } from '@/shared/http/aipp'; -import { debounce, getUiD, getCurrentTime, setSpaClassName } from '@/shared/utils/common'; +import { getAppInfo, updateFormInfo } from '@/shared/http/aipp'; +import { debounce, getCurrentTime, getUiD, setSpaClassName, getAppConfig } from '@/shared/utils/common'; import { useAppDispatch, useAppSelector } from '@/store/hook'; import { setInspirationOpen } from '@/store/chatStore/chatStore'; -import { setAppId, setAippId, setAppInfo, setChoseNodeId, setValidateInfo } from '@/store/appInfo/appInfo'; -import { setIsDebug } from "@/store/common/common"; -import { getUser } from '../helper'; -import { setTestStatus } from "@/store/flowTest/flowTest"; +import { setAippId, setAppId, setAppInfo, setChoseNodeId, setValidateInfo } from '@/store/appInfo/appInfo'; +import { setIsDebug } from '@/store/common/common'; +import { setTestStatus } from '@/store/flowTest/flowTest'; +import { RenderContext } from '@/pages/aippIndex/context'; /** * 应用配置页面首页 @@ -30,9 +29,11 @@ import { setTestStatus } from "@/store/flowTest/flowTest"; const AippIndex = () => { const { appId, tenantId, aippId } = useParams(); const [showElsa, setShowElsa] = useState(false); + const [showConfig, setShowConfig] = useState(true); const [spinning, setSpinning] = useState(false); const [saveTime, setSaveTime] = useState(''); const [reloadInspiration, setReloadInspiration] = useState(''); + const [workFlow, setWorkFlow] = useState(''); const [showChat, setShowChat] = useState(false); const [messageChecked, setMessageCheck] = useState(false); const [showFlowChangeWarning, setShowFlowChangeWarning] = useState(false); @@ -40,13 +41,35 @@ const AippIndex = () => { const inspirationRefresh = useRef(false); const dispatch = useAppDispatch(); const appInfo = useAppSelector((state) => state.appStore.appInfo); + const pluginList = useAppSelector((state) => state.chatCommonStore.pluginList); const addFlowRef = useRef(null); + const renderRef = useRef(false); + const elsaReadOnlyRef = useRef(false); + + const [pluginName, setPluginName] = useState('default'); + const [plugin, setPlugin] = useState(); + const elsaChange = () => { setShowElsa(!showElsa); showElsa && getAippDetails(true); } + const handleChangeShowConfig = () => { + setShowConfig(!showConfig); + }; + + useEffect(() => { + const found = pluginList.find((item: any) => item.name === pluginName); + setPlugin(found); + }, [pluginList, pluginName]); + + useEffect(() => { + if (plugin) { + setShowConfig(false); + } + }, [plugin]); + useEffect(() => { dispatch(setAppInfo({})); dispatch(setAppId(appId)); @@ -54,8 +77,10 @@ const AippIndex = () => { dispatch(setInspirationOpen(false)); // TODO: 待后端接口归一后调用 getUser() getAippDetails(); - if (window.location.href.indexOf('type=chatWorkflow') !== -1) { + // TODO: 后续归一插件和应用创建工具流入口的时候需注意type + if (window.location.href.indexOf('type=workFlow') !== -1) { setShowElsa(true); + setWorkFlow('workFlow'); }; return () => { dispatch(setChoseNodeId('')); @@ -79,15 +104,27 @@ const AippIndex = () => { res.data.hideHistory = true; aippRef.current = res.data; dispatch(setAppInfo(res.data)); + RefreshChatStyle(res.data); } } finally { setSpinning(false); } } + + // 基于appInfo更新对话界面 + const RefreshChatStyle = (appInfo) => { + const appChatStyle = getAppConfig(appInfo) ? getAppConfig(appInfo).appChatStyle : null; + setPluginName(appChatStyle || 'default'); + }; + + // 修改aipp更新回调 - const updateAippCallBack = (data) => { - if (data) { - aippRef.current = data; + const updateAippCallBack = (partialData) => { + if (partialData) { + aippRef.current = { + ...aippRef.current, + ...partialData + }; dispatch(setAppInfo(aippRef.current)); } } @@ -139,19 +176,22 @@ const AippIndex = () => { <>
- -
- {showElsa ? - ( + className={`container ${showElsa ? 'layout-elsa-content' : ''} ${showChat ? 'layout-show-preview' : ''}`} + > + + {!workFlow ? ( + + ) : null} +
+ {showElsa ? ( { setSaveTime={setSaveTime} showFlowChangeWarning={showFlowChangeWarning} setShowFlowChangeWarning={setShowFlowChangeWarning} + updateAippCallBack={updateAippCallBack} /> - ) : - ( + ) : ( { handleConfigDataChange={handleConfigDataChange} inspirationChange={inspirationChange} showElsa={showElsa} + showConfig={showConfig} + onChangeShowConfig={handleChangeShowConfig} /> )} - -
+ +
+
diff --git a/app-engine/frontend/src/pages/appDetail/overview/index.tsx b/app-engine/frontend/src/pages/appDetail/overview/index.tsx index d986dec942..de771463e0 100644 --- a/app-engine/frontend/src/pages/appDetail/overview/index.tsx +++ b/app-engine/frontend/src/pages/appDetail/overview/index.tsx @@ -64,11 +64,13 @@ const AppOverview: React.FC = () => { setLoading(false); }); }, []); + // 获取图片 const getImgPath = async (icon) => { const res: any = await convertImgPath(icon); setAppIcon(res); }; + // 去编排点击回调 const gotoArrange = () => { setBtnLoading(true); @@ -78,18 +80,16 @@ const AppOverview: React.FC = () => { dispatch(setAppInfo({})); const newAppId = res.data.id; const aippId = res.data.aippId; + + let url = `/app-develop/${tenantId}/app-detail/${newAppId}`; if (aippId) { - if (detail.appCategory === 'workflow') { - navigate({ - pathname: `/app-develop/${tenantId}/app-detail/${newAppId}/${aippId}`, - search: '?type=chatWorkflow', - }); - } else { - navigate(`/app-develop/${tenantId}/app-detail/${newAppId}/${aippId}`); - } - } else { - navigate(`/app-develop/${tenantId}/app-detail/${newAppId}`); + url += `/${aippId}`; } + if (detail.appCategory === 'workflow') { + url += '?type=chatWorkflow'; + } + + navigate(url); } }).catch(() => { setBtnLoading(false); @@ -131,6 +131,7 @@ const AppOverview: React.FC = () => { setOpening(opening || '-'); }), [detail]; + return (
diff --git a/app-engine/frontend/src/pages/apps/index.tsx b/app-engine/frontend/src/pages/apps/index.tsx index 1807fd8e41..ec9b4a3f77 100644 --- a/app-engine/frontend/src/pages/apps/index.tsx +++ b/app-engine/frontend/src/pages/apps/index.tsx @@ -71,11 +71,13 @@ const Apps: React.FC = () => { function clickCard(item: any, e: any) { let id = item.runnables?.APP?.appId; let aippId = item.runnables?.APP?.aippId; + + let url = `/app/${tenantId}/chat/${id}`; if (aippId) { - navigate(`/app/${tenantId}/chat/${id}/${aippId}`); - } else { - navigate(`/app/${tenantId}/chat/${id}`); + url += `/${aippId}`; } + + navigate(url); } // 点击更多操作选项 diff --git a/app-engine/frontend/src/pages/chatPreview/chatComminPage.tsx b/app-engine/frontend/src/pages/chatPreview/chatComminPage.tsx index eef0d87531..b1fbc4607c 100644 --- a/app-engine/frontend/src/pages/chatPreview/chatComminPage.tsx +++ b/app-engine/frontend/src/pages/chatPreview/chatComminPage.tsx @@ -5,18 +5,125 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import React from 'react'; +import qs from 'qs'; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { useParams, useHistory } from 'react-router-dom'; +import useSearchParams from "@/shared/hooks/useSearchParams"; import { AippContext } from '../aippIndex/context'; import ChatPreview from './index'; +import { useAppDispatch, useAppSelector } from "@/store/hook"; +import { TENANT_ID } from "@/pages/chatPreview/components/send-editor/common/config"; +import { findConfigValue } from "@/shared/utils/common"; +import { setAppInfo } from "@/store/appInfo/appInfo"; // 公共参数,公共聊天界面 const CommonChat = (props: any) => { - const { contextProvider, previewBack } = props; + const { contextProvider, previewBack, showElsa, pluginName = 'default' } = props; + const iframeRef = useRef(null); + const appInfo = useAppSelector((state) => state.appStore.appInfo); + const isDebug = useAppSelector((state) => state.commonStore.isDebug); + const pluginList = useAppSelector((state) => state.chatCommonStore.pluginList); + + const history = useHistory(); + const dispatch = useAppDispatch(); + + const { uid } = useParams(); + const isPreview = useMemo(() => !!uid, [uid]); + + const searchParams = useSearchParams(); + const [plugin, setPlugin] = useState(); + + useEffect(() => { + const found = pluginList.find((item: any) => item.name === pluginName); + setPlugin(found); + }, [pluginList, pluginName]); + + const iframeUrl = useMemo(() => { + let url = plugin?.url; + const hasSearch = url?.includes('?'); + const search = qs.stringify(searchParams); + if (search) { + url += hasSearch ? `&${search}` : `?${search}` + } + return url; + }, [plugin, isPreview, searchParams]); + + const handleReady = () => { + sendMessageToIframe(); + }; + + const handleBack = () => { + if (isPreview) { + history.goBack(); + } else { + history.push({ + pathname: '/app' + }); + } + }; + + const handleNavigate = (data: any) => { + const search = new URLSearchParams(history.location.search); + Object.entries(data.params).forEach(([key, value]) => { + if (value === null) { + search.delete(key); + } else { + search.set(key, String(value)); + } + }); + + history.push({ + search: search.toString() + }); + }; + + useEffect(() => { + const handler = (e: { data: string }) => { + const data = JSON.parse(e.data); + if (data.type === 'ready') { + handleReady(); + } else if (data.type === 'back') { + handleBack(); + } else if (data.type === 'navigate') { + handleNavigate(data); + } + }; + window.addEventListener('message', handler); + return () => window.removeEventListener('message', handler); + }); + + // 给iframe的对话界面传递参数 + const sendMessageToIframe = () => { + let params = { + tenantId: TENANT_ID, + appId: appInfo.id, + useMemory: findConfigValue(appInfo, 'memory').memorySwitch, + isDebug + } + iframeRef.current?.contentWindow.postMessage({...params}, '*'); + } + + useEffect(() => { + return () => { + dispatch(setAppInfo({})); + }; + }, []); + return ( - - - + (plugin && !showElsa) + ?