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/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 093665598f..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))); } @@ -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,7 +430,8 @@ 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); 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..7fdb15b41d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersion.java @@ -0,0 +1,1110 @@ +/*--------------------------------------------------------------------------------------------- + * 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; + + 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(); + } + + /** + * 获取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, 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..1b9b68230d --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/AppVersionFactory.java @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * 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; + + 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) { + 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; + } + + /** + * 创建 {@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) + .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..23e5332d5c --- /dev/null +++ b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/domains/appversion/Dependencies.java @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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; +} 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..1ce0b09432 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,33 @@ public class AppExportDto { @Property(description = "应用流程图配置信息") AppExportFlowGraph flowGraph; + + /** + * 获取icon路径. + * + * @param contextRoot 请求上下文根 + * @param context 操作人上下文信息. + * @return {@link String} icon路径. + */ + @JsonIgnore + public String getIconPath(String contextRoot, 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); + } + + /** + * 获取类型. + * + * @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 579d7ea7fa..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); } /** @@ -287,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); @@ -392,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()); } /** @@ -531,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); @@ -543,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; @@ -601,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/AippLogMapper.java b/app-builder/jane/plugins/aipp-plugin/src/main/java/modelengine/fit/jober/aipp/mapper/AippLogMapper.java index 83a20d2417..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); /** * 删除指定实例的历史记录。 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/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/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/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 dc45f2978b..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,6 +10,7 @@ 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; @@ -25,7 +26,6 @@ @Component public class AppBuilderRuntimeInfoRepositoryImpl implements AppBuilderRuntimeInfoRepository { private final AppBuilderRuntimeInfoMapper mapper; - private final AppBuilderRuntimeInfoSerializer serializer; public AppBuilderRuntimeInfoRepositoryImpl(AppBuilderRuntimeInfoMapper mapper) { 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 a5b373538f..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,11 +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.jane.common.entity.OperationContext; import modelengine.fitframework.annotation.Genericable; import java.util.List; @@ -172,16 +171,6 @@ RangedResultSet recentPublished(AppQueryCondition cond, long o @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); - /** * 根据应用配置进行可用性校验。 * @@ -192,16 +181,6 @@ RangedResultSet recentPublished(AppQueryCondition cond, long o @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); - /** * 将应用发布为应用模板。 * 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 3573ed1682..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.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.ToolService; -import modelengine.jade.store.service.support.DeployStatus; 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 9798f7efc4..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); - } - 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); + 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); } - 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,186 +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; - appearance = this.resetGraphId(flowGraph); - // 动态修改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 @@ -763,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 @@ -809,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 @@ -839,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 @@ -879,423 +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 RangedResultSet recentPublished(AppQueryCondition cond, long offset, int limit, - String appId, OperationContext context) { - this.validateApp(appId); - try { - String aippId = MetaUtils.getAippIdByAppId(this.metaService, appId, context); - RangedResultSet metaRangedResultSet = - MetaUtils.getPublishedMetaByPage(this.metaService, aippId, offset, limit, context); - List allPublishedMeta = metaRangedResultSet.getResults(); - 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() - .map(app -> appFactory.create(app.getId())) - .collect(Collectors.toMap(AppBuilderApp::getId, Function.identity())); - return RangedResultSet.create(this.buildPublishedAppResDtos(allPublishedMeta, appIdKeyAppValueMap), - metaRangedResultSet.getRange()); - } catch (AippTaskNotFoundException exception) { - throw new AippException(QUERY_PUBLICATION_HISTORY_FAILED); - } + public RangedResultSet recentPublished(AppQueryCondition cond, long offset, int limit, String appId, + OperationContext context) { + 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 @@ -1311,763 +368,10 @@ public List checkAvailable(List appCheckDtos, Operatio @Override @Transactional public AppBuilderAppDto recoverApp(String appId, String resetId, OperationContext context) { - AppBuilderApp resetApp = this.appFactory.create(resetId); - AppBuilderApp currentApp = this.appFactory.create(appId); - List resetFormProperties = resetApp.getFormProperties(); - List currentFormProperties = currentApp.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()); - } - }); - currentApp.getFormPropertyRepository().updateMany(currentFormProperties); - - AppBuilderFlowGraph resetGraph = resetApp.getFlowGraph(); - AppBuilderFlowGraph currentGraph = currentApp.getFlowGraph(); - String currentGraphId = currentApp.getFlowGraphId(); - resetGraph.setId(currentGraphId); - - Map appearance; - appearance = this.resetGraphId(resetGraph); - currentGraph.setAppearance(JSONObject.toJSONString(appearance)); - currentApp.getFlowGraphRepository().updateOne(currentGraph); - - this.appFactory.update(currentApp); - return this.buildFullAppDto(currentApp); - } - - 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 AppBuilderAppDto 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); - AppBuilderAppDto dto = this.buildFullAppDto(app); - dto.setPublishedDescription(publishedDescription); - dto.setPublishedUpdateLog(publishedUpdateLog); - return dto; - } - - 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); - } - - private Map resetGraphId(AppBuilderFlowGraph flowGraph) { - 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()); - return appearance; + 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..9158d0f7a5 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,39 +78,23 @@ @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 HttpClassicClientFactory httpClassicClientFactory; @@ -124,7 +109,8 @@ 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.form.group-name}") String groupName, + @Value("${app-engine.form.path}") String formPath, @Value("${app-engine.resource.path-prefix}") String resourcePathPrefix) { this.httpClassicClientFactory = httpClassicClientFactory; this.imageGenModelUrl = imageGenModelUrl; @@ -142,8 +128,8 @@ public FileServiceImpl(HttpClassicClientFactory httpClassicClientFactory, @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()); } @@ -355,7 +348,9 @@ 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()); } @@ -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/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..fdc0b4a568 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()) 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 5e787ee3f5..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,287 +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); - } - - /** - * 分页查询指定应用的已发布元数据列表,按更新时间倒序。 - * - * @param metaService 表示提供元数据访问功能的 {@link MetaService}。 - * @param metaId 表示指定应用唯一标识的 {@link String}。 - * @param offset 表示偏移量的 {@code long}。 - * @param limit 表示单页最大数量的 {@code int}。 - * @param context 表示操作人上下文的 {@link OperationContext}。 - * @return 表示查询到的结果集的 {@link RangedResultSet}{@code <}{@link Meta}{@code >}。 - */ - public static RangedResultSet getPublishedMetaByPage(MetaService metaService, String metaId, long offset, - int limit, OperationContext context) { - MetaFilter metaFilter = getNormalMetaFilter(metaId, NormalFilterEnum.PUBLISHED); - metaFilter.setOrderBys(Collections.singletonList(formatSorter(AippSortKeyEnum.UPDATE_AT.name(), - DirectionEnum.DESCEND.name()))); - return metaService.list(metaFilter, false, offset, limit, 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实例中获取查询结果 * @@ -351,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 6bba3a8641..4d9971805f 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 @@ -54,6 +54,11 @@ app-engine: ttl: businessData: 15 nonBusinessData: 1 + max-number: 200 + question: + max-length: 20000 + user-context: + max-length: 500 elsa: endpoint: elsaKey: 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/mapper/AippChatMapper.xml b/app-builder/jane/plugins/aipp-plugin/src/main/resources/mapper/AippChatMapper.xml index d5ae54c057..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 @@ -80,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} + + + @@ -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/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/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql index a1cc9e5f41..93aa078d4f 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql +++ b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/data/appbuilder_insert.sql @@ -1,6 +1,6 @@ -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; @@ -74,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', @@ -87,12 +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; + '{"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 index b6c34f2ac8..4ce7779bfb 100644 --- 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 @@ -1,4 +1,4 @@ -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") 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') 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 ('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; diff --git a/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/appbuilder_create.sql b/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/appbuilder_create.sql index bb5e69ec39..d34da6e817 100644 --- a/app-builder/jane/plugins/aipp-plugin/src/main/resources/sql/schema/create_tables/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/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..f2b1af654e --- /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); + } + + /** + * 基本测试. + */ + @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 3bf9c6cb1a..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; @@ -85,36 +104,19 @@ */ @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; @@ -226,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)); @@ -266,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<>(); @@ -279,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 @@ -317,8 +317,15 @@ void shouldFailedWhenNoTool() throws InterruptedException { 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); @@ -338,22 +345,28 @@ void shouldOkWhenUseWorkflowNoReturn() throws InterruptedException { .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)); @@ -378,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 @@ -422,10 +441,14 @@ 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(); + 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,16 +473,14 @@ void shouldFailLLmNodeWhenHandleGivenWorkflowException() throws InterruptedExcep 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)); @@ -493,8 +514,15 @@ 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() { 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/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 4e9128e4d8..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.jade.store.entity.transfer.PluginData; -import modelengine.jade.store.service.AppService; -import modelengine.jade.store.service.ToolService; +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 d13f4239e4..52d9d0157d 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; - + private UploadedFileManageService uploadedFileService; @Mock - private AppBuilderConfigRepository configRepository; - + private AppTaskService appTaskService; @Mock - private AppBuilderFormRepository formRepository; - + private AppVersionService appVersionService; @Mock - private AppBuilderConfigPropertyRepository configPropertyRepository; - + private AppDomainService appDomainService; @Mock - private AppBuilderFormPropertyRepository formPropertyRepository; - + private AppFactory appDomainFactory; @Mock - private MetaService metaService; + private AppTemplateFactory templateFactory; - @Mock - private UsrAppCollectionService usrAppCollectionService; - - @Mock - private UploadedFileManageService uploadedFileService; - - @Mock - private AippModelCenter aippModelCenter; - - @Mock - private AppTypeService appTypeService; + private ConverterFactory converterFactory; @Mock private AippFlowDefinitionService aippFlowDefinitionService; @@ -179,279 +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(String appId) { - 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(appId); - 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(appId)); - appBuilderApp.getConfig().setApp(appBuilderApp); - appBuilderApp.setConfigId(appBuilderApp.getConfig().getId()); - appBuilderApp.setFlowGraph(this.mockGraph()); - appBuilderApp.setFlowGraphId(appBuilderApp.getFlowGraph().getId()); - appBuilderApp.setFormProperties(mockFormProperties(appId)); - return appBuilderApp; } - private AppBuilderConfig mockConfig(String appId) { - AppBuilderConfig config = new AppBuilderConfig(this.formRepository, this.formPropertyRepository, - this.configPropertyRepository, this.appRepository); - - config.setAppId(appId); - 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(appId); - 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(String appId) { - 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); - formProperty.setAppId(appId); - 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\": \"69e9dec999384b1791e24a3032010e77\", \"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(appId); - 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(); } /** @@ -463,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("id"); - 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("test-appid")); - 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("45698235b3d24209aefd59eb7d1c3322")); - 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 @@ -710,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 @@ -737,179 +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(appId)); - 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); - String appId = "45698235b3d24209aefd59eb7d1c3322"; - Assertions.assertDoesNotThrow(() -> saveNewAppBuilderApp.invoke(appBuilderAppService, mockApp(appId))); - verify(uploadedFileService).updateRecord(eq(appId), 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(appId)); - 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 @@ -923,71 +290,73 @@ 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); - 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"); - } - - @Test - @DisplayName("测试根据应用种类获取应用列表") + @DisplayName("测试获取应用列表") void testListApplication() { - AppBuilderApp app = this.mockApp("testId"); - 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"); + 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(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 testResetApp() { - String currentAppId = "currentId"; - String resetAppId = "resetId"; - String resetTenantId = "default"; - - AppBuilderApp currentApp = this.mockApp(currentAppId); - String graphString = "{\"id\": \"graphId\", \"title\":\"graphId\"}, \"tenant\":\"tenantId\""; - String currentGraphId = "graphId"; - AppBuilderFlowGraph currentGraph = - AppBuilderFlowGraph.builder().appearance(graphString).id(currentGraphId).name("LLM template").build(); - currentApp.setFlowGraph(currentGraph); - currentApp.setFlowGraphId(currentGraphId); - - when(this.appRepository.selectWithId(eq(currentAppId))).thenReturn(currentApp); - when(this.appRepository.selectWithId(eq(resetAppId))).thenReturn(this.mockApp(resetAppId)); - AppBuilderAppDto dto = this.appBuilderAppService.recoverApp(currentAppId, resetAppId, new OperationContext()); - - assertThat(dto).extracting(dto1 -> dto1.getFlowGraph().getId(), - dto1 -> dto1.getFlowGraph().getAppearance().get("id"), - dto1 -> dto1.getFlowGraph().getAppearance().get("title"), - dto1 -> dto1.getFlowGraph().getAppearance().get("tenant")) - .containsExactly(currentGraphId, currentGraphId, currentGraphId, resetTenantId); + /** + * 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); + 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/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..d7ae2ce33d 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,11 +177,10 @@ 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 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"); assertThat(iconUrl).startsWith("/api/jober/v1/api/123"); @@ -186,9 +196,9 @@ 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 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"); 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 b3bf632b7b..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 @@ -20,4 +20,6 @@ truncate table app_template; truncate table app_builder_runtime_info; -truncate table t_chat_session; \ No newline at end of file +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 7bebe480be..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 @@ -147,6 +147,43 @@ create table if not exists app_template ( 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, @@ -166,17 +203,4 @@ create table if not exists app_builder_runtime_info create_at timestamp not null default current_timestamp, update_by varchar(64), update_at timestamp not null default current_timestamp -); - -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 ); \ 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/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_t_model_import.sql b/app-builder/plugins/aipp-custom-model-center/src/main/resources/sql/data/tr_t_model_import.sql index 941fe21fc4..d50fb687da 100644 --- 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 @@ -26,7 +26,7 @@ INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "d 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") 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') 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; 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/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 index 4752621832..c8dceea14a 100644 --- 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 @@ -1,4 +1,4 @@ -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") 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') 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 ('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; 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-knowledge/plugins/knowledge-manager/src/main/resources/sql/data/tr_t_knowledge_config.sql b/app-knowledge/plugins/knowledge-manager/src/main/resources/sql/data/tr_t_knowledge_config.sql index a413fe00a4..3c4b6a23ee 100644 --- a/app-knowledge/plugins/knowledge-manager/src/main/resources/sql/data/tr_t_knowledge_config.sql +++ b/app-knowledge/plugins/knowledge-manager/src/main/resources/sql/data/tr_t_knowledge_config.sql @@ -35,7 +35,7 @@ INSERT INTO "public"."store_plugin" ("plugin_id", "plugin_name", "extension", "d 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 ('aed5570845d440c78ed3326f8482483c', '知识库管理表单', '31f20efc7e0848deab6a6bc10fc3021e', '{"imgUrl": "smart_form/17b732c9-5272-42a6-a79d-8d0334a8aa19/form.jpeg", "schema": {"name": "知识库管理表单", "return": {"type": "object", "properties": {"info": {"type": "object", "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "apiKey": {"type": "string"}, "groupId": {"type": "string"}, "isDefault": {"type": "integer"}}}, "action": {"enum": ["add", "delete", "switch", "quit"], "type": "string"}}}, "parameters": {"type": "object", "required": ["knowledgeList", "knowledgeOptions"], "properties": {"knowledgeList": {"type": "array", "items": {"type": "object", "required": ["groupId", "name", "apiKey", "isDefault"], "properties": {"id": {"type": "integer"}, "name": {"type": "string"}, "apiKey": {"type": "string"}, "userId": {"type": "String"}, "groupId": {"type": "string"}, "isDefault": {"type": "integer"}}}}, "knowledgeOptions": {"type": "array", "items": {"type": "object", "required": ["name", "groupId", "description"], "properties": {"name": {"type": "string"}, "groupId": {"type": "string"}, "description": {"type": "string"}}}}}}}, "fileName": "knowledge.zip", "fileSize": 362092, "fileUuid": "904be596a4b94873b22a008ca807cc3b", "iframeUrl": "smart_form/17b732c9-5272-42a6-a79d-8d0334a8aa19/build/index.html", "description": "这是一个知识库管理表单"}', 'runtime', 'Jade', '2025-04-28 02:04:18.174594', 'Jade', '2025-04-28 14:05:47.721196', 0, '83dc556b25544414b88b538281be4d66', '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") VALUES ('c419cf9d3f2d472da6b376bbb07c0f5a', '知识库配置应用', 'Jade', '2025-04-27 11:38:16.181311', 'Jade', '2025-04-28 14:11:01.841006', 'c9a41dd87eb04b89bbfa6de2b221bb5b', '7075f47449504e639571887473986da6', '31f20efc7e0848deab6a6bc10fc3021e', 'app', '1.0.0', '{"icon": "", "name": "知识库配置应用", "app_type": "b653edb7eb5a49be91abcd2c5877c6ad", "greeting": "", "store_id": "4ff18263-7cc1-40fc-bd96-4c0ee9eb3af0", "is_update": false, "description": "当你想要配置知识库的时候,请使用我!", "publishedUpdateLog": "", "publishedDescription": ""}', 'active', 'workflow', 'chatbot', 0, 0, 'z7hFDTDrjuCUHMn4', 'b653edb7eb5a49be91abcd2c5877c6ad') 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 ('c419cf9d3f2d472da6b376bbb07c0f5a', '知识库配置应用', 'Jade', '2025-04-27 11:38:16.181311', 'Jade', '2025-04-28 14:11:01.841006', 'c9a41dd87eb04b89bbfa6de2b221bb5b', '7075f47449504e639571887473986da6', '31f20efc7e0848deab6a6bc10fc3021e', 'app', '1.0.0', '{"icon": "", "name": "知识库配置应用", "app_type": "b653edb7eb5a49be91abcd2c5877c6ad", "greeting": "", "store_id": "4ff18263-7cc1-40fc-bd96-4c0ee9eb3af0", "is_update": false, "description": "当你想要配置知识库的时候,请使用我!", "publishedUpdateLog": "", "publishedDescription": ""}', 'active', 'workflow', 'chatbot', 0, 0, 'z7hFDTDrjuCUHMn4', 'b653edb7eb5a49be91abcd2c5877c6ad', '5185dad4c8124522a2612c20f8497cf0', 't', 'published', '4ff18263-7cc1-40fc-bd96-4c0ee9eb3af0', '2025-04-28 14:11:19', 'c419cf9d3f2d472da6b376bbb07c0f5a') 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 ('c9a41dd87eb04b89bbfa6de2b221bb5b', 'b8986770a6ffef44bbf2a9f26d6fc1be', 'c419cf9d3f2d472da6b376bbb07c0f5a', '31f20efc7e0848deab6a6bc10fc3021e', 'Jade', '2025-04-27 11:38:16.181311', 'Jade', '2025-04-28 14:11:01.841006', 0) ON CONFLICT (id) DO NOTHING; diff --git a/app-knowledge/plugins/knowledge-node-default/src/test/java/modelengine/jade/knowledge/RetrieverHandlerTest.java b/app-knowledge/plugins/knowledge-node-default/src/test/java/modelengine/jade/knowledge/RetrieverHandlerTest.java index a6576940bc..7ed10b9ebd 100644 --- a/app-knowledge/plugins/knowledge-node-default/src/test/java/modelengine/jade/knowledge/RetrieverHandlerTest.java +++ b/app-knowledge/plugins/knowledge-node-default/src/test/java/modelengine/jade/knowledge/RetrieverHandlerTest.java @@ -11,6 +11,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import modelengine.fel.core.document.MeasurableDocument; +import modelengine.fitframework.broker.client.Invoker; import modelengine.jade.knowledge.document.KnowledgeDocument; import modelengine.jade.knowledge.entity.RetrieverOption; import modelengine.jade.knowledge.retriever.RetrieverHandler; @@ -18,9 +20,6 @@ import modelengine.jade.knowledge.router.KnowledgeServiceRouter; import modelengine.jade.knowledge.util.RetrieverServiceUtils; -import modelengine.fel.core.document.MeasurableDocument; -import modelengine.fitframework.broker.client.Invoker; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/common/plugins/http-interceptor/src/main/java/modelengine/jade/common/filter/support/StaticResourceFilter.java b/common/plugins/http-interceptor/src/main/java/modelengine/jade/common/filter/support/StaticResourceFilter.java index 9a43ef3e9a..be103045a9 100644 --- a/common/plugins/http-interceptor/src/main/java/modelengine/jade/common/filter/support/StaticResourceFilter.java +++ b/common/plugins/http-interceptor/src/main/java/modelengine/jade/common/filter/support/StaticResourceFilter.java @@ -30,7 +30,7 @@ /** * 用于静态资源的过滤器类。 * - * @author 邬涨财 w00575064 + * @author 邬涨财 * @since 2023-07-13 */ @Component