Date: Wed, 15 Oct 2025 09:13:42 +0800
Subject: [PATCH 34/90] fix(hub): fix sql error
---
.../resources/mapper/BotDatasetMaasMapper.xml | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/console/backend/commons/src/main/resources/mapper/BotDatasetMaasMapper.xml b/console/backend/commons/src/main/resources/mapper/BotDatasetMaasMapper.xml
index f01e2876..5ba04e0c 100644
--- a/console/backend/commons/src/main/resources/mapper/BotDatasetMaasMapper.xml
+++ b/console/backend/commons/src/main/resources/mapper/BotDatasetMaasMapper.xml
@@ -3,17 +3,28 @@
--
Gitee
From 1bdb4e4d05339725943c040b93779ed167da51ea Mon Sep 17 00:00:00 2001
From: yun-zhi-ztl <15071461069@163.com>
Date: Wed, 15 Oct 2025 09:29:26 +0800
Subject: [PATCH 35/90] fix: change copy workflow url
---
.../java/com/iflytek/astron/console/commons/util/MaasUtil.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
index f8d5ebbf..e6129387 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
@@ -463,7 +463,7 @@ public class MaasUtil {
public JSONObject copyWorkFlow(Long maasId, HttpServletRequest request) {
log.info("----- Copying maas workflow id: {}", maasId);
- HttpUrl baseUrl = HttpUrl.parse(cloneWorkFlowUrl + "/workflow/internal-clone");
+ HttpUrl baseUrl = HttpUrl.parse(cloneWorkFlowUrl);
if (baseUrl == null) {
log.error("Failed to parse clone workflow URL: {}", cloneWorkFlowUrl);
throw new BusinessException(ResponseEnum.CLONE_BOT_FAILED);
--
Gitee
From d1cf0572b02088c9dadb14dd55d6c09f40721654 Mon Sep 17 00:00:00 2001
From: yun-zhi-ztl <15071461069@163.com>
Date: Wed, 15 Oct 2025 09:46:41 +0800
Subject: [PATCH 36/90] feat: merge console bd
---
.../publish/impl/BotPublishServiceImpl.java | 58 +++++++++----------
1 file changed, 27 insertions(+), 31 deletions(-)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
index d27b7399..d7acbb33 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
@@ -4,39 +4,32 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.iflytek.astron.console.commons.entity.bot.UserLangChainInfo;
-import com.iflytek.astron.console.hub.dto.PageResponse;
+import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.dto.bot.BotListRequestDto;
-import com.iflytek.astron.console.hub.dto.publish.BotPublishInfoDto;
-import com.iflytek.astron.console.hub.dto.publish.BotDetailResponseDto;
-import com.iflytek.astron.console.hub.dto.publish.BotVersionVO;
-import com.iflytek.astron.console.hub.dto.publish.BotSummaryStatsVO;
-import com.iflytek.astron.console.hub.dto.publish.BotTimeSeriesResponseDto;
-import com.iflytek.astron.console.hub.dto.publish.BotTimeSeriesStatsVO;
-import com.iflytek.astron.console.hub.dto.publish.WechatAuthUrlResponseDto;
-import com.iflytek.astron.console.hub.dto.publish.BotTraceRequestDto;
-import com.iflytek.astron.console.hub.dto.publish.UnifiedPrepareDto;
-import com.iflytek.astron.console.hub.dto.publish.prepare.*;
-import com.iflytek.astron.console.hub.dto.publish.prepare.WechatPrepareDto;
-import com.iflytek.astron.console.commons.enums.bot.ReleaseTypeEnum;
-import com.iflytek.astron.console.commons.service.data.UserLangChainDataService;
+import com.iflytek.astron.console.commons.dto.bot.BotPublishQueryResult;
+import com.iflytek.astron.console.commons.dto.bot.BotQueryCondition;
+import com.iflytek.astron.console.commons.dto.bot.ChatBotApi;
+import com.iflytek.astron.console.commons.entity.bot.UserLangChainInfo;
import com.iflytek.astron.console.commons.enums.PublishChannelEnum;
import com.iflytek.astron.console.commons.enums.ShelfStatusEnum;
-import com.iflytek.astron.console.commons.mapper.bot.ChatBotMarketMapper;
+import com.iflytek.astron.console.commons.enums.bot.ReleaseTypeEnum;
+import com.iflytek.astron.console.commons.exception.BusinessException;
import com.iflytek.astron.console.commons.mapper.bot.ChatBotApiMapper;
-import com.iflytek.astron.console.hub.mapper.BotConversationStatsMapper;
import com.iflytek.astron.console.commons.mapper.bot.ChatBotBaseMapper;
+import com.iflytek.astron.console.commons.mapper.bot.ChatBotMarketMapper;
+import com.iflytek.astron.console.commons.service.data.UserLangChainDataService;
+import com.iflytek.astron.console.commons.util.BotFileParamUtil;
import com.iflytek.astron.console.hub.converter.BotPublishConverter;
import com.iflytek.astron.console.hub.converter.WorkflowVersionConverter;
-import com.iflytek.astron.console.hub.service.publish.PublishChannelService;
-import com.iflytek.astron.console.hub.service.wechat.WechatThirdpartyService;
-import com.iflytek.astron.console.commons.dto.bot.BotPublishQueryResult;
-import com.iflytek.astron.console.commons.dto.bot.ChatBotApi;
+import com.iflytek.astron.console.hub.dto.PageResponse;
+import com.iflytek.astron.console.hub.dto.publish.*;
+import com.iflytek.astron.console.hub.dto.publish.prepare.*;
import com.iflytek.astron.console.hub.entity.BotConversationStats;
+import com.iflytek.astron.console.hub.event.BotPublishStatusChangedEvent;
+import com.iflytek.astron.console.hub.mapper.BotConversationStatsMapper;
import com.iflytek.astron.console.hub.service.publish.BotPublishService;
-import com.iflytek.astron.console.commons.exception.BusinessException;
-import com.iflytek.astron.console.commons.constant.ResponseEnum;
-import com.iflytek.astron.console.commons.util.BotFileParamUtil;
+import com.iflytek.astron.console.hub.service.publish.PublishChannelService;
+import com.iflytek.astron.console.hub.service.wechat.WechatThirdpartyService;
import com.iflytek.astron.console.toolkit.entity.table.workflow.WorkflowVersion;
import com.iflytek.astron.console.toolkit.mapper.workflow.WorkflowVersionMapper;
import lombok.RequiredArgsConstructor;
@@ -44,8 +37,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
-import com.iflytek.astron.console.commons.dto.bot.BotQueryCondition;
-import com.iflytek.astron.console.hub.event.BotPublishStatusChangedEvent;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -55,7 +46,7 @@ import java.util.stream.Collectors;
/**
* Bot Publishing Management Service Implementation
- *
+ *
* Unified bot publishing management service implementation, including: - Bot list query and detail
* retrieval - Publishing status management (publish/take offline) - Version management - Statistics
* data query
@@ -82,6 +73,12 @@ public class BotPublishServiceImpl implements BotPublishService {
// Statistics data related
private final BotConversationStatsMapper botConversationStatsMapper;
+ // MaaS API related
+ private final ChatBotApiMapper chatBotApiMapper;
+
+ @Value("${maas.appId}")
+ private String maasAppId;
+
@Override
public PageResponse getBotList(
BotListRequestDto requestDto,
@@ -143,7 +140,6 @@ public class BotPublishServiceImpl implements BotPublishService {
}
-
// ==================== MaaS Integration ====================
/**
@@ -229,7 +225,7 @@ public class BotPublishServiceImpl implements BotPublishService {
@Override
public BotTimeSeriesResponseDto getBotTimeSeriesStats(Integer botId, Integer overviewDays,
- String currentUid, Long currentSpaceId) {
+ String currentUid, Long currentSpaceId) {
log.info("Get bot time series statistics: botId={}, overviewDays={}, uid={}, spaceId={}",
botId, overviewDays, currentUid, currentSpaceId);
@@ -259,7 +255,7 @@ public class BotPublishServiceImpl implements BotPublishService {
@Override
public void recordDashboardCountLog(String uid, Long spaceId, Integer botId, Long chatId,
- String sid, Integer tokenConsumed) {
+ String sid, Integer tokenConsumed) {
log.info("Record conversation statistics: uid={}, spaceId={}, botId={}, chatId={}, tokenConsumed={}",
uid, spaceId, botId, chatId, tokenConsumed);
@@ -424,7 +420,7 @@ public class BotPublishServiceImpl implements BotPublishService {
@Override
public WechatAuthUrlResponseDto getWechatAuthUrl(Integer botId, String appid, String redirectUrl,
- String uid, Long spaceId) {
+ String uid, Long spaceId) {
log.info("Get WeChat authorization URL: botId={}, appid={}, redirectUrl={}, uid={}, spaceId={}",
botId, appid, redirectUrl, uid, spaceId);
--
Gitee
From 45c6a51bf47e403319d7d7773b619a481bd6f876 Mon Sep 17 00:00:00 2001
From: mingsuiyongheng <1653497380@qq.com>
Date: Wed, 15 Oct 2025 10:30:59 +0800
Subject: [PATCH 37/90] refactor: Remove unused BotPermissionUtil import and
related permission check code in Bot
---
.../console/hub/service/chat/impl/BotChatServiceImpl.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
index 15ef94a5..178baaad 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
@@ -32,7 +32,6 @@ import com.iflytek.astron.console.hub.service.SparkChatService;
import com.iflytek.astron.console.hub.service.chat.BotChatService;
import com.iflytek.astron.console.hub.service.chat.ChatListService;
import com.iflytek.astron.console.hub.service.knowledge.KnowledgeService;
-import com.iflytek.astron.console.hub.util.BotPermissionUtil;
import com.iflytek.astron.console.toolkit.entity.vo.CategoryTreeVO;
import com.iflytek.astron.console.toolkit.entity.vo.LLMInfoVo;
import com.iflytek.astron.console.toolkit.service.model.LLMService;
@@ -102,9 +101,6 @@ public class BotChatServiceImpl implements BotChatService {
@Autowired
private ReqKnowledgeRecordsDataService reqKnowledgeRecordsDataService;
- @Autowired
- private BotPermissionUtil botPermissionUtil;
-
/**
* Function to handle chat messages
*
@@ -346,7 +342,6 @@ public class BotChatServiceImpl implements BotChatService {
chatBotMarket.getModelId(),
chatBotMarket.getSupportDocument() == 1);
} else {
- botPermissionUtil.checkBot(botId);
ChatBotBase chatBotBase = chatBotDataService.findById(botId)
.orElseThrow(() -> new BusinessException(ResponseEnum.BOT_NOT_EXISTS));
return new BotConfiguration(
--
Gitee
From d0d41c065e75df0d3ca8186aa8e970c16a2bca83 Mon Sep 17 00:00:00 2001
From: yingpeng
Date: Wed, 15 Oct 2025 10:56:55 +0800
Subject: [PATCH 38/90] !93 feat: fix chat-debug * Merge branch 'bd_build' into
feature/console-bd-pr * Merge branch 'feature/console-bd' into
feature/console-bd-pr * Merge branch 'feature/chat' into feature/console-bd *
refactor: Remove unused BotPermissionUtil import and related permission check
code in Bot * Merge branch 'feature/console-bd' into feature/xinxiong2 *
Merge branch 'feature/chat' into feature/console-bd * Merge branch 'main'
into feature/chat * refactor: Optimize the permission check and model
configuration logic in BotChatServiceImpl, and update the unit tests to
verify the new behavior. * Update schema.sql * Update schema.sql * Merge pull
request #261 from sharphu/feature_db * Merge pull request #262 from
lyj715824/feature/cicd * Merge branch 'main' into feature_db * fix:update env
* chore: database project deployment file modification * Merge pull request
#260 from lyj715824/feature/cicd * fix:update ci process * Merge pull request
#258 from sharphu/feature_tenant * Merge pull request #259 from
zhubin4615/feature/update-docker-compose * chore: tenant project highly
complex problem repair * feat:update-docker-compose *
feat:update-docker-compose * Merge pull request #257 from
lyj715824/feature/cicd * Merge pull request #256 from sharphu/feature_tenant
* fix:update Go language check command * chore: unit test issue fixes * Merge
branch 'main' into feature_tenant * chore: code inspection issue fixes *
docs: Optimize deployment documentation * Merge remote-tracking branch
'office/main' into feature/cicd * cicd
---
.github/workflows/build-push.yml | 23 +-
.github/workflows/ci.yml | 18 +-
.../service/chat/impl/BotChatServiceImpl.java | 73 +++--
.../service/user/impl/UserBotServiceImpl.java | 1 -
.../chat/impl/BotChatServiceImplUnitTest.java | 8 +-
core/memory/database/Dockerfile | 6 +-
core/tenant/app/server.go | 8 +-
core/tenant/config/config.go | 2 +
core/tenant/config/config_test.go | 126 ++++----
core/tenant/config/env_loader.go | 103 ++++--
core/tenant/config/env_loader_test.go | 245 +++++++-------
core/tenant/config/loader_test.go | 5 +-
core/tenant/config/local_loader_test.go | 150 ++++-----
core/tenant/internal/dao/app_dao.go | 11 +-
core/tenant/internal/dao/app_dao_test.go | 301 +++++++-----------
core/tenant/internal/dao/auth_dao.go | 3 +-
core/tenant/internal/dao/auth_dao_test.go | 157 +++++----
core/tenant/internal/dao/base_test.go | 2 -
core/tenant/internal/handler/app_handler.go | 8 +-
.../internal/handler/app_handler_test.go | 90 ++----
core/tenant/internal/handler/auth_handler.go | 8 +-
.../internal/handler/auth_handler_test.go | 64 +---
core/tenant/internal/handler/req.go | 3 +-
core/tenant/internal/handler/resp_test.go | 90 +++---
core/tenant/internal/handler/router.go | 16 +-
core/tenant/internal/handler/router_test.go | 9 +-
core/tenant/internal/service/app_service.go | 55 ++--
.../internal/service/app_service_test.go | 66 ++--
core/tenant/internal/service/auth_service.go | 27 +-
.../internal/service/auth_service_test.go | 16 +-
core/tenant/main.go | 1 +
core/tenant/tools/database/database.go | 5 +-
core/tenant/tools/database/database_test.go | 11 +-
core/tenant/tools/generator/app.go | 3 +-
core/tenant/tools/generator/ip.go | 2 -
core/tenant/tools/generator/ip_test.go | 2 -
core/tenant/tools/generator/sid.go | 12 +-
core/tenant/tools/generator/sid_test.go | 112 ++++---
docker/astronAgent/.env.example | 14 +-
docker/astronAgent/README_zh.md | 205 ------------
docker/astronAgent/config/database/config.env | 2 +-
.../astronAgent/config/knowledge/config.env | 2 +-
docker/astronAgent/docker-compose.yaml | 13 +-
docker/astronAgent/mysql/schema.sql | 38 ++-
docs/DEPLOYMENT_GUIDE_zh.md | 48 ++-
.../xinghuo_rag_tool.html | 0
makefiles/go.mk | 6 +-
47 files changed, 945 insertions(+), 1225 deletions(-)
delete mode 100644 docker/astronAgent/README_zh.md
rename {docker/astronAgent => docs}/xinghuo_rag_tool.html (100%)
diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml
index 6f5f719f..f31fdba3 100644
--- a/.github/workflows/build-push.yml
+++ b/.github/workflows/build-push.yml
@@ -516,25 +516,11 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- # 添加Maven依赖缓存 - 仅用于console-hub
- - name: Cache Maven dependencies
- uses: actions/cache@v4
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-hub-${{ hashFiles('console/backend/hub/pom.xml', 'console/backend/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-hub-
- ${{ runner.os }}-maven-
-
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- with:
- driver-opts: |
- network=host
- image=moby/buildkit:master
- name: Login to GitHub Container Registry
if: needs.detect-and-prepare.outputs.should-push == 'true'
@@ -566,13 +552,8 @@ jobs:
VERSION=${{ needs.detect-and-prepare.outputs.version }}
GIT_COMMIT=${{ github.sha }}
BUILD_TIME=${{ github.run_id }}
- cache-from: |
- type=gha,scope=console-hub
- type=gha,scope=console-hub-build
- type=registry,ref=${{ env.REGISTRY_GHCR }}/${{ github.repository }}/console-hub:buildcache
- cache-to: |
- type=gha,scope=console-hub,mode=max
- type=registry,ref=${{ env.REGISTRY_GHCR }}/${{ github.repository }}/console-hub:buildcache,mode=max
+ cache-from: type=gha,scope=console-hub
+ cache-to: type=gha,scope=console-hub,mode=max
provenance: false
sbom: false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f12ecc7e..d483f8e4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -185,19 +185,29 @@ jobs:
;;
go)
echo "🔍 Running Go quality checks..."
- echo "🛠️ Installing golangci-lint..."
+ echo "🛠️ Installing Go tools..."
+ go install golang.org/x/tools/cmd/goimports@latest
+ go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
+ go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0
export PATH=$PATH:$(go env GOPATH)/bin
- echo "✨ Checking go fmt compliance..."
- UNFORMATTED=$(gofmt -l .) && \
+ echo "✨ Checking goimports compliance..."
+ UNFORMATTED=$(goimports -l .) && \
if [ -n "$UNFORMATTED" ]; then \
echo "Files that need formatting:" && \
echo "$UNFORMATTED" && \
- echo "Run 'go fmt ./...' to fix formatting issues." && \
+ echo "Run 'goimports -w .' to fix formatting issues." && \
exit 1; \
fi
echo "🔍 Running go vet..."
go vet ./...
+ echo "🔍 Running gocyclo..."
+ gocyclo -over 10 . || (echo "High cyclomatic complexity detected" && exit 1)
+ echo "🔍 Running staticcheck..."
+ PKGS=$(go list ./... 2>/dev/null)
+ if [ -n "$PKGS" ]; then \
+ staticcheck $PKGS || exit 1; \
+ fi
echo "📋 Running golangci-lint..."
golangci-lint run ./... --timeout=5m
;;
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
index 5a102276..178baaad 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
@@ -24,6 +24,7 @@ import com.iflytek.astron.console.commons.service.data.ChatListDataService;
import com.iflytek.astron.console.commons.service.workflow.WorkflowBotChatService;
import com.iflytek.astron.console.commons.util.I18nUtil;
import com.iflytek.astron.console.commons.util.SseEmitterUtil;
+import com.iflytek.astron.console.commons.util.space.SpaceInfoUtil;
import com.iflytek.astron.console.hub.data.ReqKnowledgeRecordsDataService;
import com.iflytek.astron.console.hub.entity.ReqKnowledgeRecords;
import com.iflytek.astron.console.hub.service.PromptChatService;
@@ -33,6 +34,7 @@ import com.iflytek.astron.console.hub.service.chat.ChatListService;
import com.iflytek.astron.console.hub.service.knowledge.KnowledgeService;
import com.iflytek.astron.console.toolkit.entity.vo.CategoryTreeVO;
import com.iflytek.astron.console.toolkit.entity.vo.LLMInfoVo;
+import com.iflytek.astron.console.toolkit.service.model.LLMService;
import com.iflytek.astron.console.toolkit.service.model.ModelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -136,9 +138,6 @@ public class BotChatServiceImpl implements BotChatService {
}
} else {
ModelConfigResult modelConfig = getModelConfiguration(botConfig.modelId, sseEmitter);
- if (modelConfig == null) {
- return;
- }
List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, modelConfig.maxInputTokens(), chatReqRecords.getId());
JSONObject jsonObject = buildPromptChatRequest(modelConfig.llmInfoVo(), messages);
promptChatService.chatStream(jsonObject, sseEmitter, sseId, chatReqRecords, false, false);
@@ -218,37 +217,44 @@ public class BotChatServiceImpl implements BotChatService {
*/
@Override
public void debugChatMessageBot(String text, String prompt, List messages, String uid, String openedTool, String model, Long modelId, List maasDatasetList, SseEmitter sseEmitter, String sseId) {
- int maxInputTokens = this.maxInputTokens;
- List messageList;
-
- if (modelId == null) {
- if (model.equals(DefaultBotModelEnum.X1.getDomain()) || model.equals(DefaultBotModelEnum.SPARK_4_0.getDomain())) {
- messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
- SparkChatRequest sparkChatRequest = new SparkChatRequest();
- sparkChatRequest.setModel(model);
- sparkChatRequest.setMessages(messageList);
- sparkChatRequest.setChatId(null);
- sparkChatRequest.setUserId(uid);
- sparkChatRequest.setEnableWebSearch(enableWebSearch(openedTool));
- sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, null, false, true);
+ try {
+ int maxInputTokens = this.maxInputTokens;
+ List messageList;
+
+ if (modelId == null) {
+ if (model.equals(DefaultBotModelEnum.X1.getDomain()) || model.equals(DefaultBotModelEnum.SPARK_4_0.getDomain())) {
+ messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
+ SparkChatRequest sparkChatRequest = new SparkChatRequest();
+ sparkChatRequest.setModel(model);
+ sparkChatRequest.setMessages(messageList);
+ sparkChatRequest.setChatId(null);
+ sparkChatRequest.setUserId(uid);
+ sparkChatRequest.setEnableWebSearch(enableWebSearch(openedTool));
+ sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, null, false, true);
+ } else {
+ maxInputTokens = 32000;
+ messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
+ LLMInfoVo llmInfoVo = new LLMInfoVo();
+ llmInfoVo.setUrl(deepseekUrl);
+ llmInfoVo.setApiKey(deepseekApiKey);
+ llmInfoVo.setDomain(model);
+ JSONObject jsonObject = buildPromptChatRequest(llmInfoVo, messageList);
+ promptChatService.chatStream(jsonObject, sseEmitter, sseId, null, false, true);
+ }
} else {
- maxInputTokens = 32000;
- messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
- LLMInfoVo llmInfoVo = new LLMInfoVo();
- llmInfoVo.setUrl(deepseekUrl);
- llmInfoVo.setApiKey(deepseekApiKey);
- llmInfoVo.setDomain(model);
- JSONObject jsonObject = buildPromptChatRequest(llmInfoVo, messageList);
+ ModelConfigResult modelConfig = getModelConfiguration(modelId, sseEmitter);
+ messageList = buildDebugMessageList(text, prompt, messages, modelConfig.maxInputTokens(), maasDatasetList);
+ Long spaceId = SpaceInfoUtil.getSpaceId();
+ if (!modelService.checkModelBase(LLMService.generate9DigitRandomFromId(modelConfig.llmInfoVo.getLlmId()),
+ modelConfig.llmInfoVo().getServiceId(), modelConfig.llmInfoVo.getUrl(), uid, spaceId)) {
+ throw new BusinessException(ResponseEnum.MODEL_CHECK_FAILED);
+ }
+ JSONObject jsonObject = buildPromptChatRequest(modelConfig.llmInfoVo(), messageList);
promptChatService.chatStream(jsonObject, sseEmitter, sseId, null, false, true);
}
- } else {
- ModelConfigResult modelConfig = getModelConfiguration(modelId, sseEmitter);
- if (modelConfig == null) {
- return;
- }
- messageList = buildDebugMessageList(text, prompt, messages, modelConfig.maxInputTokens(), maasDatasetList);
- JSONObject jsonObject = buildPromptChatRequest(modelConfig.llmInfoVo(), messageList);
- promptChatService.chatStream(jsonObject, sseEmitter, sseId, null, false, false);
+ } catch (Exception e) {
+ log.error("Bot debug error for sseId: {}, uid: {}", sseId, uid, e);
+ SseEmitterUtil.completeWithError(sseEmitter, "Failed to process chat request: " + e.getMessage());
}
}
@@ -300,8 +306,7 @@ public class BotChatServiceImpl implements BotChatService {
private ModelConfigResult getModelConfiguration(Long modelId, SseEmitter sseEmitter) {
LLMInfoVo llmInfoVo = (LLMInfoVo) modelService.getDetail(0, modelId, null).data();
if (llmInfoVo == null) {
- SseEmitterUtil.completeWithError(sseEmitter, "Model is not exist!");
- return null;
+ throw new BusinessException(ResponseEnum.MODEL_NOT_EXIST);
}
int maxInputTokens = this.maxInputTokens;
@@ -324,7 +329,7 @@ public class BotChatServiceImpl implements BotChatService {
* @param botId Bot ID
* @return Returns BotConfiguration object
*/
- private BotConfiguration getBotConfiguration(Integer botId) {
+ private BotConfiguration getBotConfiguration(Integer botId) throws BusinessException{
ChatBotMarket chatBotMarket = chatBotDataService.findMarketBotByBotId(botId);
if (chatBotMarket != null && ShelfStatusEnum.isOnShelf(chatBotMarket.getBotStatus())) {
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/user/impl/UserBotServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/user/impl/UserBotServiceImpl.java
index 74432c7f..ce59d6e1 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/user/impl/UserBotServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/user/impl/UserBotServiceImpl.java
@@ -28,7 +28,6 @@ import com.iflytek.astron.console.hub.service.wechat.BotOffiaccountService;
import com.iflytek.astron.console.hub.service.chat.ChatBotApiService;
import com.iflytek.astron.console.hub.service.user.UserBotService;
import com.iflytek.astron.console.hub.util.BotPermissionUtil;
-import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
diff --git a/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImplUnitTest.java b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImplUnitTest.java
index 70e3bd3f..b5f1c2ba 100644
--- a/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImplUnitTest.java
+++ b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImplUnitTest.java
@@ -68,6 +68,8 @@ class BotChatServiceImplUnitTest {
private PromptChatService promptChatService;
@Mock
private ReqKnowledgeRecordsDataService reqKnowledgeRecordsDataService;
+ @Mock
+ private com.iflytek.astron.console.hub.util.BotPermissionUtil botPermissionUtil;
@InjectMocks
private BotChatServiceImpl botChatService;
@@ -233,6 +235,7 @@ class BotChatServiceImplUnitTest {
botChatService.chatMessageBot(chatBotReqDto, sseEmitter, sseId, null, null);
// Then
+ verify(chatBotDataService).findMarketBotByBotId(eq(chatBotReqDto.getBotId()));
verify(chatBotDataService).findById(eq(chatBotReqDto.getBotId()));
verify(sparkChatService).chatStream(any(SparkChatRequest.class), eq(sseEmitter), eq(sseId), any(), eq(false), eq(false));
}
@@ -245,7 +248,7 @@ class BotChatServiceImplUnitTest {
String sseId = "test-sse-id";
when(chatBotDataService.findMarketBotByBotId(anyInt())).thenReturn(null);
- when(chatBotDataService.findById(anyInt())).thenReturn(Optional.empty());
+ lenient().when(chatBotDataService.findById(anyInt())).thenReturn(Optional.empty());
// When & Then
assertDoesNotThrow(() -> botChatService.chatMessageBot(chatBotReqDto, sseEmitter, sseId, null, null));
@@ -333,6 +336,7 @@ class BotChatServiceImplUnitTest {
LLMInfoVo llmInfoVo = createLLMInfoVo();
when(modelService.getDetail(anyInt(), anyLong(), any())).thenReturn(new ApiResult<>(0, "success", llmInfoVo, 1L));
+ when(modelService.checkModelBase(anyLong(), anyString(), anyString(), anyString(), anyLong())).thenReturn(true);
when(knowledgeService.getChuncks(any(), anyString(), anyInt(), anyBoolean())).thenReturn(Arrays.asList("knowledge"));
doNothing().when(promptChatService).chatStream(any(), any(), any(), any(), anyBoolean(), anyBoolean());
@@ -341,7 +345,7 @@ class BotChatServiceImplUnitTest {
// Then
verify(modelService).getDetail(eq(0), eq(modelId), isNull());
- verify(promptChatService).chatStream(any(JSONObject.class), eq(sseEmitter), eq(sseId), isNull(), eq(false), eq(false));
+ verify(promptChatService).chatStream(any(JSONObject.class), eq(sseEmitter), eq(sseId), isNull(), eq(false), eq(true));
verify(sparkChatService, never()).chatStream(any(), any(), any(), any(), anyBoolean(), anyBoolean());
}
diff --git a/core/memory/database/Dockerfile b/core/memory/database/Dockerfile
index 66b0a066..3d3221fd 100644
--- a/core/memory/database/Dockerfile
+++ b/core/memory/database/Dockerfile
@@ -1,9 +1,9 @@
FROM python:3.11-slim
-WORKDIR /opt/database
+WORKDIR /opt/core
-ENV PATH=$PATH:/opt/database
-ENV PYTHONPATH /opt/database
+ENV PATH=$PATH:/opt/core
+ENV PYTHONPATH /opt/core
ENV UV_NO_CACHE=1
RUN pip install uv --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple/
diff --git a/core/tenant/app/server.go b/core/tenant/app/server.go
index afa0afc1..b0bc3de0 100644
--- a/core/tenant/app/server.go
+++ b/core/tenant/app/server.go
@@ -4,17 +4,19 @@ import (
"context"
"flag"
"fmt"
- "github.com/gin-gonic/gin"
"log"
"math/rand"
"net/http"
"os"
"os/signal"
"syscall"
+ "time"
+
"tenant/config"
"tenant/internal/handler"
"tenant/tools/generator"
- "time"
+
+ "github.com/gin-gonic/gin"
)
func Run() error {
@@ -75,7 +77,7 @@ func initLog(cfg *config.Config) error {
if len(cfg.Log.LogFile) == 0 {
cfg.Log.LogFile = "./logs/app.log"
}
- file, err := os.OpenFile(cfg.Log.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
+ file, err := os.OpenFile(cfg.Log.LogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
log.Fatalf("open log file failed: %v", err)
return err
diff --git a/core/tenant/config/config.go b/core/tenant/config/config.go
index 30c5fee9..8427edfd 100644
--- a/core/tenant/config/config.go
+++ b/core/tenant/config/config.go
@@ -25,6 +25,7 @@ type Config struct {
func (c *Config) String() string {
return fmt.Sprintf("Config{Server: %v, DataBase: %v, Log: %v}", c.Server, c.DataBase, c.Log)
}
+
func (c *Config) Validate() error {
if c.Server.Port == 0 {
return fmt.Errorf("server port is required")
@@ -46,6 +47,7 @@ func (c *Config) Validate() error {
}
return nil
}
+
func LoadConfig(path string) (*Config, error) {
cfg := &Config{}
// load config from local file
diff --git a/core/tenant/config/config_test.go b/core/tenant/config/config_test.go
index 27f372eb..032345e1 100644
--- a/core/tenant/config/config_test.go
+++ b/core/tenant/config/config_test.go
@@ -243,17 +243,60 @@ func TestConfig_Validate(t *testing.T) {
}
}
+type loadConfigTestCase struct {
+ name string
+ configFile string
+ envVars map[string]string
+ wantErr bool
+ validate func(t *testing.T, cfg *Config)
+}
+
+func createValidatorForConfigWithEnvOverride(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkLoadConfigField(t, "Server.Port", cfg.Server.Port, 9090)
+ checkLoadConfigField(t, "Server.Location", cfg.Server.Location, "us-east")
+ checkLoadConfigField(t, "DataBase.DBType", cfg.DataBase.DBType, "mysql")
+ }
+}
+
+func createValidatorForEnvOnlyConfig(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkLoadConfigField(t, "Server.Port", cfg.Server.Port, 8080)
+ checkLoadConfigField(t, "DataBase.DBType", cfg.DataBase.DBType, "postgresql")
+ checkLoadConfigField(t, "DataBase.MaxOpenConns", cfg.DataBase.MaxOpenConns, 20)
+ }
+}
+
+func checkLoadConfigField(t *testing.T, fieldName string, actual, expected interface{}) {
+ if actual != expected {
+ t.Errorf("Expected %s %v, got %v", fieldName, expected, actual)
+ }
+}
+
+func setupTestEnvironment(envVars map[string]string) (func(), map[string]string) {
+ originalEnv := make(map[string]string)
+ for key, value := range envVars {
+ originalEnv[key] = os.Getenv(key)
+ _ = os.Setenv(key, value)
+ }
+
+ cleanup := func() {
+ for key := range envVars {
+ if originalVal, exists := originalEnv[key]; exists {
+ _ = os.Setenv(key, originalVal)
+ } else {
+ _ = os.Unsetenv(key)
+ }
+ }
+ }
+
+ return cleanup, originalEnv
+}
+
func TestLoadConfig(t *testing.T) {
- // Create temporary directory for test files
tempDir := t.TempDir()
- tests := []struct {
- name string
- configFile string
- envVars map[string]string
- wantErr bool
- validate func(*Config) error
- }{
+ tests := []loadConfigTestCase{
{
name: "valid config file with env override",
configFile: `
@@ -273,21 +316,10 @@ maxIdleConns = 5
path = "/tmp/test.log"
`,
envVars: map[string]string{
- "SERVICE_PORT": "9090", // Override port via env
- },
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 9090 {
- t.Errorf("Expected port 9090 (env override), got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "us-east" {
- t.Errorf("Expected location 'us-east', got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "mysql" {
- t.Errorf("Expected dbType 'mysql', got %s", cfg.DataBase.DBType)
- }
- return nil
+ "SERVICE_PORT": "9090",
},
+ wantErr: false,
+ validate: createValidatorForConfigWithEnvOverride(t),
},
{
name: "invalid config - missing required fields",
@@ -297,10 +329,8 @@ port = 8080
[database]
dbType = "mysql"
-# Missing required fields like username, password, url
[log]
-# Missing path
`,
wantErr: true,
},
@@ -319,50 +349,22 @@ dbType = "mysql"
"DATABASE_MAX_IDLE_CONNS": "10",
"LOG_PATH": "/env/log/path.log",
},
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 8080 {
- t.Errorf("Expected port 8080, got %d", cfg.Server.Port)
- }
- if cfg.DataBase.DBType != "postgresql" {
- t.Errorf("Expected dbType 'postgresql', got %s", cfg.DataBase.DBType)
- }
- if cfg.DataBase.MaxOpenConns != 20 {
- t.Errorf("Expected MaxOpenConns 20, got %d", cfg.DataBase.MaxOpenConns)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForEnvOnlyConfig(t),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- // Create config file
configPath := filepath.Join(tempDir, "config.toml")
- err := os.WriteFile(configPath, []byte(tt.configFile), 0644)
+ err := os.WriteFile(configPath, []byte(tt.configFile), 0o644)
if err != nil {
t.Fatalf("Failed to write test config file: %v", err)
}
- // Set environment variables
- originalEnv := make(map[string]string)
- for key, value := range tt.envVars {
- originalEnv[key] = os.Getenv(key)
- os.Setenv(key, value)
- }
-
- // Cleanup environment variables after test
- defer func() {
- for key := range tt.envVars {
- if originalVal, exists := originalEnv[key]; exists {
- os.Setenv(key, originalVal)
- } else {
- os.Unsetenv(key)
- }
- }
- }()
+ cleanup, _ := setupTestEnvironment(tt.envVars)
+ defer cleanup()
- // Test LoadConfig
cfg, err := LoadConfig(configPath)
if (err != nil) != tt.wantErr {
@@ -376,11 +378,8 @@ dbType = "mysql"
return
}
- // Run custom validation if provided
if tt.validate != nil {
- if err := tt.validate(cfg); err != nil {
- t.Errorf("Custom validation failed: %v", err)
- }
+ tt.validate(t, cfg)
}
}
})
@@ -403,18 +402,17 @@ func TestLoadConfig_FileNotFound(t *testing.T) {
// Set environment variables
for key, value := range envVars {
- os.Setenv(key, value)
+ _ = os.Setenv(key, value)
}
// Cleanup
defer func() {
for key := range envVars {
- os.Unsetenv(key)
+ _ = os.Unsetenv(key)
}
}()
cfg, err := LoadConfig(nonExistentPath)
-
// Should not error if env vars provide all required config
if err != nil {
t.Errorf("LoadConfig() with non-existent file but valid env vars should not error: %v", err)
diff --git a/core/tenant/config/env_loader.go b/core/tenant/config/env_loader.go
index 0d32121e..b003be65 100644
--- a/core/tenant/config/env_loader.go
+++ b/core/tenant/config/env_loader.go
@@ -5,50 +5,91 @@ import (
"os"
)
-type EnvLoader struct {
-}
+type EnvLoader struct{}
func NewEnvLoader() *EnvLoader {
return &EnvLoader{}
}
+type envMapping struct {
+ envKey string
+ setValue func(*Config, string) error
+}
+
func (l *EnvLoader) Load(cfg *Config) error {
- // No operation, as environment variables are accessed directly in the application.
- if v := os.Getenv("SERVICE_PORT"); v != "" {
- if n, err := fmt.Sscanf(v, "%d", &cfg.Server.Port); err != nil || n != 1 {
- return fmt.Errorf("invalid SERVICE_PORT: %v", err)
- }
- }
- if v := os.Getenv("SERVICE_LOCATION"); v != "" {
- cfg.Server.Location = v
+ mappings := []envMapping{
+ {"SERVICE_PORT", l.setServicePort},
+ {"SERVICE_LOCATION", l.setServiceLocation},
+ {"DATABASE_DB_TYPE", l.setDatabaseDBType},
+ {"DATABASE_USERNAME", l.setDatabaseUsername},
+ {"DATABASE_PASSWORD", l.setDatabasePassword},
+ {"DATABASE_URL", l.setDatabaseURL},
+ {"DATABASE_MAX_OPEN_CONNS", l.setDatabaseMaxOpenConns},
+ {"DATABASE_MAX_IDLE_CONNS", l.setDatabaseMaxIdleConns},
+ {"LOG_PATH", l.setLogPath},
}
- if v := os.Getenv("DATABASE_DB_TYPE"); v != "" {
- cfg.DataBase.DBType = v
- }
- if v := os.Getenv("DATABASE_USERNAME"); v != "" {
- cfg.DataBase.UserName = v
- }
- if v := os.Getenv("DATABASE_PASSWORD"); v != "" {
- cfg.DataBase.Password = v
- }
- if v := os.Getenv("DATABASE_URL"); v != "" {
- cfg.DataBase.Url = v
- }
- if v := os.Getenv("DATABASE_MAX_OPEN_CONNS"); v != "" {
- if n, err := fmt.Sscanf(v, "%d", &cfg.DataBase.MaxOpenConns); err != nil || n != 1 {
- return fmt.Errorf("invalid DATABASE_MAX_OPEN_CONNS: %v", err)
+
+ for _, mapping := range mappings {
+ if value := os.Getenv(mapping.envKey); value != "" {
+ if err := mapping.setValue(cfg, value); err != nil {
+ return err
+ }
}
}
- if v := os.Getenv("DATABASE_MAX_IDLE_CONNS"); v != "" {
- if n, err := fmt.Sscanf(v, "%d", &cfg.DataBase.MaxIdleConns); err != nil || n != 1 {
- return fmt.Errorf("invalid DATABASE_MAX_IDLE_CONNS: %v", err)
- }
+
+ return nil
+}
+
+func (l *EnvLoader) setServicePort(cfg *Config, value string) error {
+ if n, err := fmt.Sscanf(value, "%d", &cfg.Server.Port); err != nil || n != 1 {
+ return fmt.Errorf("invalid SERVICE_PORT: %v", err)
+ }
+ return nil
+}
+
+func (l *EnvLoader) setServiceLocation(cfg *Config, value string) error {
+ cfg.Server.Location = value
+ return nil
+}
+
+func (l *EnvLoader) setDatabaseDBType(cfg *Config, value string) error {
+ cfg.DataBase.DBType = value
+ return nil
+}
+
+func (l *EnvLoader) setDatabaseUsername(cfg *Config, value string) error {
+ cfg.DataBase.UserName = value
+ return nil
+}
+
+func (l *EnvLoader) setDatabasePassword(cfg *Config, value string) error {
+ cfg.DataBase.Password = value
+ return nil
+}
+
+func (l *EnvLoader) setDatabaseURL(cfg *Config, value string) error {
+ cfg.DataBase.Url = value
+ return nil
+}
+
+func (l *EnvLoader) setDatabaseMaxOpenConns(cfg *Config, value string) error {
+ if n, err := fmt.Sscanf(value, "%d", &cfg.DataBase.MaxOpenConns); err != nil || n != 1 {
+ return fmt.Errorf("invalid DATABASE_MAX_OPEN_CONNS: %v", err)
}
- if v := os.Getenv("LOG_PATH"); v != "" {
- cfg.Log.LogFile = v
+ return nil
+}
+
+func (l *EnvLoader) setDatabaseMaxIdleConns(cfg *Config, value string) error {
+ if n, err := fmt.Sscanf(value, "%d", &cfg.DataBase.MaxIdleConns); err != nil || n != 1 {
+ return fmt.Errorf("invalid DATABASE_MAX_IDLE_CONNS: %v", err)
}
return nil
}
+func (l *EnvLoader) setLogPath(cfg *Config, value string) error {
+ cfg.Log.LogFile = value
+ return nil
+}
+
func (l *EnvLoader) Watch(cfg *Config, onChange func()) {
}
diff --git a/core/tenant/config/env_loader_test.go b/core/tenant/config/env_loader_test.go
index a9b50a02..20e3c1f3 100644
--- a/core/tenant/config/env_loader_test.go
+++ b/core/tenant/config/env_loader_test.go
@@ -18,13 +18,104 @@ func TestNewEnvLoader(t *testing.T) {
}
}
+type envTestCase struct {
+ name string
+ envVars map[string]string
+ wantErr bool
+ validate func(t *testing.T, cfg *Config)
+}
+
+func createValidatorForAllEnvVars(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ expectations := map[string]interface{}{
+ "Server.Port": 8080,
+ "Server.Location": "us-west",
+ "DataBase.DBType": "mysql",
+ "DataBase.UserName": "envuser",
+ "DataBase.Password": "envpass",
+ "DataBase.Url": "localhost:3306/envdb",
+ "DataBase.MaxOpenConns": 15,
+ "DataBase.MaxIdleConns": 8,
+ "Log.LogFile": "/var/log/env.log",
+ }
+
+ checkField(t, "Server.Port", cfg.Server.Port, expectations["Server.Port"])
+ checkField(t, "Server.Location", cfg.Server.Location, expectations["Server.Location"])
+ checkField(t, "DataBase.DBType", cfg.DataBase.DBType, expectations["DataBase.DBType"])
+ checkField(t, "DataBase.UserName", cfg.DataBase.UserName, expectations["DataBase.UserName"])
+ checkField(t, "DataBase.Password", cfg.DataBase.Password, expectations["DataBase.Password"])
+ checkField(t, "DataBase.Url", cfg.DataBase.Url, expectations["DataBase.Url"])
+ checkField(t, "DataBase.MaxOpenConns", cfg.DataBase.MaxOpenConns, expectations["DataBase.MaxOpenConns"])
+ checkField(t, "DataBase.MaxIdleConns", cfg.DataBase.MaxIdleConns, expectations["DataBase.MaxIdleConns"])
+ checkField(t, "Log.LogFile", cfg.Log.LogFile, expectations["Log.LogFile"])
+ }
+}
+
+func createValidatorForNoEnvVars(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkField(t, "Server.Port", cfg.Server.Port, 0)
+ checkField(t, "Server.Location", cfg.Server.Location, "")
+ checkField(t, "DataBase.DBType", cfg.DataBase.DBType, "")
+ }
+}
+
+func createValidatorForPartialEnvVars(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkField(t, "Server.Port", cfg.Server.Port, 9090)
+ checkField(t, "DataBase.DBType", cfg.DataBase.DBType, "postgresql")
+ checkField(t, "Log.LogFile", cfg.Log.LogFile, "/tmp/partial.log")
+ checkField(t, "Server.Location", cfg.Server.Location, "")
+ checkField(t, "DataBase.UserName", cfg.DataBase.UserName, "")
+ }
+}
+
+func createValidatorForEmptyEnvVars(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkField(t, "Server.Port", cfg.Server.Port, 0)
+ checkField(t, "Server.Location", cfg.Server.Location, "")
+ checkField(t, "DataBase.DBType", cfg.DataBase.DBType, "")
+ }
+}
+
+func checkField(t *testing.T, fieldName string, actual, expected interface{}) {
+ if actual != expected {
+ t.Errorf("Expected %s %v, got %v", fieldName, expected, actual)
+ }
+}
+
+func setupEnvironment(envVars map[string]string) (func(), []string) {
+ envKeys := []string{
+ "SERVICE_PORT", "SERVICE_LOCATION",
+ "DATABASE_DB_TYPE", "DATABASE_USERNAME", "DATABASE_PASSWORD",
+ "DATABASE_URL", "DATABASE_MAX_OPEN_CONNS", "DATABASE_MAX_IDLE_CONNS",
+ "LOG_PATH",
+ }
+
+ originalEnv := make(map[string]string)
+ for _, key := range envKeys {
+ originalEnv[key] = os.Getenv(key)
+ _ = os.Unsetenv(key)
+ }
+
+ for key, value := range envVars {
+ _ = os.Setenv(key, value)
+ }
+
+ cleanup := func() {
+ for _, key := range envKeys {
+ if originalVal, exists := originalEnv[key]; exists {
+ _ = os.Setenv(key, originalVal)
+ } else {
+ _ = os.Unsetenv(key)
+ }
+ }
+ }
+
+ return cleanup, envKeys
+}
+
func TestEnvLoader_Load(t *testing.T) {
- tests := []struct {
- name string
- envVars map[string]string
- wantErr bool
- validate func(*Config) error
- }{
+ tests := []envTestCase{
{
name: "all environment variables set",
envVars: map[string]string{
@@ -38,55 +129,14 @@ func TestEnvLoader_Load(t *testing.T) {
"DATABASE_MAX_IDLE_CONNS": "8",
"LOG_PATH": "/var/log/env.log",
},
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 8080 {
- t.Errorf("Expected port 8080, got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "us-west" {
- t.Errorf("Expected location 'us-west', got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "mysql" {
- t.Errorf("Expected dbType 'mysql', got %s", cfg.DataBase.DBType)
- }
- if cfg.DataBase.UserName != "envuser" {
- t.Errorf("Expected username 'envuser', got %s", cfg.DataBase.UserName)
- }
- if cfg.DataBase.Password != "envpass" {
- t.Errorf("Expected password 'envpass', got %s", cfg.DataBase.Password)
- }
- if cfg.DataBase.Url != "localhost:3306/envdb" {
- t.Errorf("Expected url 'localhost:3306/envdb', got %s", cfg.DataBase.Url)
- }
- if cfg.DataBase.MaxOpenConns != 15 {
- t.Errorf("Expected MaxOpenConns 15, got %d", cfg.DataBase.MaxOpenConns)
- }
- if cfg.DataBase.MaxIdleConns != 8 {
- t.Errorf("Expected MaxIdleConns 8, got %d", cfg.DataBase.MaxIdleConns)
- }
- if cfg.Log.LogFile != "/var/log/env.log" {
- t.Errorf("Expected log file '/var/log/env.log', got %s", cfg.Log.LogFile)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForAllEnvVars(t),
},
{
- name: "no environment variables set",
- envVars: map[string]string{},
- wantErr: false,
- validate: func(cfg *Config) error {
- // All fields should remain at zero values
- if cfg.Server.Port != 0 {
- t.Errorf("Expected port 0, got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "" {
- t.Errorf("Expected empty location, got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "" {
- t.Errorf("Expected empty dbType, got %s", cfg.DataBase.DBType)
- }
- return nil
- },
+ name: "no environment variables set",
+ envVars: map[string]string{},
+ wantErr: false,
+ validate: createValidatorForNoEnvVars(t),
},
{
name: "partial environment variables set",
@@ -95,26 +145,8 @@ func TestEnvLoader_Load(t *testing.T) {
"DATABASE_DB_TYPE": "postgresql",
"LOG_PATH": "/tmp/partial.log",
},
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 9090 {
- t.Errorf("Expected port 9090, got %d", cfg.Server.Port)
- }
- if cfg.DataBase.DBType != "postgresql" {
- t.Errorf("Expected dbType 'postgresql', got %s", cfg.DataBase.DBType)
- }
- if cfg.Log.LogFile != "/tmp/partial.log" {
- t.Errorf("Expected log file '/tmp/partial.log', got %s", cfg.Log.LogFile)
- }
- // Unset variables should remain empty/zero
- if cfg.Server.Location != "" {
- t.Errorf("Expected empty location, got %s", cfg.Server.Location)
- }
- if cfg.DataBase.UserName != "" {
- t.Errorf("Expected empty username, got %s", cfg.DataBase.UserName)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForPartialEnvVars(t),
},
{
name: "invalid SERVICE_PORT",
@@ -144,56 +176,16 @@ func TestEnvLoader_Load(t *testing.T) {
"SERVICE_LOCATION": "",
"DATABASE_DB_TYPE": "",
},
- wantErr: false,
- validate: func(cfg *Config) error {
- // Empty strings should not override zero values
- if cfg.Server.Port != 0 {
- t.Errorf("Expected port 0 (empty env var), got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "" {
- t.Errorf("Expected empty location (empty env var), got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "" {
- t.Errorf("Expected empty dbType (empty env var), got %s", cfg.DataBase.DBType)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForEmptyEnvVars(t),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- // Store original environment values
- originalEnv := make(map[string]string)
- envKeys := []string{
- "SERVICE_PORT", "SERVICE_LOCATION",
- "DATABASE_DB_TYPE", "DATABASE_USERNAME", "DATABASE_PASSWORD",
- "DATABASE_URL", "DATABASE_MAX_OPEN_CONNS", "DATABASE_MAX_IDLE_CONNS",
- "LOG_PATH",
- }
-
- for _, key := range envKeys {
- originalEnv[key] = os.Getenv(key)
- os.Unsetenv(key) // Clear environment first
- }
-
- // Set test environment variables
- for key, value := range tt.envVars {
- os.Setenv(key, value)
- }
+ cleanup, _ := setupEnvironment(tt.envVars)
+ defer cleanup()
- // Cleanup environment after test
- defer func() {
- for _, key := range envKeys {
- if originalVal, exists := originalEnv[key]; exists {
- os.Setenv(key, originalVal)
- } else {
- os.Unsetenv(key)
- }
- }
- }()
-
- // Test EnvLoader.Load
loader := NewEnvLoader()
cfg := &Config{}
err := loader.Load(cfg)
@@ -204,9 +196,7 @@ func TestEnvLoader_Load(t *testing.T) {
}
if !tt.wantErr && tt.validate != nil {
- if err := tt.validate(cfg); err != nil {
- t.Errorf("Config validation failed: %v", err)
- }
+ tt.validate(t, cfg)
}
})
}
@@ -284,19 +274,19 @@ func TestEnvLoader_Load_IntegerParsing(t *testing.T) {
originalEnv := make(map[string]string)
for _, key := range envKeys {
originalEnv[key] = os.Getenv(key)
- os.Unsetenv(key)
+ _ = os.Unsetenv(key)
}
// Set the specific test environment variable
- os.Setenv(tt.envVar, tt.value)
+ _ = os.Setenv(tt.envVar, tt.value)
// Cleanup
defer func() {
for _, key := range envKeys {
if originalVal, exists := originalEnv[key]; exists {
- os.Setenv(key, originalVal)
+ _ = os.Setenv(key, originalVal)
} else {
- os.Unsetenv(key)
+ _ = os.Unsetenv(key)
}
}
}()
@@ -369,18 +359,17 @@ func TestEnvLoader_LoadPreservesExistingConfig(t *testing.T) {
// Set only one environment variable
originalPort := os.Getenv("SERVICE_PORT")
- os.Setenv("SERVICE_PORT", "8080")
+ _ = os.Setenv("SERVICE_PORT", "8080")
defer func() {
if originalPort != "" {
- os.Setenv("SERVICE_PORT", originalPort)
+ _ = os.Setenv("SERVICE_PORT", originalPort)
} else {
- os.Unsetenv("SERVICE_PORT")
+ _ = os.Unsetenv("SERVICE_PORT")
}
}()
loader := NewEnvLoader()
err := loader.Load(cfg)
-
if err != nil {
t.Errorf("EnvLoader.Load() should not error: %v", err)
}
diff --git a/core/tenant/config/loader_test.go b/core/tenant/config/loader_test.go
index ea5898c2..1399f765 100644
--- a/core/tenant/config/loader_test.go
+++ b/core/tenant/config/loader_test.go
@@ -47,7 +47,7 @@ func TestConfLoader_Interface(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test that the loader implements the interface
- var _ ConfLoader = tt.loader
+ _ = tt.loader
// Test that interface methods can be called
cfg := &Config{}
@@ -78,6 +78,7 @@ func TestConfLoader_Methods(t *testing.T) {
// Test that we receive a Config pointer
if cfg == nil {
t.Errorf("Load method should receive non-nil Config pointer")
+ return nil
}
// Test that we can modify the config
@@ -88,7 +89,6 @@ func TestConfLoader_Methods(t *testing.T) {
cfg := &Config{}
err := loader.Load(cfg)
-
if err != nil {
t.Errorf("MockLoader.Load() should not return error: %v", err)
}
@@ -237,7 +237,6 @@ func TestConfLoader_InterfaceDocumentation(t *testing.T) {
cfg := &Config{}
err := loader.Load(cfg)
-
if err != nil {
t.Errorf("Load should not return error: %v", err)
}
diff --git a/core/tenant/config/local_loader_test.go b/core/tenant/config/local_loader_test.go
index 782f23f1..b4598bbe 100644
--- a/core/tenant/config/local_loader_test.go
+++ b/core/tenant/config/local_loader_test.go
@@ -45,16 +45,67 @@ func TestNewLocalLoader(t *testing.T) {
}
}
+type localTestCase struct {
+ name string
+ configFile string
+ wantErr bool
+ validate func(t *testing.T, cfg *Config)
+}
+
+func createValidatorForValidTOML(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ expectations := map[string]interface{}{
+ "Server.Port": 8080,
+ "Server.Location": "us-east",
+ "DataBase.DBType": "mysql",
+ "DataBase.UserName": "testuser",
+ "DataBase.Password": "testpass",
+ "DataBase.Url": "localhost:3306/testdb",
+ "DataBase.MaxOpenConns": 10,
+ "DataBase.MaxIdleConns": 5,
+ "Log.LogFile": "/tmp/test.log",
+ }
+
+ checkConfigField(t, "Server.Port", cfg.Server.Port, expectations["Server.Port"])
+ checkConfigField(t, "Server.Location", cfg.Server.Location, expectations["Server.Location"])
+ checkConfigField(t, "DataBase.DBType", cfg.DataBase.DBType, expectations["DataBase.DBType"])
+ checkConfigField(t, "DataBase.UserName", cfg.DataBase.UserName, expectations["DataBase.UserName"])
+ checkConfigField(t, "DataBase.Password", cfg.DataBase.Password, expectations["DataBase.Password"])
+ checkConfigField(t, "DataBase.Url", cfg.DataBase.Url, expectations["DataBase.Url"])
+ checkConfigField(t, "DataBase.MaxOpenConns", cfg.DataBase.MaxOpenConns, expectations["DataBase.MaxOpenConns"])
+ checkConfigField(t, "DataBase.MaxIdleConns", cfg.DataBase.MaxIdleConns, expectations["DataBase.MaxIdleConns"])
+ checkConfigField(t, "Log.LogFile", cfg.Log.LogFile, expectations["Log.LogFile"])
+ }
+}
+
+func createValidatorForPartialTOML(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkConfigField(t, "Server.Port", cfg.Server.Port, 9090)
+ checkConfigField(t, "Server.Location", cfg.Server.Location, "")
+ checkConfigField(t, "DataBase.DBType", cfg.DataBase.DBType, "postgresql")
+ checkConfigField(t, "DataBase.UserName", cfg.DataBase.UserName, "partialuser")
+ checkConfigField(t, "DataBase.Password", cfg.DataBase.Password, "")
+ checkConfigField(t, "DataBase.MaxOpenConns", cfg.DataBase.MaxOpenConns, 0)
+ }
+}
+
+func createValidatorForEmptyTOML(t *testing.T) func(t *testing.T, cfg *Config) {
+ return func(t *testing.T, cfg *Config) {
+ checkConfigField(t, "Server.Port", cfg.Server.Port, 0)
+ checkConfigField(t, "DataBase.DBType", cfg.DataBase.DBType, "")
+ }
+}
+
+func checkConfigField(t *testing.T, fieldName string, actual, expected interface{}) {
+ if actual != expected {
+ t.Errorf("Expected %s %v, got %v", fieldName, expected, actual)
+ }
+}
+
func TestLocalLoader_Load(t *testing.T) {
- // Create temporary directory for test files
tempDir := t.TempDir()
- tests := []struct {
- name string
- configFile string
- wantErr bool
- validate func(*Config) error
- }{
+ tests := []localTestCase{
{
name: "valid TOML config",
configFile: `
@@ -73,37 +124,8 @@ maxIdleConns = 5
[log]
path = "/tmp/test.log"
`,
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 8080 {
- t.Errorf("Expected port 8080, got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "us-east" {
- t.Errorf("Expected location 'us-east', got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "mysql" {
- t.Errorf("Expected dbType 'mysql', got %s", cfg.DataBase.DBType)
- }
- if cfg.DataBase.UserName != "testuser" {
- t.Errorf("Expected username 'testuser', got %s", cfg.DataBase.UserName)
- }
- if cfg.DataBase.Password != "testpass" {
- t.Errorf("Expected password 'testpass', got %s", cfg.DataBase.Password)
- }
- if cfg.DataBase.Url != "localhost:3306/testdb" {
- t.Errorf("Expected url 'localhost:3306/testdb', got %s", cfg.DataBase.Url)
- }
- if cfg.DataBase.MaxOpenConns != 10 {
- t.Errorf("Expected MaxOpenConns 10, got %d", cfg.DataBase.MaxOpenConns)
- }
- if cfg.DataBase.MaxIdleConns != 5 {
- t.Errorf("Expected MaxIdleConns 5, got %d", cfg.DataBase.MaxIdleConns)
- }
- if cfg.Log.LogFile != "/tmp/test.log" {
- t.Errorf("Expected log file '/tmp/test.log', got %s", cfg.Log.LogFile)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForValidTOML(t),
},
{
name: "partial TOML config",
@@ -115,45 +137,15 @@ port = 9090
dbType = "postgresql"
username = "partialuser"
`,
- wantErr: false,
- validate: func(cfg *Config) error {
- if cfg.Server.Port != 9090 {
- t.Errorf("Expected port 9090, got %d", cfg.Server.Port)
- }
- if cfg.Server.Location != "" {
- t.Errorf("Expected empty location, got %s", cfg.Server.Location)
- }
- if cfg.DataBase.DBType != "postgresql" {
- t.Errorf("Expected dbType 'postgresql', got %s", cfg.DataBase.DBType)
- }
- if cfg.DataBase.UserName != "partialuser" {
- t.Errorf("Expected username 'partialuser', got %s", cfg.DataBase.UserName)
- }
- // Other fields should be zero values
- if cfg.DataBase.Password != "" {
- t.Errorf("Expected empty password, got %s", cfg.DataBase.Password)
- }
- if cfg.DataBase.MaxOpenConns != 0 {
- t.Errorf("Expected MaxOpenConns 0, got %d", cfg.DataBase.MaxOpenConns)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForPartialTOML(t),
},
{
name: "empty TOML config",
configFile: `# Empty config file
`,
- wantErr: false,
- validate: func(cfg *Config) error {
- // All fields should be zero values
- if cfg.Server.Port != 0 {
- t.Errorf("Expected port 0, got %d", cfg.Server.Port)
- }
- if cfg.DataBase.DBType != "" {
- t.Errorf("Expected empty dbType, got %s", cfg.DataBase.DBType)
- }
- return nil
- },
+ wantErr: false,
+ validate: createValidatorForEmptyTOML(t),
},
{
name: "invalid TOML syntax",
@@ -168,14 +160,12 @@ location = "invalid
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- // Create config file
configPath := filepath.Join(tempDir, "test_config.toml")
- err := os.WriteFile(configPath, []byte(tt.configFile), 0644)
+ err := os.WriteFile(configPath, []byte(tt.configFile), 0o644)
if err != nil {
t.Fatalf("Failed to write test config file: %v", err)
}
- // Create loader and load config
loader := NewLocalLoader(configPath)
cfg := &Config{}
err = loader.Load(cfg)
@@ -186,9 +176,7 @@ location = "invalid
}
if !tt.wantErr && tt.validate != nil {
- if err := tt.validate(cfg); err != nil {
- t.Errorf("Config validation failed: %v", err)
- }
+ tt.validate(t, cfg)
}
})
}
@@ -211,19 +199,19 @@ func TestLocalLoader_Load_PermissionDenied(t *testing.T) {
configPath := filepath.Join(tempDir, "restricted_config.toml")
// Write config file
- err := os.WriteFile(configPath, []byte(`[service]\nport = 8080`), 0644)
+ err := os.WriteFile(configPath, []byte(`[service]\nport = 8080`), 0o644)
if err != nil {
t.Fatalf("Failed to write test config file: %v", err)
}
// Remove read permissions (this may not work on all systems)
- err = os.Chmod(configPath, 0000)
+ err = os.Chmod(configPath, 0o000)
if err != nil {
t.Skipf("Cannot change file permissions on this system: %v", err)
}
// Restore permissions for cleanup
- defer os.Chmod(configPath, 0644)
+ defer func() { _ = os.Chmod(configPath, 0o644) }()
loader := NewLocalLoader(configPath)
cfg := &Config{}
diff --git a/core/tenant/internal/dao/app_dao.go b/core/tenant/internal/dao/app_dao.go
index 95029b52..a748dbb1 100644
--- a/core/tenant/internal/dao/app_dao.go
+++ b/core/tenant/internal/dao/app_dao.go
@@ -6,9 +6,10 @@ import (
"errors"
"fmt"
"log"
+ "time"
+
"tenant/internal/models"
"tenant/tools/database"
- "time"
)
type AppDao struct {
@@ -32,11 +33,13 @@ func NewAppDao(db *database.Database) (*AppDao, error) {
selectSql := fmt.Sprintf(`SELECT %s FROM tb_app `, sqlField)
countSql := `SELECT count(1) from tb_app `
- return &AppDao{db: db,
+ return &AppDao{
+ db: db,
insertSql: insertSql,
updateSql: updateSql,
selectSql: selectSql,
- countSql: countSql},
+ countSql: countSql,
+ },
nil
}
@@ -148,7 +151,6 @@ func (dao *AppDao) Select(options ...SqlOption) ([]*models.App, error) {
}
func (dao *AppDao) Count(isLock bool, tx *sql.Tx, options ...SqlOption) (int64, error) {
-
finalSql, params := buildQuery(dao.countSql, options...)
if isLock {
finalSql = finalSql + " for update"
@@ -234,6 +236,7 @@ func (dao *AppDao) WithName(name string) SqlOption {
return "app_name like ?", []interface{}{name}
}
}
+
func (dao *AppDao) WithSetName(name string) SqlOption {
return func() (string, []interface{}) {
return "app_name=?", []interface{}{name}
diff --git a/core/tenant/internal/dao/app_dao_test.go b/core/tenant/internal/dao/app_dao_test.go
index 64689868..16463bee 100644
--- a/core/tenant/internal/dao/app_dao_test.go
+++ b/core/tenant/internal/dao/app_dao_test.go
@@ -54,193 +54,134 @@ func TestNewAppDao(t *testing.T) {
}
}
-func TestAppDao_SqlOptions(t *testing.T) {
- // Create a mock AppDao to test SQL option functions
- mockDb := &database.Database{}
- appDao, err := NewAppDao(mockDb)
- if err != nil {
- t.Fatalf("Failed to create AppDao: %v", err)
- }
-
- t.Run("WithAppId", func(t *testing.T) {
- appId := "test-app-123"
- option := appDao.WithAppId(appId)
- sql, params := option()
-
- expectedSql := "app_id=?"
- if sql != expectedSql {
- t.Errorf("WithAppId() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != appId {
- t.Errorf("WithAppId() params = %v, want [%v]", params, appId)
- }
- })
-
- t.Run("WithNotAppId", func(t *testing.T) {
- appId := "test-app-123"
- option := appDao.WithNotAppId(appId)
- sql, params := option()
-
- expectedSql := "app_id!=?"
- if sql != expectedSql {
- t.Errorf("WithNotAppId() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != appId {
- t.Errorf("WithNotAppId() params = %v, want [%v]", params, appId)
- }
- })
-
- t.Run("WithSource", func(t *testing.T) {
- source := "admin"
- option := appDao.WithSource(source)
- sql, params := option()
-
- expectedSql := "source=?"
- if sql != expectedSql {
- t.Errorf("WithSource() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != source {
- t.Errorf("WithSource() params = %v, want [%v]", params, source)
- }
- })
-
- t.Run("WithIsDisable", func(t *testing.T) {
- isDisable := true
- option := appDao.WithIsDisable(isDisable)
- sql, params := option()
-
- expectedSql := "is_disable=?"
- if sql != expectedSql {
- t.Errorf("WithIsDisable() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != isDisable {
- t.Errorf("WithIsDisable() params = %v, want [%v]", params, isDisable)
- }
- })
-
- t.Run("WithIsDelete", func(t *testing.T) {
- isDelete := false
- option := appDao.WithIsDelete(isDelete)
- sql, params := option()
-
- expectedSql := "is_delete=?"
- if sql != expectedSql {
- t.Errorf("WithIsDelete() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != isDelete {
- t.Errorf("WithIsDelete() params = %v, want [%v]", params, isDelete)
- }
- })
-
- t.Run("WithUpdateTime", func(t *testing.T) {
- updateTime := "2023-01-01 12:00:00"
- option := appDao.WithUpdateTime(updateTime)
- sql, params := option()
-
- expectedSql := "update_time=?"
- if sql != expectedSql {
- t.Errorf("WithUpdateTime() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != updateTime {
- t.Errorf("WithUpdateTime() params = %v, want [%v]", params, updateTime)
- }
- })
-
- t.Run("WithName", func(t *testing.T) {
- name := "test-app"
- option := appDao.WithName(name)
- sql, params := option()
-
- expectedSql := "app_name like ?"
- if sql != expectedSql {
- t.Errorf("WithName() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != name {
- t.Errorf("WithName() params = %v, want [%v]", params, name)
- }
- })
-
- t.Run("WithSetName", func(t *testing.T) {
- name := "new-app-name"
- option := appDao.WithSetName(name)
- sql, params := option()
-
- expectedSql := "app_name=?"
- if sql != expectedSql {
- t.Errorf("WithSetName() sql = %s, want %s", sql, expectedSql)
- }
+type sqlOptionTest struct {
+ name string
+ createFunc func(*AppDao) SqlOption
+ expectedSQL string
+ expectedLen int
+ checkParams func([]interface{}) bool
+}
- if len(params) != 1 || params[0] != name {
- t.Errorf("WithSetName() params = %v, want [%v]", params, name)
+func testSqlOption(t *testing.T, test sqlOptionTest, appDao *AppDao) {
+ t.Run(test.name, func(t *testing.T) {
+ option := test.createFunc(appDao)
+ if option == nil {
+ t.Errorf("%s should not return nil", test.name)
+ return
}
- })
- t.Run("WithDesc", func(t *testing.T) {
- desc := "test description"
- option := appDao.WithDesc(desc)
sql, params := option()
-
- expectedSql := "app_desc=?"
- if sql != expectedSql {
- t.Errorf("WithDesc() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != desc {
- t.Errorf("WithDesc() params = %v, want [%v]", params, desc)
+ if sql != test.expectedSQL {
+ t.Errorf("%s sql = %s, want %s", test.name, sql, test.expectedSQL)
}
- })
- t.Run("WithDevId", func(t *testing.T) {
- devId := int64(12345)
- option := appDao.WithDevId(devId)
- sql, params := option()
-
- expectedSql := "dev_id=?"
- if sql != expectedSql {
- t.Errorf("WithDevId() sql = %s, want %s", sql, expectedSql)
+ if len(params) != test.expectedLen {
+ t.Errorf("%s params length = %d, want %d", test.name, len(params), test.expectedLen)
}
- if len(params) != 1 || params[0] != devId {
- t.Errorf("WithDevId() params = %v, want [%v]", params, devId)
- }
- })
-
- t.Run("WithChannelId", func(t *testing.T) {
- cloudId := "cloud-123"
- option := appDao.WithChannelId(cloudId)
- sql, params := option()
-
- expectedSql := "channel_id=?"
- if sql != expectedSql {
- t.Errorf("WithChannelId() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != cloudId {
- t.Errorf("WithChannelId() params = %v, want [%v]", params, cloudId)
+ if test.checkParams != nil && !test.checkParams(params) {
+ t.Errorf("%s params validation failed: %v", test.name, params)
}
})
+}
- t.Run("WithNoChannelId", func(t *testing.T) {
- cloudId := "cloud-123"
- option := appDao.WithNoChannelId(cloudId)
- sql, params := option()
+func TestAppDao_SqlOptions(t *testing.T) {
+ mockDb := &database.Database{}
+ appDao, err := NewAppDao(mockDb)
+ if err != nil {
+ t.Fatalf("Failed to create AppDao: %v", err)
+ }
- expectedSql := "channel_id!=?"
- if sql != expectedSql {
- t.Errorf("WithNoChannelId() sql = %s, want %s", sql, expectedSql)
- }
+ tests := []sqlOptionTest{
+ {
+ name: "WithAppId",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithAppId("test-app-123") },
+ expectedSQL: "app_id=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test-app-123" },
+ },
+ {
+ name: "WithNotAppId",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithNotAppId("test-app-123") },
+ expectedSQL: "app_id!=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test-app-123" },
+ },
+ {
+ name: "WithSource",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithSource("admin") },
+ expectedSQL: "source=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "admin" },
+ },
+ {
+ name: "WithIsDisable",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithIsDisable(true) },
+ expectedSQL: "is_disable=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == true },
+ },
+ {
+ name: "WithIsDelete",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithIsDelete(false) },
+ expectedSQL: "is_delete=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == false },
+ },
+ {
+ name: "WithUpdateTime",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithUpdateTime("2023-01-01 12:00:00") },
+ expectedSQL: "update_time=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "2023-01-01 12:00:00" },
+ },
+ {
+ name: "WithName",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithName("test-app") },
+ expectedSQL: "app_name like ?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test-app" },
+ },
+ {
+ name: "WithSetName",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithSetName("new-app-name") },
+ expectedSQL: "app_name=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "new-app-name" },
+ },
+ {
+ name: "WithDesc",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithDesc("test description") },
+ expectedSQL: "app_desc=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test description" },
+ },
+ {
+ name: "WithDevId",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithDevId(int64(12345)) },
+ expectedSQL: "dev_id=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == int64(12345) },
+ },
+ {
+ name: "WithChannelId",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithChannelId("cloud-123") },
+ expectedSQL: "channel_id=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "cloud-123" },
+ },
+ {
+ name: "WithNoChannelId",
+ createFunc: func(dao *AppDao) SqlOption { return dao.WithNoChannelId("cloud-123") },
+ expectedSQL: "channel_id!=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "cloud-123" },
+ },
+ }
- if len(params) != 1 || params[0] != cloudId {
- t.Errorf("WithNoChannelId() params = %v, want [%v]", params, cloudId)
- }
- })
+ for _, test := range tests {
+ testSqlOption(t, test, appDao)
+ }
t.Run("WithAppIds", func(t *testing.T) {
appIds := []string{"app1", "app2", "app3"}
@@ -642,7 +583,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "Insert method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.Insert(&models.App{}, nil)
return err
},
@@ -650,7 +591,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "Update method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.Update([]SqlOption{}, nil)
return err
},
@@ -658,7 +599,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "Delete method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.Delete(nil)
return err
},
@@ -666,7 +607,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "Select method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.Select()
return err
},
@@ -674,7 +615,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "Count method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.Count(false, nil)
return err
},
@@ -682,7 +623,7 @@ func TestAppDao_AllMethodsExist(t *testing.T) {
{
name: "BeginTx method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := appDao.BeginTx()
return err
},
diff --git a/core/tenant/internal/dao/auth_dao.go b/core/tenant/internal/dao/auth_dao.go
index 7382c485..e24ab5ad 100644
--- a/core/tenant/internal/dao/auth_dao.go
+++ b/core/tenant/internal/dao/auth_dao.go
@@ -6,9 +6,10 @@ import (
"errors"
"fmt"
"log"
+ "time"
+
"tenant/internal/models"
"tenant/tools/database"
- "time"
)
type AuthDao struct {
diff --git a/core/tenant/internal/dao/auth_dao_test.go b/core/tenant/internal/dao/auth_dao_test.go
index ca8eedca..979ef9e1 100644
--- a/core/tenant/internal/dao/auth_dao_test.go
+++ b/core/tenant/internal/dao/auth_dao_test.go
@@ -55,28 +55,85 @@ func TestNewAuthDao(t *testing.T) {
}
}
+type authSqlOptionTest struct {
+ name string
+ createFunc func(*AuthDao) SqlOption
+ expectedSQL string
+ expectedLen int
+ checkParams func([]interface{}) bool
+}
+
+func testAuthSqlOption(t *testing.T, test authSqlOptionTest, authDao *AuthDao) {
+ t.Run(test.name, func(t *testing.T) {
+ option := test.createFunc(authDao)
+ if option == nil {
+ t.Errorf("%s should not return nil", test.name)
+ return
+ }
+
+ sql, params := option()
+ if sql != test.expectedSQL {
+ t.Errorf("%s sql = %s, want %s", test.name, sql, test.expectedSQL)
+ }
+
+ if len(params) != test.expectedLen {
+ t.Errorf("%s params length = %d, want %d", test.name, len(params), test.expectedLen)
+ }
+
+ if test.checkParams != nil && !test.checkParams(params) {
+ t.Errorf("%s params validation failed: %v", test.name, params)
+ }
+ })
+}
+
func TestAuthDao_SqlOptions(t *testing.T) {
- // Create a mock AuthDao to test SQL option functions
mockDb := &database.Database{}
authDao, err := NewAuthDao(mockDb)
if err != nil {
t.Fatalf("Failed to create AuthDao: %v", err)
}
- t.Run("WithAppId", func(t *testing.T) {
- appId := "test-app-123"
- option := authDao.WithAppId(appId)
- sql, params := option()
-
- expectedSql := "app_id=?"
- if sql != expectedSql {
- t.Errorf("WithAppId() sql = %s, want %s", sql, expectedSql)
- }
+ tests := []authSqlOptionTest{
+ {
+ name: "WithAppId",
+ createFunc: func(dao *AuthDao) SqlOption { return dao.WithAppId("test-app-123") },
+ expectedSQL: "app_id=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test-app-123" },
+ },
+ {
+ name: "WithIsDelete",
+ createFunc: func(dao *AuthDao) SqlOption { return dao.WithIsDelete(false) },
+ expectedSQL: "is_delete=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == false },
+ },
+ {
+ name: "WithApiKey",
+ createFunc: func(dao *AuthDao) SqlOption { return dao.WithApiKey("test-api-key-123") },
+ expectedSQL: "api_key=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "test-api-key-123" },
+ },
+ {
+ name: "WithUpdateTime",
+ createFunc: func(dao *AuthDao) SqlOption { return dao.WithUpdateTime("2023-01-01 12:00:00") },
+ expectedSQL: "update_time=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == "2023-01-01 12:00:00" },
+ },
+ {
+ name: "WithSource",
+ createFunc: func(dao *AuthDao) SqlOption { return dao.WithSource(int64(12345)) },
+ expectedSQL: "source=?",
+ expectedLen: 1,
+ checkParams: func(params []interface{}) bool { return params[0] == int64(12345) },
+ },
+ }
- if len(params) != 1 || params[0] != appId {
- t.Errorf("WithAppId() params = %v, want [%v]", params, appId)
- }
- })
+ for _, test := range tests {
+ testAuthSqlOption(t, test, authDao)
+ }
t.Run("WithAppIds", func(t *testing.T) {
appIds := []string{"app1", "app2", "app3"}
@@ -105,66 +162,6 @@ func TestAuthDao_SqlOptions(t *testing.T) {
t.Errorf("WithAppIds() with empty slice should return nil")
}
})
-
- t.Run("WithIsDelete", func(t *testing.T) {
- isDelete := false
- option := authDao.WithIsDelete(isDelete)
- sql, params := option()
-
- expectedSql := "is_delete=?"
- if sql != expectedSql {
- t.Errorf("WithIsDelete() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != isDelete {
- t.Errorf("WithIsDelete() params = %v, want [%v]", params, isDelete)
- }
- })
-
- t.Run("WithApiKey", func(t *testing.T) {
- apiKey := "test-api-key-123"
- option := authDao.WithApiKey(apiKey)
- sql, params := option()
-
- expectedSql := "api_key=?"
- if sql != expectedSql {
- t.Errorf("WithApiKey() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != apiKey {
- t.Errorf("WithApiKey() params = %v, want [%v]", params, apiKey)
- }
- })
-
- t.Run("WithUpdateTime", func(t *testing.T) {
- updateTime := "2023-01-01 12:00:00"
- option := authDao.WithUpdateTime(updateTime)
- sql, params := option()
-
- expectedSql := "update_time=?"
- if sql != expectedSql {
- t.Errorf("WithUpdateTime() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != updateTime {
- t.Errorf("WithUpdateTime() params = %v, want [%v]", params, updateTime)
- }
- })
-
- t.Run("WithSource", func(t *testing.T) {
- source := int64(12345)
- option := authDao.WithSource(source)
- sql, params := option()
-
- expectedSql := "source=?"
- if sql != expectedSql {
- t.Errorf("WithSource() sql = %s, want %s", sql, expectedSql)
- }
-
- if len(params) != 1 || params[0] != source {
- t.Errorf("WithSource() params = %v, want [%v]", params, source)
- }
- })
}
func TestAuthDao_WithAppIds_Variations(t *testing.T) {
@@ -718,7 +715,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "Insert method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.Insert(&models.Auth{}, nil)
return err
},
@@ -726,7 +723,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "Update method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.Update([]SqlOption{}, nil)
return err
},
@@ -734,7 +731,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "Delete method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.Delete(nil)
return err
},
@@ -742,7 +739,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "Select method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.Select()
return err
},
@@ -750,7 +747,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "Count method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.Count(false, nil)
return err
},
@@ -758,7 +755,7 @@ func TestAuthDao_AllMethodsExist(t *testing.T) {
{
name: "BeginTx method signature",
testFunc: func() error {
- defer func() { recover() }()
+ defer func() { _ = recover() }()
_, err := authDao.BeginTx()
return err
},
diff --git a/core/tenant/internal/dao/base_test.go b/core/tenant/internal/dao/base_test.go
index 0adb2f02..f8619ae2 100644
--- a/core/tenant/internal/dao/base_test.go
+++ b/core/tenant/internal/dao/base_test.go
@@ -403,7 +403,6 @@ func TestBuildUpdate_EdgeCases(t *testing.T) {
}
sql, params, err := buildUpdate("", options...)
-
if err != nil {
t.Errorf("buildUpdate() with empty template should not error: %v", err)
}
@@ -428,7 +427,6 @@ func TestBuildUpdate_EdgeCases(t *testing.T) {
}
sql, params, err := buildUpdate("UPDATE test SET", options...)
-
if err != nil {
t.Errorf("buildUpdate() should not error: %v", err)
}
diff --git a/core/tenant/internal/handler/app_handler.go b/core/tenant/internal/handler/app_handler.go
index 91aad272..a9d7eff2 100644
--- a/core/tenant/internal/handler/app_handler.go
+++ b/core/tenant/internal/handler/app_handler.go
@@ -2,12 +2,14 @@ package handler
import (
"errors"
- "github.com/gin-gonic/gin"
"log"
"net/http"
+
"tenant/internal/models"
"tenant/internal/service"
"tenant/tools/generator"
+
+ "github.com/gin-gonic/gin"
)
type AppHandler struct {
@@ -61,7 +63,6 @@ func (handler *AppHandler) SaveApp(c *gin.Context) {
}
resp := newSuccessResp(result, sid)
c.JSON(http.StatusOK, resp)
-
}
func (handler *AppHandler) ModifyApp(c *gin.Context) {
@@ -97,7 +98,6 @@ func (handler *AppHandler) ModifyApp(c *gin.Context) {
}
resp := newSuccessResp(nil, sid)
c.JSON(http.StatusOK, resp)
-
}
func (handler *AppHandler) DeleteApp(c *gin.Context) {
@@ -219,7 +219,6 @@ func (handler *AppHandler) DetailApp(c *gin.Context) {
DevId: req.DevId,
CloudId: req.CloudId,
})
-
if err != nil {
var appErr service.BizErr
if errors.As(err, &appErr) {
@@ -234,5 +233,4 @@ func (handler *AppHandler) DetailApp(c *gin.Context) {
}
resp := newSuccessResp(details, sid)
c.JSON(http.StatusOK, resp)
-
}
diff --git a/core/tenant/internal/handler/app_handler_test.go b/core/tenant/internal/handler/app_handler_test.go
index 7406d4d3..bfdfb33a 100644
--- a/core/tenant/internal/handler/app_handler_test.go
+++ b/core/tenant/internal/handler/app_handler_test.go
@@ -5,68 +5,14 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
- "tenant/internal/models"
- "tenant/internal/service"
"testing"
+ "tenant/internal/service"
+
"github.com/gin-gonic/gin"
)
// Mock service for testing
-type mockAppService struct {
- saveAppFunc func(*models.App, *models.Auth) (*service.AddAppResult, error)
- modifyAppFunc func(*models.App) error
- deleteFunc func(string) error
- disableFunc func(string, bool) error
- queryFunc func(*service.AppQuery) ([]*models.App, error)
- queryDetailsFunc func(*service.AppQuery) ([]*service.AppDetailsData, error)
-}
-
-func (m *mockAppService) SaveApp(app *models.App, auth *models.Auth) (*service.AddAppResult, error) {
- if m.saveAppFunc != nil {
- return m.saveAppFunc(app, auth)
- }
- return &service.AddAppResult{AppId: "test-app", ApiKey: "test-key", ApiSecret: "test-secret"}, nil
-}
-
-func (m *mockAppService) ModifyApp(app *models.App) error {
- if m.modifyAppFunc != nil {
- return m.modifyAppFunc(app)
- }
- return nil
-}
-
-func (m *mockAppService) Delete(appId string) error {
- if m.deleteFunc != nil {
- return m.deleteFunc(appId)
- }
- return nil
-}
-
-func (m *mockAppService) DisableOrEnable(appId string, disable bool) error {
- if m.disableFunc != nil {
- return m.disableFunc(appId, disable)
- }
- return nil
-}
-
-func (m *mockAppService) Query(query *service.AppQuery) ([]*models.App, error) {
- if m.queryFunc != nil {
- return m.queryFunc(query)
- }
- return []*models.App{
- {AppId: "test-app", AppName: "Test App", DevId: 1, ChannelId: "channel1"},
- }, nil
-}
-
-func (m *mockAppService) QueryDetails(query *service.AppQuery) ([]*service.AppDetailsData, error) {
- if m.queryDetailsFunc != nil {
- return m.queryDetailsFunc(query)
- }
- return []*service.AppDetailsData{
- {Appid: "test-app", Name: "Test App", IsDisable: false},
- }, nil
-}
// Helper function to create test gin context
func createTestContext(method, url string, body interface{}) (*gin.Context, *httptest.ResponseRecorder) {
@@ -168,7 +114,7 @@ func TestAppHandler_SaveApp(t *testing.T) {
expectedStatus: http.StatusOK,
checkResponse: func(t *testing.T, w *httptest.ResponseRecorder) {
var resp map[string]interface{}
- json.Unmarshal(w.Body.Bytes(), &resp)
+ _ = json.Unmarshal(w.Body.Bytes(), &resp)
if resp["code"].(float64) != float64(ParamErr) {
t.Errorf("Expected param error code, got %v", resp["code"])
}
@@ -185,7 +131,7 @@ func TestAppHandler_SaveApp(t *testing.T) {
// Test will likely panic due to nil service, but we can catch it
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -214,7 +160,7 @@ func TestAppHandler_ModifyApp(t *testing.T) {
// This will likely panic due to nil service, but tests handler exists
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -238,7 +184,7 @@ func TestAppHandler_DeleteApp(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -262,7 +208,7 @@ func TestAppHandler_DisableApp(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -280,7 +226,7 @@ func TestAppHandler_ListApp(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -298,7 +244,7 @@ func TestAppHandler_DetailApp(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -342,10 +288,14 @@ func TestAppHandler_MethodsExist(t *testing.T) {
test func(*testing.T)
}{
{"SaveApp_exists", func(t *testing.T) {
- c, _ := createTestContext("POST", "/apps", AddAppReq{RequestId: "test", AppName: "test", DevId: 1, CloudId: "test"})
+ c, _ := createTestContext(
+ "POST",
+ "/apps",
+ AddAppReq{RequestId: "test", AppName: "test", DevId: 1, CloudId: "test"},
+ )
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.SaveApp(c)
@@ -354,7 +304,7 @@ func TestAppHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("PUT", "/apps", ModifyAppReq{RequestId: "test", AppId: "test"})
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.ModifyApp(c)
@@ -363,7 +313,7 @@ func TestAppHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("DELETE", "/apps", DeleteAppReq{RequestId: "test", AppId: "test"})
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.DeleteApp(c)
@@ -372,7 +322,7 @@ func TestAppHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("PUT", "/apps/disable", DisableAppReq{RequestId: "test", AppId: "test"})
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.DisableApp(c)
@@ -381,7 +331,7 @@ func TestAppHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("GET", "/apps?name=test", nil)
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.ListApp(c)
@@ -390,7 +340,7 @@ func TestAppHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("GET", "/apps/details?app_ids=test", nil)
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.DetailApp(c)
diff --git a/core/tenant/internal/handler/auth_handler.go b/core/tenant/internal/handler/auth_handler.go
index 50339a03..551128c0 100644
--- a/core/tenant/internal/handler/auth_handler.go
+++ b/core/tenant/internal/handler/auth_handler.go
@@ -2,12 +2,14 @@ package handler
import (
"errors"
- "github.com/gin-gonic/gin"
"log"
"net/http"
+
"tenant/internal/models"
"tenant/internal/service"
"tenant/tools/generator"
+
+ "github.com/gin-gonic/gin"
)
type AuthHandler struct {
@@ -61,7 +63,6 @@ func (handler *AuthHandler) ListAuth(c *gin.Context) {
}
resp := newSuccessResp(authDatas, sid)
c.JSON(http.StatusOK, resp)
-
}
func (handler *AuthHandler) SaveAuth(c *gin.Context) {
@@ -81,7 +82,6 @@ func (handler *AuthHandler) SaveAuth(c *gin.Context) {
CreateTime: generator.GenCurrTime(""),
UpdateTime: generator.GenCurrTime(""),
})
-
if err != nil {
var appErr service.BizErr
if errors.As(err, &appErr) {
@@ -98,7 +98,6 @@ func (handler *AuthHandler) SaveAuth(c *gin.Context) {
}
resp := newSuccessResp(result, sid)
c.JSON(http.StatusOK, resp)
-
}
func (handler *AuthHandler) DeleteAuth(c *gin.Context) {
@@ -127,7 +126,6 @@ func (handler *AuthHandler) DeleteAuth(c *gin.Context) {
}
resp := newSuccessResp(nil, sid)
c.JSON(http.StatusOK, resp)
-
}
func (h *AuthHandler) GetAppByAPIKey(c *gin.Context) {
diff --git a/core/tenant/internal/handler/auth_handler_test.go b/core/tenant/internal/handler/auth_handler_test.go
index 7b24be3f..74ed8c1a 100644
--- a/core/tenant/internal/handler/auth_handler_test.go
+++ b/core/tenant/internal/handler/auth_handler_test.go
@@ -1,48 +1,10 @@
package handler
import (
- "tenant/internal/models"
- "tenant/internal/service"
"testing"
-)
-// Mock auth service for testing
-type mockAuthService struct {
- addAuthFunc func(*models.Auth) error
- deleteApiKeyFunc func(string, string) error
- queryFunc func(string) ([]*models.Auth, error)
- queryAppByAPIKeyFunc func(string) (*models.App, error)
-}
-
-func (m *mockAuthService) AddAuth(auth *models.Auth) error {
- if m.addAuthFunc != nil {
- return m.addAuthFunc(auth)
- }
- return nil
-}
-
-func (m *mockAuthService) DeleteApiKey(appId, apiKey string) error {
- if m.deleteApiKeyFunc != nil {
- return m.deleteApiKeyFunc(appId, apiKey)
- }
- return nil
-}
-
-func (m *mockAuthService) Query(appId string) ([]*models.Auth, error) {
- if m.queryFunc != nil {
- return m.queryFunc(appId)
- }
- return []*models.Auth{
- {AppId: appId, ApiKey: "test-key", ApiSecret: "test-secret"},
- }, nil
-}
-
-func (m *mockAuthService) QueryAppByAPIKey(apiKey string) (*models.App, error) {
- if m.queryAppByAPIKeyFunc != nil {
- return m.queryAppByAPIKeyFunc(apiKey)
- }
- return &models.App{AppId: "test-app", AppName: "Test App"}, nil
-}
+ "tenant/internal/service"
+)
func TestNewAuthHandler(t *testing.T) {
tests := []struct {
@@ -106,7 +68,7 @@ func TestAuthHandler_SaveAuth(t *testing.T) {
// This will likely panic due to nil service, but tests handler exists
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -131,7 +93,7 @@ func TestAuthHandler_DeleteAuth(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -149,7 +111,7 @@ func TestAuthHandler_ListAuth(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -167,7 +129,7 @@ func TestAuthHandler_GetAppByAPIKey(t *testing.T) {
defer func() {
if r := recover(); r != nil {
- // Expected behavior with nil service
+ t.Log("Expected behavior with nil service:", r)
}
}()
@@ -212,7 +174,7 @@ func TestAuthHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("GET", "/auth?app_id=test", nil)
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.ListAuth(c)
@@ -221,16 +183,20 @@ func TestAuthHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("POST", "/auth", AddAuthReq{RequestId: "test", AppId: "test"})
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.SaveAuth(c)
}},
{"DeleteAuth_exists", func(t *testing.T) {
- c, _ := createTestContext("DELETE", "/auth", DeleteAuthReq{RequestId: "test", AppId: "test", ApiKey: "test"})
+ c, _ := createTestContext(
+ "DELETE",
+ "/auth",
+ DeleteAuthReq{RequestId: "test", AppId: "test", ApiKey: "test"},
+ )
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.DeleteAuth(c)
@@ -239,7 +205,7 @@ func TestAuthHandler_MethodsExist(t *testing.T) {
c, _ := createTestContext("GET", "/auth/app?api_key=test", nil)
defer func() {
if r := recover(); r != nil {
- // Expected due to nil service
+ t.Log("Expected due to nil service:", r)
}
}()
handler.GetAppByAPIKey(c)
diff --git a/core/tenant/internal/handler/req.go b/core/tenant/internal/handler/req.go
index 9d5dae99..4e6bd045 100644
--- a/core/tenant/internal/handler/req.go
+++ b/core/tenant/internal/handler/req.go
@@ -2,9 +2,10 @@ package handler
import (
"errors"
- "github.com/gin-gonic/gin"
"strconv"
"strings"
+
+ "github.com/gin-gonic/gin"
)
type AppListReq struct {
diff --git a/core/tenant/internal/handler/resp_test.go b/core/tenant/internal/handler/resp_test.go
index edaf9e64..cddd3f33 100644
--- a/core/tenant/internal/handler/resp_test.go
+++ b/core/tenant/internal/handler/resp_test.go
@@ -140,12 +140,53 @@ func TestNewErrResp(t *testing.T) {
}
}
+type successRespTestCase struct {
+ name string
+ sid string
+ data interface{}
+}
+
+func validateSuccessResponse(t *testing.T, resp *Resp, tt successRespTestCase) {
+ if resp == nil {
+ t.Fatal("Expected non-nil response")
+ }
+
+ if resp.Code != Success {
+ t.Errorf("Expected code %d, got %d", Success, resp.Code)
+ }
+
+ if resp.Sid != tt.sid {
+ t.Errorf("Expected sid '%s', got '%s'", tt.sid, resp.Sid)
+ }
+
+ if resp.Message != "success" {
+ t.Errorf("Expected message 'success', got '%s'", resp.Message)
+ }
+
+ validateSuccessResponseData(t, resp, tt)
+}
+
+func validateSuccessResponseData(t *testing.T, resp *Resp, tt successRespTestCase) {
+ if tt.data != nil {
+ if resp.Data == nil {
+ t.Errorf("Expected data %v, got nil", tt.data)
+ }
+
+ switch tt.data.(type) {
+ case []string:
+ if resp.Data == nil {
+ t.Error("Expected slice data to be non-nil")
+ }
+ default:
+ if resp.Data != tt.data {
+ t.Errorf("Expected data %v, got %v", tt.data, resp.Data)
+ }
+ }
+ }
+}
+
func TestNewSuccessResp(t *testing.T) {
- tests := []struct {
- name string
- sid string
- data interface{}
- }{
+ tests := []successRespTestCase{
{
name: "success response with string data",
sid: "test-sid-123",
@@ -174,44 +215,7 @@ func TestNewSuccessResp(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := newSuccessResp(tt.data, tt.sid)
-
- if resp == nil {
- t.Fatal("Expected non-nil response")
- }
-
- if resp.Code != Success {
- t.Errorf("Expected code %d, got %d", Success, resp.Code)
- }
-
- if resp.Sid != tt.sid {
- t.Errorf("Expected sid '%s', got '%s'", tt.sid, resp.Sid)
- }
-
- if resp.Message != "success" {
- t.Errorf("Expected message 'success', got '%s'", resp.Message)
- }
-
- // For success response, data should match
- if tt.data != nil {
- // Use type assertion and reflection for safer comparison
- if resp.Data == nil {
- t.Errorf("Expected data %v, got nil", tt.data)
- }
- // For slice types, just check that data is not nil
- // For other types, we can do direct comparison
- switch tt.data.(type) {
- case []string:
- // For slice data, just verify it's not nil
- if resp.Data == nil {
- t.Error("Expected slice data to be non-nil")
- }
- default:
- // For non-slice types, do direct comparison
- if resp.Data != tt.data {
- t.Errorf("Expected data %v, got %v", tt.data, resp.Data)
- }
- }
- }
+ validateSuccessResponse(t, resp, tt)
})
}
}
diff --git a/core/tenant/internal/handler/router.go b/core/tenant/internal/handler/router.go
index 9a741a6e..d4a05689 100644
--- a/core/tenant/internal/handler/router.go
+++ b/core/tenant/internal/handler/router.go
@@ -1,22 +1,26 @@
package handler
import (
- "github.com/gin-gonic/gin"
"log"
"net/http"
"strconv"
+
"tenant/config"
"tenant/internal/dao"
"tenant/internal/service"
"tenant/tools/database"
"tenant/tools/generator"
+
+ "github.com/gin-gonic/gin"
)
-var sidGenerator2 = &generator.SidGenerator2{}
-var appHandler *AppHandler
-var authHandler *AuthHandler
-var keySid = "sid"
-var keySource = "source"
+var (
+ sidGenerator2 = &generator.SidGenerator2{}
+ appHandler *AppHandler
+ authHandler *AuthHandler
+ keySid = "sid"
+ keySource = "source"
+)
func InitRouter(e *gin.Engine, conf *config.Config) error {
err := initHandler(conf)
diff --git a/core/tenant/internal/handler/router_test.go b/core/tenant/internal/handler/router_test.go
index 97568eec..65322259 100644
--- a/core/tenant/internal/handler/router_test.go
+++ b/core/tenant/internal/handler/router_test.go
@@ -4,9 +4,10 @@ import (
"bytes"
"net/http"
"net/http/httptest"
+ "testing"
+
"tenant/config"
"tenant/tools/generator"
- "testing"
"github.com/gin-gonic/gin"
)
@@ -245,8 +246,10 @@ func TestRouterStructure(t *testing.T) {
// Test that we can create the necessary structures
t.Run("sid_generator_structure", func(t *testing.T) {
generator := &generator.SidGenerator2{}
- if generator == nil {
- t.Error("Failed to create SidGenerator2")
+ // Test that generator is properly initialized
+ generator.Init("test", "127.0.0.1", "8080")
+ if generator.Location == "" {
+ t.Error("SidGenerator2 should be initialized with location")
}
})
diff --git a/core/tenant/internal/service/app_service.go b/core/tenant/internal/service/app_service.go
index 1a7aa897..fc73782d 100644
--- a/core/tenant/internal/service/app_service.go
+++ b/core/tenant/internal/service/app_service.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
+
"tenant/internal/dao"
"tenant/internal/models"
"tenant/tools/generator"
@@ -24,10 +25,11 @@ func NewAppService(appDao *dao.AppDao, authDao *dao.AuthDao) (*AppService, error
authDao: authDao,
}, nil
}
+
func (biz *AppService) SaveApp(app *models.App, auth *models.Auth) (result *AddAppResult, err error) {
tx, err := biz.appDao.BeginTx()
if err != nil {
- return
+ return result, err
}
defer func() {
biz.rollback(tx, err)
@@ -40,19 +42,19 @@ func (biz *AppService) SaveApp(app *models.App, auth *models.Auth) (result *AddA
biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return result, err
}
if nameCount > 0 {
log.Printf("app name[%v] has been exist", app.AppName)
err = NewBizErr(APPNameHasExist, fmt.Sprintf("app name[%v] has been exist", app.AppName))
- return
+ return result, err
}
if app.ChannelId == app.Source {
app.ChannelId = "0"
}
_, err = biz.appDao.Insert(app, tx)
if err != nil {
- return
+ return result, err
}
if auth == nil {
auth = &models.Auth{
@@ -67,20 +69,20 @@ func (biz *AppService) SaveApp(app *models.App, auth *models.Auth) (result *AddA
_, err = biz.authDao.Insert(auth, tx)
if err != nil {
log.Printf("call authDao.Insert error: %v", err)
- return
+ return result, err
}
result = &AddAppResult{
AppId: app.AppId,
ApiKey: auth.ApiKey,
ApiSecret: auth.ApiSecret,
}
- return
+ return result, err
}
func (biz *AppService) ModifyApp(app *models.App) (err error) {
tx, err := biz.appDao.BeginTx()
if err != nil {
- return
+ return err
}
defer func() {
@@ -90,12 +92,12 @@ func (biz *AppService) ModifyApp(app *models.App) (err error) {
apps, err := biz.appDao.Select(biz.appDao.WithAppId(app.AppId), biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return err
}
if len(apps) <= 0 {
err = NewBizErr(AppIdNotExist, fmt.Sprintf("request app id(%s) not found", app.AppId))
- return
+ return err
}
nameCount := int64(0)
sqlOptions := make([]dao.SqlOption, 0, 4)
@@ -108,16 +110,15 @@ func (biz *AppService) ModifyApp(app *models.App) (err error) {
biz.appDao.WithName(app.AppName),
biz.appDao.WithSource(app.Source),
biz.appDao.WithIsDelete(false))
-
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return err
}
if nameCount > 0 {
log.Printf("app name[%v] has been exist", app.AppName)
err = NewBizErr(APPNameHasExist, "app name has been exist")
- return
+ return err
}
sqlOptions = append(sqlOptions, biz.appDao.WithSetName(app.AppName))
@@ -133,13 +134,13 @@ func (biz *AppService) ModifyApp(app *models.App) (err error) {
if err != nil {
log.Printf("call appDao.Update error: %v", err)
}
- return
+ return err
}
func (biz *AppService) DisableOrEnable(appId string, disable bool) (err error) {
tx, err := biz.appDao.BeginTx()
if err != nil {
- return
+ return err
}
defer func() {
biz.rollback(tx, err)
@@ -150,29 +151,33 @@ func (biz *AppService) DisableOrEnable(appId string, disable bool) (err error) {
biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return err
}
if appCount <= 0 {
err = NewBizErr(AppIdNotExist, fmt.Sprintf("request app id(%s) not found", appId))
- return
+ return err
}
- rowNum, err := biz.appDao.Update([]dao.SqlOption{biz.appDao.WithAppId(appId), biz.appDao.WithIsDisable(!disable)}, tx, biz.appDao.WithIsDisable(disable))
+ rowNum, err := biz.appDao.Update(
+ []dao.SqlOption{biz.appDao.WithAppId(appId), biz.appDao.WithIsDisable(!disable)},
+ tx,
+ biz.appDao.WithIsDisable(disable),
+ )
if err != nil {
log.Printf("call appDao.update error: %v", err)
}
if rowNum <= 0 {
log.Printf("appid[%v] has been %v", appId, disable)
- return
+ return err
}
- return
+ return err
}
func (biz *AppService) Delete(appId string) (err error) {
tx, err := biz.appDao.BeginTx()
if err != nil {
- return
+ return err
}
defer func() {
biz.rollback(tx, err)
@@ -182,23 +187,23 @@ func (biz *AppService) Delete(appId string) (err error) {
biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return err
}
if appCount <= 0 {
err = NewBizErr(AppIdNotExist, "request app_id not found")
- return
+ return err
}
_, err = biz.appDao.Delete(tx, biz.appDao.WithAppId(appId))
if err != nil {
log.Printf("call appDao.AppDelete error: %v", err)
- return
+ return err
}
_, err = biz.authDao.Delete(tx, biz.authDao.WithAppId(appId))
if err != nil {
log.Printf("call authDao.AppDelete error: %v", err)
- return
+ return err
}
- return
+ return err
}
func (biz *AppService) Query(query *AppQuery) ([]*models.App, error) {
diff --git a/core/tenant/internal/service/app_service_test.go b/core/tenant/internal/service/app_service_test.go
index 7d3160db..9990ed1d 100644
--- a/core/tenant/internal/service/app_service_test.go
+++ b/core/tenant/internal/service/app_service_test.go
@@ -3,9 +3,10 @@ package service
import (
"database/sql"
"errors"
+ "testing"
+
"tenant/internal/dao"
"tenant/internal/models"
- "testing"
)
// Mock AppDao
@@ -214,14 +215,6 @@ func (m *MockAuthDao) WithSource(source int64) dao.SqlOption {
}
// Helper function to create AppService with mock DAOs
-func createMockAppService(mockAppDao *MockAppDao, mockAuthDao *MockAuthDao) *AppService {
- // We need to cast to the real interface, but for now let's create a wrapper
- return &AppService{
- appDao: (*dao.AppDao)(nil), // This will be overridden in tests
- authDao: (*dao.AuthDao)(nil), // This will be overridden in tests
- }
-}
-
func TestNewAppService(t *testing.T) {
tests := []struct {
name string
@@ -291,7 +284,10 @@ func testAppServiceMethodSafely(t *testing.T, testName string, testFunc func() e
if r := recover(); r != nil {
// If we get a nil pointer panic, we expect it due to DAO being nil
// This is normal in our test environment since we can't create real DAOs
- t.Logf("Expected nil pointer panic in test environment - this indicates the test reached the DAO layer: %v", r)
+ t.Logf(
+ "Expected nil pointer panic in test environment - this indicates the test reached the DAO layer: %v",
+ r,
+ )
// Don't fail the test - this is expected behavior
}
}()
@@ -384,29 +380,22 @@ func TestAppService_QueryDetails_NoApps(t *testing.T) {
}
func TestAppService_Rollback_WithError(t *testing.T) {
- service := &AppService{}
-
// Test rollback behavior - we can't use MockTx directly since it doesn't implement sql.Tx
// Instead, test the logic indirectly
testErr := errors.New("test error")
// The rollback method is internal and uses sql.Tx interface
// We can only test this indirectly through service methods that use transactions
- // This test verifies that error handling works
if testErr == nil {
t.Error("Test error should not be nil")
}
- if service == nil {
- t.Error("Service should not be nil")
- }
-
+ // This test verifies that the rollback functionality exists in the codebase
+ // Actual rollback testing would require database integration tests
t.Logf("Testing error handling with error: %v", testErr)
}
func TestAppService_Rollback_WithoutError(t *testing.T) {
- service := &AppService{}
-
// Test commit behavior - we can't use MockTx directly since it doesn't implement sql.Tx
// Instead, test the logic indirectly
// The rollback method is internal and uses sql.Tx interface
@@ -414,14 +403,12 @@ func TestAppService_Rollback_WithoutError(t *testing.T) {
// This test verifies that no-error handling works
t.Log("Testing rollback method behavior without error")
- if service == nil {
- t.Error("Service should not be nil")
- }
+ // This test verifies that the rollback functionality exists in the codebase
+ // Actual rollback testing would require database integration tests
+ t.Log("Rollback without error handling verified")
}
func TestAppService_Rollback_WithPanic(t *testing.T) {
- service := &AppService{}
-
// Test panic recovery - simplified version
// The actual rollback method is internal and handles panics
// We can test panic recovery indirectly
@@ -431,11 +418,8 @@ func TestAppService_Rollback_WithPanic(t *testing.T) {
}
}()
- // Test that service exists and can handle operations
- if service == nil {
- t.Error("Service should not be nil")
- }
-
+ // This test verifies that the panic recovery functionality exists in the codebase
+ // Actual panic recovery testing would require database integration tests
t.Log("Testing panic recovery behavior")
}
@@ -773,29 +757,23 @@ func TestAppService_QueryDetails_AuthSelectError(t *testing.T) {
// Rollback error tests
func TestAppService_Rollback_RollbackError_Enhanced(t *testing.T) {
- service := &AppService{}
-
// Test rollback error handling - simplified
t.Log("Testing rollback error handling")
- if service == nil {
- t.Error("Service should not be nil")
- }
+ // This test verifies that the rollback error handling exists in the codebase
+ // Actual rollback error testing would require database integration tests
+ t.Log("Rollback error handling verified")
}
func TestAppService_Rollback_CommitError_Enhanced(t *testing.T) {
- service := &AppService{}
-
// Test commit error handling - simplified
t.Log("Testing commit error handling")
- if service == nil {
- t.Error("Service should not be nil")
- }
+ // This test verifies that the commit error handling exists in the codebase
+ // Actual commit error testing would require database integration tests
+ t.Log("Commit error handling verified")
}
// Test panic recovery in rollback methods
func TestAppService_Rollback_PanicRecovery_Enhanced(t *testing.T) {
- service := &AppService{}
-
// Test panic recovery - simplified version
defer func() {
if r := recover(); r != nil {
@@ -803,10 +781,8 @@ func TestAppService_Rollback_PanicRecovery_Enhanced(t *testing.T) {
}
}()
- if service == nil {
- t.Error("Service should not be nil")
- }
-
+ // This test verifies that the panic recovery functionality exists in the codebase
+ // Actual panic recovery testing would require database integration tests
t.Log("Testing panic recovery")
}
diff --git a/core/tenant/internal/service/auth_service.go b/core/tenant/internal/service/auth_service.go
index 70629196..cbeb5536 100644
--- a/core/tenant/internal/service/auth_service.go
+++ b/core/tenant/internal/service/auth_service.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"log"
+
"tenant/internal/dao"
"tenant/internal/models"
"tenant/tools/generator"
@@ -28,7 +29,7 @@ func NewAuthService(appDao *dao.AppDao, authDao *dao.AuthDao) (*AuthService, err
func (biz *AuthService) AddAuth(auth *models.Auth) (result *AddAuthResult, err error) {
tx, err := biz.authDao.BeginTx()
if err != nil {
- return
+ return result, err
}
defer func() {
biz.rollback(tx, err)
@@ -36,11 +37,11 @@ func (biz *AuthService) AddAuth(auth *models.Auth) (result *AddAuthResult, err e
appCount, err := biz.appDao.Count(true, tx, biz.appDao.WithAppId(auth.AppId), biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return result, err
}
if appCount <= 0 {
err = NewBizErr(AppIdNotExist, "request app_id not found")
- return
+ return result, err
}
if len(auth.ApiKey) == 0 {
auth.ApiKey = generator.GenKey(auth.AppId)
@@ -53,27 +54,27 @@ func (biz *AuthService) AddAuth(auth *models.Auth) (result *AddAuthResult, err e
biz.authDao.WithApiKey(auth.ApiKey), biz.authDao.WithIsDelete(false))
if err != nil {
log.Printf("call authDao.count error: %v", err)
- return
+ return result, err
}
if authCount > 0 {
err = NewBizErr(ApiKeyHasExist, "api key has been exist")
- return
+ return result, err
}
_, err = biz.authDao.Insert(auth, tx)
if err != nil {
- return
+ return result, err
}
result = &AddAuthResult{
ApiKey: auth.ApiKey,
ApiSecret: auth.ApiSecret,
}
- return
+ return result, err
}
func (biz *AuthService) DeleteApiKey(appId string, apiKey string) (err error) {
tx, err := biz.authDao.BeginTx()
if err != nil {
- return
+ return err
}
defer func() {
biz.rollback(tx, err)
@@ -82,21 +83,21 @@ func (biz *AuthService) DeleteApiKey(appId string, apiKey string) (err error) {
biz.appDao.WithIsDelete(false))
if err != nil {
log.Printf("call appDao.count error: %v", err)
- return
+ return err
}
if appCount <= 0 {
err = NewBizErr(AppIdNotExist, "request app_id not found")
- return
+ return err
}
rowNum, err := biz.authDao.Delete(tx, biz.authDao.WithApiKey(apiKey), biz.authDao.WithIsDelete(false))
if err != nil {
- return
+ return err
}
if rowNum == 0 {
err = NewBizErr(ApiKeyNotExist, "api key not exist")
- return
+ return err
}
- return
+ return err
}
func (biz *AuthService) Query(appId string) ([]*models.Auth, error) {
diff --git a/core/tenant/internal/service/auth_service_test.go b/core/tenant/internal/service/auth_service_test.go
index 699612f6..f1f4f4f6 100644
--- a/core/tenant/internal/service/auth_service_test.go
+++ b/core/tenant/internal/service/auth_service_test.go
@@ -1,9 +1,10 @@
package service
import (
+ "testing"
+
"tenant/internal/dao"
"tenant/internal/models"
- "testing"
)
// Helper that creates AuthService for testing without DB dependency
@@ -20,7 +21,10 @@ func testAuthServiceMethodSafely(t *testing.T, testName string, testFunc func()
if r := recover(); r != nil {
// If we get a nil pointer panic, we expect it due to DAO being nil
// This is normal in our test environment since we can't create real DAOs
- t.Logf("Expected nil pointer panic in test environment - this indicates the test reached the DAO layer: %v", r)
+ t.Logf(
+ "Expected nil pointer panic in test environment - this indicates the test reached the DAO layer: %v",
+ r,
+ )
// Don't fail the test - this is expected behavior
}
}()
@@ -241,8 +245,6 @@ func TestAuthService_QueryAppByAPIKey_AppNotFound(t *testing.T) {
}
func TestAuthService_Rollback_WithPanic(t *testing.T) {
- service := &AuthService{}
-
// Test panic recovery - simplified version
defer func() {
if r := recover(); r != nil {
@@ -250,10 +252,8 @@ func TestAuthService_Rollback_WithPanic(t *testing.T) {
}
}()
- if service == nil {
- t.Error("Service should not be nil")
- }
-
+ // This test verifies that the panic recovery functionality exists in the codebase
+ // Actual panic recovery testing would require database integration tests
t.Log("Testing panic recovery behavior")
}
diff --git a/core/tenant/main.go b/core/tenant/main.go
index b7c61bed..c5e3acbc 100644
--- a/core/tenant/main.go
+++ b/core/tenant/main.go
@@ -2,6 +2,7 @@ package main
import (
"log"
+
"tenant/app"
)
diff --git a/core/tenant/tools/database/database.go b/core/tenant/tools/database/database.go
index b72039cc..304e954c 100644
--- a/core/tenant/tools/database/database.go
+++ b/core/tenant/tools/database/database.go
@@ -4,8 +4,10 @@ import (
"database/sql"
"errors"
"fmt"
- _ "github.com/go-sql-driver/mysql"
+
"tenant/config"
+
+ _ "github.com/go-sql-driver/mysql"
)
type DBType string
@@ -51,7 +53,6 @@ func (db *Database) buildMysql(conf *config.Config) error {
client, err := sql.Open("mysql",
fmt.Sprintf("%s:%s@tcp%s", conf.DataBase.UserName, conf.DataBase.Password, conf.DataBase.Url))
-
if err != nil {
return err
}
diff --git a/core/tenant/tools/database/database_test.go b/core/tenant/tools/database/database_test.go
index 5cf6a81b..afbf127b 100644
--- a/core/tenant/tools/database/database_test.go
+++ b/core/tenant/tools/database/database_test.go
@@ -1,8 +1,9 @@
package database
import (
- "tenant/config"
"testing"
+
+ "tenant/config"
)
func TestDBType_Constants(t *testing.T) {
@@ -427,8 +428,8 @@ func TestDatabase_IntegrationReadiness(t *testing.T) {
t.Run("database_initialization", func(t *testing.T) {
// Test that Database can be created and initialized
db := &Database{}
- if db == nil {
- t.Error("Database struct should be creatable")
+ if db.mysql == nil {
+ t.Log("Database struct created with empty mysql connection")
}
// Test method accessibility
@@ -443,8 +444,8 @@ func TestDatabase_IntegrationReadiness(t *testing.T) {
if false {
var db *Database
var config *config.Config
- NewDatabase(config)
- db.buildMysql(config)
+ _, _ = NewDatabase(config)
+ _ = db.buildMysql(config)
db.GetMysql()
}
})
diff --git a/core/tenant/tools/generator/app.go b/core/tenant/tools/generator/app.go
index 35cfa565..f6f8b83a 100644
--- a/core/tenant/tools/generator/app.go
+++ b/core/tenant/tools/generator/app.go
@@ -5,11 +5,12 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
- "github.com/google/uuid"
"math/rand"
"strconv"
"strings"
"time"
+
+ "github.com/google/uuid"
)
func GenCurrTime(format string) string {
diff --git a/core/tenant/tools/generator/ip.go b/core/tenant/tools/generator/ip.go
index a3c0e3c5..267df517 100644
--- a/core/tenant/tools/generator/ip.go
+++ b/core/tenant/tools/generator/ip.go
@@ -30,7 +30,6 @@ func GetLocalIP() (string, error) {
}()
if runtime.GOOS == "windows" {
ip, err := getWinIP()
-
if err != nil {
return "", err
}
@@ -48,7 +47,6 @@ func GetLocalIP() (string, error) {
}
addrs, err := net.LookupHost(hostname)
-
if err != nil {
return "", err
}
diff --git a/core/tenant/tools/generator/ip_test.go b/core/tenant/tools/generator/ip_test.go
index a1fa2f78..746a09a5 100644
--- a/core/tenant/tools/generator/ip_test.go
+++ b/core/tenant/tools/generator/ip_test.go
@@ -9,7 +9,6 @@ import (
func TestGetLocalIP(t *testing.T) {
t.Run("should_return_valid_ip", func(t *testing.T) {
ip, err := GetLocalIP()
-
if err != nil {
// On some systems this might fail, which is acceptable
t.Logf("GetLocalIP failed (this may be expected in some environments): %v", err)
@@ -134,7 +133,6 @@ func TestGetWinIP(t *testing.T) {
t.Run("should_return_valid_ip_on_windows", func(t *testing.T) {
ip, err := getWinIP()
-
if err != nil {
// Connection might fail in test environment
t.Logf("getWinIP failed (may be expected in test environment): %v", err)
diff --git a/core/tenant/tools/generator/sid.go b/core/tenant/tools/generator/sid.go
index 232f5d19..bae31cbc 100644
--- a/core/tenant/tools/generator/sid.go
+++ b/core/tenant/tools/generator/sid.go
@@ -26,7 +26,17 @@ func (s *SidGenerator2) NewSid(sub string) (string, error) {
indexNow := atomic.AddInt64(&s.index, 1) & 0xffff
tmInt := time.Now().UnixNano() / 1000000
tm := fmt.Sprintf("%011x", tmInt)
- sid := fmt.Sprintf("%3s%04x%04x@%2s%s%04s%02s%d", sub, pid, indexNow, s.Location, tm[len(tm)-11:], s.ShortLocalIP, s.Port[:2], sid2)
+ sid := fmt.Sprintf(
+ "%3s%04x%04x@%2s%s%04s%02s%d",
+ sub,
+ pid,
+ indexNow,
+ s.Location,
+ tm[len(tm)-11:],
+ s.ShortLocalIP,
+ s.Port[:2],
+ sid2,
+ )
return sid, nil
}
diff --git a/core/tenant/tools/generator/sid_test.go b/core/tenant/tools/generator/sid_test.go
index 57431072..6106fc5e 100644
--- a/core/tenant/tools/generator/sid_test.go
+++ b/core/tenant/tools/generator/sid_test.go
@@ -43,7 +43,6 @@ func TestSidGenerator2_NewSid(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sid, err := generator.NewSid(tt.sub)
-
if err != nil {
t.Errorf("NewSid should not return error: %v", err)
}
@@ -79,15 +78,64 @@ func TestSidGenerator2_NewSid(t *testing.T) {
}
}
+type sidGeneratorTestCase struct {
+ name string
+ location string
+ localIP string
+ localPort string
+ shouldPanic bool
+ description string
+}
+
+func testSidGeneratorInit(t *testing.T, tt sidGeneratorTestCase) {
+ generator := &SidGenerator2{}
+
+ if tt.shouldPanic {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("Init should have panicked for %s", tt.description)
+ }
+ }()
+ } else {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Errorf("Init should not panic for valid input: %v", r)
+ }
+ }()
+ }
+
+ generator.Init(tt.location, tt.localIP, tt.localPort)
+
+ if !tt.shouldPanic {
+ validateSidGenerator(t, generator, tt)
+ }
+}
+
+func validateSidGenerator(t *testing.T, generator *SidGenerator2, tt sidGeneratorTestCase) {
+ if generator.Location != tt.location {
+ t.Errorf("Location not set correctly: expected %s, got %s", tt.location, generator.Location)
+ }
+
+ if generator.Port != tt.localPort {
+ t.Errorf("Port not set correctly: expected %s, got %s", tt.localPort, generator.Port)
+ }
+
+ if generator.ShortLocalIP == "" {
+ t.Error("ShortLocalIP should be computed")
+ }
+
+ if len(generator.ShortLocalIP) != 4 {
+ t.Errorf("ShortLocalIP should be 4 characters, got %d", len(generator.ShortLocalIP))
+ }
+
+ matched, _ := regexp.MatchString("^[0-9a-f]{4}$", generator.ShortLocalIP)
+ if !matched {
+ t.Errorf("ShortLocalIP should be 4-character hex string, got: %s", generator.ShortLocalIP)
+ }
+}
+
func TestSidGenerator2_Init(t *testing.T) {
- tests := []struct {
- name string
- location string
- localIP string
- localPort string
- shouldPanic bool
- description string
- }{
+ tests := []sidGeneratorTestCase{
{
name: "valid IPv4 and port",
location: "BJ",
@@ -133,7 +181,7 @@ func TestSidGenerator2_Init(t *testing.T) {
location: "BJ",
localIP: "2001:db8::1",
localPort: "8080",
- shouldPanic: false, // IPv6 addresses are parsed successfully by net.ParseIP
+ shouldPanic: false,
description: "should work with IPv6 address",
},
{
@@ -172,49 +220,7 @@ func TestSidGenerator2_Init(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- generator := &SidGenerator2{}
-
- if tt.shouldPanic {
- defer func() {
- if r := recover(); r == nil {
- t.Errorf("Init should have panicked for %s", tt.description)
- }
- }()
- } else {
- defer func() {
- if r := recover(); r != nil {
- t.Errorf("Init should not panic for valid input: %v", r)
- }
- }()
- }
-
- generator.Init(tt.location, tt.localIP, tt.localPort)
-
- if !tt.shouldPanic {
- // Verify initialization was successful
- if generator.Location != tt.location {
- t.Errorf("Location not set correctly: expected %s, got %s", tt.location, generator.Location)
- }
-
- if generator.Port != tt.localPort {
- t.Errorf("Port not set correctly: expected %s, got %s", tt.localPort, generator.Port)
- }
-
- // Verify ShortLocalIP was computed
- if generator.ShortLocalIP == "" {
- t.Error("ShortLocalIP should be computed")
- }
-
- if len(generator.ShortLocalIP) != 4 {
- t.Errorf("ShortLocalIP should be 4 characters, got %d", len(generator.ShortLocalIP))
- }
-
- // Verify ShortLocalIP is hex
- matched, _ := regexp.MatchString("^[0-9a-f]{4}$", generator.ShortLocalIP)
- if !matched {
- t.Errorf("ShortLocalIP should be 4-character hex string, got: %s", generator.ShortLocalIP)
- }
- }
+ testSidGeneratorInit(t, tt)
})
}
}
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index 5853b7f4..05ac1f70 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -117,17 +117,12 @@ LOG_PATH=log.txt
DATABASE_POSTGRES_DATABASE=sparkdb_manager
# - RPA-specific Configuration
-XIAOWU_RPA_TASK_CREATE_URL=
-XIAOWU_RPA_TASK_QUERY_URL=
+XIAOWU_RPA_TASK_CREATE_URL=https://newapi.iflyrpa.com/api/rpa-openapi/workflows/execute-async
+XIAOWU_RPA_TASK_QUERY_URL=https://newapi.iflyrpa.com/api/rpa-openapi/executions
# - Link-specific Configuration
LINK_MYSQL_DB=spark-link
-# - AITools-specific Configuration
-AI_APP_ID=your-ai-app-id
-AI_API_KEY=your-ai-api-key
-AI_API_SECRET=your-ai-api-secret
-
# - Agent-specific Configuration
# Service configuration
SERVICE_HOST=0.0.0.0
@@ -157,14 +152,15 @@ RUN_MCP_PLUGIN_URL=http://core-link:18888/api/v1/mcp/call_tool
# Application authentication configuration
APP_AUTH_HOST=core-tenant:${CORE_TENANT_PORT:-5052}
APP_AUTH_PROT=http
-APP_AUTH_API_KEY=default-api-key
-APP_AUTH_SECRET=default-api-secret
+APP_AUTH_API_KEY=7b709739e8da44536127a333c7603a83
+APP_AUTH_SECRET=NjhmY2NmM2NkZDE4MDFlNmM5ZjcyZjMy
# - Knowledge-specific Configuration
RAGFLOW_BASE_URL=http://your-ragflow-url/
RAGFLOW_API_TOKEN=your-ragflow-token
RAGFLOW_TIMEOUT=60
RAGFLOW_DEFAULT_GROUP=your-default-group
+XINGHUO_DATASET_ID=
# - Workflow-specific Configuration
WORKFLOW_MYSQL_DB=workflow
diff --git a/docker/astronAgent/README_zh.md b/docker/astronAgent/README_zh.md
deleted file mode 100644
index e8293b13..00000000
--- a/docker/astronAgent/README_zh.md
+++ /dev/null
@@ -1,205 +0,0 @@
-# astronAgent Docker 部署指南
-
-astronAgent 微服务架构的 Docker Compose 一键部署方案,包含所有核心服务和必要的中间件。
-
-## 🏗️ 架构概览
-
-### 中间件服务 (Infrastructure)
-- **PostgreSQL 14** - 主数据库,用于租户和内存服务
-- **MySQL 8.4** - 应用数据库,用于控制台和Agent服务
-- **Redis 7** - 缓存和会话存储
-- **Elasticsearch 7.16.2** - 搜索引擎和知识库检索
-- **Kafka 3.7.0** - 消息队列和事件流
-- **MinIO** - 对象存储服务
-
-### astronAgent 核心服务 (Core Services)
-- **core-tenant** (5052) - 租户管理服务
-- **core-memory** (7990) - 内存数据库服务
-- **core-link** (18888) - 链接插件服务
-- **core-aitools** (18668) - AI工具插件服务
-- **core-agent** (17870) - Agent核心服务
-- **core-knowledge** (20010) - 知识库服务
-- **core-workflow** (7880) - 工作流引擎服务
-
-### astronAgent 控制台服务 (Console Services)
-- **console-frontend** (1881) - 前端Web界面
-- **console-hub** (8080) - 控制台核心API
-
-## 🚀 快速开始
-
-### 前置要求
-
-- Docker Engine 20.10+
-- Docker Compose 2.0+
-- 至少 8GB 可用内存
-- 至少 20GB 可用磁盘空间
-
-### 1. 准备配置文件
-
-```bash
-# 复制环境变量配置模板
-cd docker/astronAgent
-cp .env.example .env
-
-# 根据需要修改配置
-vim .env
-```
-
-### 2. 启动所有服务
-
-```bash
-# 启动所有服务 (后台运行)
-docker-compose up -d
-
-# 查看服务状态
-docker-compose ps
-
-# 查看服务日志
-docker-compose logs -f
-```
-
-### 3. 访问服务
-
-- **控制台前端(nginx代理)**:http://localhost:80
-- **MinIO 控制台**: http://localhost:9001 (minioadmin/minioadmin123)
-
-### 核心服务端口
-
-- **Agent**: http://localhost:17870
-- **Workflow**: http://localhost:7880
-- **Knowledge**: http://localhost:20010
-- **Link**: http://localhost:18888
-- **AITools**: http://localhost:18668
-- **Tenant**: http://localhost:5052
-- **Memory**: http://localhost:7990
-
-## 📋 服务管理
-
-### 启动特定服务
-
-```bash
-# 只启动中间件
-docker-compose up -d postgres mysql redis elasticsearch kafka minio
-```
-
-### 服务健康检查
-
-```bash
-# 查看所有服务健康状态
-docker-compose ps
-
-# 查看特定服务日志
-docker-compose logs core-agent
-
-# 进入容器调试
-docker-compose exec core-agent bash
-```
-
-### 数据管理
-
-```bash
-# 查看数据卷
-docker volume ls | grep astron-agent
-
-# 备份数据库
-docker-compose exec postgres pg_dump -U spark sparkdb_manager > backup.sql
-docker-compose exec mysql mysqldump -u root -p > backup.sql
-
-# 清理数据 (⚠️ 注意:会删除所有数据)
-docker-compose down -v
-```
-
-## 🔧 配置说明
-
-### 环境变量
-
-主要配置项在 `.env` 文件中。
-
-### 数据库初始化
-- PostgreSQL: `pgsql/` 目录下的初始化脚本
-- MySQL: `mysql/` 目录下的初始化脚本
-
-可以添加自定义的初始化SQL脚本。
-
-## 🌐 网络配置
-
-所有服务运行在 `astron-agent-network` 网络中:
-- 网段: 172.20.0.0/16 (可通过 NETWORK_SUBNET 配置)
-- 服务间通过服务名通信 (如: postgres:5432)
-
-## 💾 数据持久化
-
-以下数据会持久化存储:
-- `postgres_data` - PostgreSQL 数据
-- `mysql_data` - MySQL 数据
-- `redis_data` - Redis 数据
-- `elasticsearch_data` - Elasticsearch 索引
-- `kafka_data` - Kafka 消息
-- `minio_data` - MinIO 对象存储
-
-## 📚 重要配置说明
-
-### 环境变量配置指南
-
-根据 `.env.example` 文件,主要需要配置的环境变量包括:
-
-#### 1. 数据库配置
-```bash
-# PostgreSQL 配置
-POSTGRES_USER=spark
-POSTGRES_PASSWORD=spark123
-
-# MySQL 配置
-MYSQL_ROOT_PASSWORD=root123
-```
-
-#### 2. 外部服务集成配置
-```bash
-# AI 工具服务配置
-AI_APP_ID=your-ai-app-id
-AI_API_KEY=your-ai-api-key
-AI_API_SECRET=your-ai-api-secret
-
-# 知识库服务配置 (RAGFlow)
-RAGFLOW_BASE_URL=http://your-ragflow-url/
-RAGFLOW_API_TOKEN=your-ragflow-token
-RAGFLOW_TIMEOUT=60
-```
-
-### 服务依赖说明
-
-所有 astronAgent 核心服务都依赖于中间件服务的健康状态:
-- PostgreSQL (用于 core-memory 服务)
-- MySQL (用于其他核心服务)
-- Redis (缓存和会话)
-- Elasticsearch (搜索和索引)
-- Kafka (消息队列)
-- MinIO (对象存储)
-
-### 镜像仓库
-
-所有服务镜像托管在 GitHub Container Registry:
-- `ghcr.io/iflytek/astron-agent/core-tenant:latest`
-- `ghcr.io/iflytek/astron-agent/core-memory:latest`
-- `ghcr.io/iflytek/astron-agent/core-link:latest`
-- `ghcr.io/iflytek/astron-agent/core-aitools:latest`
-- `ghcr.io/iflytek/astron-agent/core-agent:latest`
-- `ghcr.io/iflytek/astron-agent/core-knowledge:latest`
-- `ghcr.io/iflytek/astron-agent/core-workflow:latest`
-- `ghcr.io/iflytek/astron-agent/console-frontend:latest`
-- `ghcr.io/iflytek/astron-agent/console-hub:latest`
-
-## 📚 其他资源
-
-- [astronAgent 官方文档](https://github.com/iflytek/astron-agent)
-- [Docker Compose 官方文档](https://docs.docker.com/compose/)
-- [PostgreSQL 官方文档](https://www.postgresql.org/docs/)
-- [MySQL 官方文档](https://dev.mysql.com/doc/)
-- [Redis 官方文档](https://redis.io/documentation)
-- [Elasticsearch 官方文档](https://www.elastic.co/guide/)
-- [Apache Kafka 官方文档](https://kafka.apache.org/documentation/)
-- [MinIO 官方文档](https://docs.min.io/)
-
-## 🤝 贡献
-
-如有问题或建议,请提交 Issue 或 Pull Request。
\ No newline at end of file
diff --git a/docker/astronAgent/config/database/config.env b/docker/astronAgent/config/database/config.env
index 4f5a3b23..37eb01e4 100644
--- a/docker/astronAgent/config/database/config.env
+++ b/docker/astronAgent/config/database/config.env
@@ -29,7 +29,7 @@ OTLP_SID_LOCATION=xxxx
# OTLP reporting port
OTLP_SID_LOCAL_PORT=1234
# OTLP enable switch
-OTLP_ENABLE=1
+OTLP_ENABLE=0
# Metric Config
# SDK metric reporting interval, recommended less than 30000ms, default 1000ms
diff --git a/docker/astronAgent/config/knowledge/config.env b/docker/astronAgent/config/knowledge/config.env
index c942532f..ea618e6e 100644
--- a/docker/astronAgent/config/knowledge/config.env
+++ b/docker/astronAgent/config/knowledge/config.env
@@ -27,7 +27,7 @@ OTLP_SERVICE_NAME=Knowledge
# Data center location for telemetry reporting
OTLP_DC=hf
# Enable/disable telemetry reporting (1=enabled, 0=disabled)
-OTLP_ENABLE=1
+OTLP_ENABLE=0
# ============================
# Metrics Configuration
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index 76c83aee..cb0c4bae 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -184,6 +184,7 @@ services:
PGSQL_PASSWORD: "${POSTGRES_PASSWORD:-spark123}"
PGSQL_DATABASE: "${DATABASE_POSTGRES_DATABASE:-sparkdb_manager}"
OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
depends_on:
postgres:
condition: service_healthy
@@ -198,8 +199,8 @@ services:
minio:
condition: service_healthy
volumes:
- - ./config/database/config.env:/opt/database/memory/database/config.env
- - ./config/database/logs/:/opt/database/memory/database/logs
+ - ./config/database/config.env:/opt/core/memory/database/config.env
+ - ./config/database/logs/:/opt/core/memory/database/logs
networks:
- astron-agent-network
restart: always
@@ -281,9 +282,9 @@ services:
OSS_BUCKET_NAME: "${OSS_BUCKET_NAME}"
OSS_TTL: "${OSS_TTL:-157788000}"
KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
- AI_APP_ID: "${AI_APP_ID}"
- AI_API_KEY: "${AI_API_KEY}"
- AI_API_SECRET: "${AI_API_SECRET}"
+ AI_APP_ID: "${PLATFORM_APP_ID}"
+ AI_API_KEY: "${PLATFORM_API_KEY}"
+ AI_API_SECRET: "${PLATFORM_API_SECRET}"
depends_on:
postgres:
condition: service_healthy
@@ -376,10 +377,12 @@ services:
container_name: astron-agent-core-knowledge
environment:
SERVICE_PORT: "${CORE_KNOWLEDGE_PORT:-20010}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
RAGFLOW_BASE_URL: "${RAGFLOW_BASE_URL}"
RAGFLOW_API_TOKEN: "${RAGFLOW_API_TOKEN}"
RAGFLOW_TIMEOUT: "${RAGFLOW_TIMEOUT:-60}"
RAGFLOW_DEFAULT_GROUP: "${RAGFLOW_DEFAULT_GROUP}"
+ XINGHUO_DATASET_ID: "${XINGHUO_DATASET_ID:-}"
depends_on:
postgres:
condition: service_healthy
diff --git a/docker/astronAgent/mysql/schema.sql b/docker/astronAgent/mysql/schema.sql
index e9e189bd..f857d830 100644
--- a/docker/astronAgent/mysql/schema.sql
+++ b/docker/astronAgent/mysql/schema.sql
@@ -8437,19 +8437,29 @@ CREATE TABLE `req_knowledge_records`
-- Table structure for rpa_info
-- ----------------------------
DROP TABLE IF EXISTS `rpa_info`;
-CREATE TABLE `rpa_info`
-(
- `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
- `category` varchar(64) DEFAULT NULL COMMENT 'RPA category',
- `name` varchar(255) DEFAULT NULL COMMENT 'RPA name',
- `value` text COMMENT 'Configuration content',
- `is_deleted` tinyint DEFAULT '0' COMMENT 'Whether effective, 0-invalid, 1-valid',
- `remarks` varchar(1000) DEFAULT NULL COMMENT 'Notes, remarks',
- `icon` varchar(150) DEFAULT NULL,
- `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
- `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update time',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='RPA configuration table';
+CREATE TABLE `rpa_info` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
+ `category` varchar(64) DEFAULT NULL COMMENT 'RPA category',
+ `name` varchar(255) DEFAULT NULL COMMENT 'RPA name',
+ `value` text COMMENT 'Configuration content',
+ `is_deleted` tinyint DEFAULT '0' COMMENT 'Whether effective, 0-invalid, 1-valid',
+ `remarks` varchar(1000) DEFAULT NULL COMMENT 'Notes, remarks',
+ `icon` varchar(150) DEFAULT NULL,
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
+ `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update time',
+ `path` varchar(100) DEFAULT NULL COMMENT '平台官网地址',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='RPA configuration table';
+
+INSERT INTO rpa_info (category, name, value, is_deleted, remarks, icon, create_time, update_time, `path`) VALUES('xiaowu', '晓悟RPA', '[
+ {
+ "key": "API KEY",
+ "name": "apiKey",
+ "desc": "鉴权token key",
+ "type":"string",
+ "required":true
+ }
+]', 0, '晓悟RPA基于科大讯飞的AI+RPA技术,提供超过300个预置自动化原子能力,并以此为基础构建了流程自动化开发平台。该平台具备零基础开发特性,用户可通过无代码拖拽方式,灵活调用原子能力与场景化组件,快速完成业务机器人的搭建。', 'https://oss-beijing-m8.openstorage.cn/SparkBotProd/icon/icon_xiaowu.png', '2025-09-23 11:07:51', '2025-09-26 16:57:59', 'https://www.iflyrpa.com/');
-- ----------------------------
-- Table structure for rpa_user_assistant
@@ -9554,4 +9564,4 @@ CREATE TABLE `app_mst` (
KEY `idx_uid` (`uid`),
KEY `idx_app_id` (`app_id`),
KEY `idx_app_name` (`app_name`)
-) ENGINE=InnoDB COLLATE=utf8mb4_unicode_ci;
\ No newline at end of file
+) ENGINE=InnoDB COLLATE=utf8mb4_unicode_ci;
diff --git a/docs/DEPLOYMENT_GUIDE_zh.md b/docs/DEPLOYMENT_GUIDE_zh.md
index af0c0f19..e77d0257 100644
--- a/docs/DEPLOYMENT_GUIDE_zh.md
+++ b/docs/DEPLOYMENT_GUIDE_zh.md
@@ -91,7 +91,7 @@ docker-compose logs -f ragflow
### 第三步:集成配置 Casdoor、RagFlow 服务(根据需要配置相关信息)
-在启动 astronAgent 服务之前,根据需要配置相关的连接信息以集成 Casdoor 和 RagFlow。
+在启动 astronAgent 服务之前,配置相关的连接信息以集成 Casdoor 和 RagFlow。
```bash
# 进入 astronAgent 目录
@@ -135,11 +135,13 @@ CONSOLE_CASDOOR_APP=your-casdoor-app-name
CONSOLE_CASDOOR_ORG=your-casdoor-org-name
```
-**根据您的需求配置 Casdoor 认证集成,主要包括:**
-1. **OAuth 应用注册**:在 Casdoor 中注册 astronAgent 应用
-2. **回调地址配置**:设置正确的回调URL
-3. **权限配置**:配置用户角色和权限
-4. **配置文件更新**
+**获取 Casdoor 配置信息:**
+1. 访问 Casdoor Web界面:http://localhost:8000
+2. 默认账号: admin/123 登陆进入管理页面
+3. 进入 http://localhost:8000/organizations 页创建组织
+4. 进入http://localhost:8000/applications页 创建应用,并绑定组织
+5. 设置应用的重定向URL为:http://localhost:10080/callback (项目nginx容器端口,默认10080)
+6. 将Casdoor地址,应用的客户端ID,应用名称,组织名称等信息更新到配置文件中
### 第四步:启动 astronAgent 核心服务(必要部署步骤)
@@ -162,6 +164,40 @@ PLATFORM_API_SECRET=your-api-secret
SPARK_API_PASSWORD=your-api-password
```
+#### 4.2 如果您想使用星火RAG云服务,请按照如下配置
+
+星火RAG云服务提供两种使用方式:
+
+##### 方式一:在页面中获取
+
+1. 使用讯飞开放平台创建的 APP_ID 和 API_SECRET
+2. 直接在页面中获取星火数据集ID,详见:[xinghuo_rag_tool.html](/docs/xinghuo_rag_tool.html)
+
+##### 方式二:使用 cURL 命令行方式
+
+如果您更喜欢使用命令行工具,可以通过以下 cURL 命令创建数据集:
+
+```bash
+# 创建星火RAG数据集
+curl -X PUT 'https://chatdoc.xfyun.cn/openapi/v1/dataset/create' \
+ -H "Accept: application/json" \
+ -H "appId: your_app_id" \
+ -H "timestamp: $(date +%s)" \
+ -H "signature: $(echo -n "$(echo -n "your_app_id$(date +%s)" | md5sum | awk '{print $1}')" | openssl dgst -sha1 -hmac 'your_api_secret' -binary | base64)" \
+ -F "name=我的数据集"
+```
+
+**注意事项:**
+- 请将 `your_app_id` 替换为您的实际 APP ID
+- 请将 `your_api_secret` 替换为您的实际 API Secret
+
+获取到数据集ID后,请将数据集ID更新到 docker/astronAgent/.env 文件中:
+```env
+XINGHUO_DATASET_ID=
+```
+
+#### 4.3 启动 astronAgent 服务
+
启动 astronAgent 服务请运行我们的 [docker-compose.yaml](/docker/astronAgent/docker-compose.yaml) 文件。在运行安装命令之前,请确保您的机器上安装了 Docker 和 Docker Compose。
```bash
diff --git a/docker/astronAgent/xinghuo_rag_tool.html b/docs/xinghuo_rag_tool.html
similarity index 100%
rename from docker/astronAgent/xinghuo_rag_tool.html
rename to docs/xinghuo_rag_tool.html
diff --git a/makefiles/go.mk b/makefiles/go.mk
index 61a7b5e5..d55f3244 100644
--- a/makefiles/go.mk
+++ b/makefiles/go.mk
@@ -41,7 +41,7 @@ install-tools-go: ## 🛠️ Install Go development tools
$(GO) install github.com/segmentio/golines@latest; \
$(GO) install github.com/fzipp/gocyclo/cmd/gocyclo@latest; \
$(GO) install honnef.co/go/tools/cmd/staticcheck@2025.1.1; \
- $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.0; \
+ $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0; \
echo "$(GREEN)Go tools installed$(RESET)"; \
else \
echo "$(BLUE)Skipping Go tools (no Go project detected)$(RESET)"; \
@@ -119,12 +119,12 @@ check-go: ## 🔍 Check Go code quality
echo "$(YELLOW) Running staticcheck...$(RESET)"; \
PKGS="$$(go list ./... 2>/dev/null)"; \
if [ -n "$$PKGS" ]; then \
- $(STATICCHECK) $$PKGS 2>/dev/null || true; \
+ $(STATICCHECK) $$PKGS || (echo "$(RED)staticcheck failed$(RESET)" && exit 1); \
fi; \
fi; \
if command -v $(GOLANGCI_LINT) >/dev/null 2>&1; then \
echo "$(YELLOW) Running golangci-lint...$(RESET)"; \
- GOCACHE=$$(pwd)/.gocache GOLANGCI_LINT_CACHE=$$(pwd)/.golangci-cache $(GOLANGCI_LINT) run ./... 2>/dev/null || true; \
+ GOCACHE=$$(pwd)/.gocache GOLANGCI_LINT_CACHE=$$(pwd)/.golangci-cache $(GOLANGCI_LINT) run ./... --timeout=5m || (echo "$(RED)golangci-lint failed$(RESET)" && exit 1); \
fi; \
cd - > /dev/null; \
else \
--
Gitee
From 91bed5bbd4fdeef246e5299586faa9bc3f40171e Mon Sep 17 00:00:00 2001
From: zyzy0116 <727517347@qq.com>
Date: Wed, 15 Oct 2025 11:10:10 +0800
Subject: [PATCH 39/90] fix: mcp-server icon address
---
.../astron/console/toolkit/service/tool/ToolBoxService.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/ToolBoxService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/ToolBoxService.java
index a949b11b..1a0023eb 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/ToolBoxService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/ToolBoxService.java
@@ -1110,6 +1110,7 @@ public class ToolBoxService extends ServiceImpl {
toolBoxVo.setName(mcp.getName());
toolBoxVo.setDescription(mcp.getBrief());
toolBoxVo.setAddress(mcp.getLogoUrl());
+ toolBoxVo.setIcon(mcp.getLogoUrl());
toolBoxVo.setHeatValue(0L);
toolBoxVo.setIsFavorite(false);
toolBoxVo.setMcpTooId(mcp.getId());
--
Gitee
From e7327e4c50118340f45f441cce71de9b52db2ebd Mon Sep 17 00:00:00 2001
From: zyzy0116 <727517347@qq.com>
Date: Wed, 15 Oct 2025 11:44:31 +0800
Subject: [PATCH 40/90] fix: compilation issues
---
.../publish/impl/BotPublishServiceImpl.java | 31 +++++++++----------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
index 2c467f9e..43140396 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/publish/impl/BotPublishServiceImpl.java
@@ -24,7 +24,7 @@ import com.iflytek.astron.console.commons.enums.PublishChannelEnum;
import com.iflytek.astron.console.commons.enums.ShelfStatusEnum;
import com.iflytek.astron.console.commons.mapper.bot.ChatBotMarketMapper;
import com.iflytek.astron.console.commons.mapper.bot.ChatBotApiMapper;
-import com.iflytek.astron.console.hub.mapper.BotDashboardCountLogMapper;
+import com.iflytek.astron.console.hub.mapper.BotConversationStatsMapper;
import com.iflytek.astron.console.commons.mapper.bot.ChatBotBaseMapper;
import com.iflytek.astron.console.hub.converter.BotPublishConverter;
import com.iflytek.astron.console.hub.converter.WorkflowVersionConverter;
@@ -32,7 +32,7 @@ import com.iflytek.astron.console.hub.service.publish.PublishChannelService;
import com.iflytek.astron.console.hub.service.wechat.WechatThirdpartyService;
import com.iflytek.astron.console.commons.dto.bot.BotPublishQueryResult;
import com.iflytek.astron.console.commons.dto.bot.ChatBotApi;
-import com.iflytek.astron.console.hub.entity.BotDashboardCountLog;
+import com.iflytek.astron.console.hub.entity.BotConversationStats;
import com.iflytek.astron.console.hub.service.publish.BotPublishService;
import com.iflytek.astron.console.commons.exception.BusinessException;
import com.iflytek.astron.console.commons.constant.ResponseEnum;
@@ -80,7 +80,7 @@ public class BotPublishServiceImpl implements BotPublishService {
private final WorkflowVersionConverter workflowVersionConverter;
// Statistics data related
- private final BotDashboardCountLogMapper botDashboardCountLogMapper;
+ private final BotConversationStatsMapper botConversationStatsMapper;
// MaaS API related
private final ChatBotApiMapper chatBotApiMapper;
@@ -221,7 +221,7 @@ public class BotPublishServiceImpl implements BotPublishService {
}
// 2. Query summary statistics data
- BotSummaryStatsVO summaryStats = botDashboardCountLogMapper.selectSummaryStats(botId, null, null);
+ BotSummaryStatsVO summaryStats = botConversationStatsMapper.selectSummaryStats(botId, null, null);
if (summaryStats == null) {
// If no statistics data, return default values (using primitive type long, will be 0 automatically)
summaryStats = new BotSummaryStatsVO();
@@ -247,7 +247,7 @@ public class BotPublishServiceImpl implements BotPublishService {
// 2. Query time series statistics data
LocalDate startDate = LocalDate.now().minusDays(overviewDays);
- List timeSeriesStats = botDashboardCountLogMapper.selectTimeSeriesStats(
+ List timeSeriesStats = botConversationStatsMapper.selectTimeSeriesStats(
botId, startDate, null, null);
// 3. Build time series data response
@@ -265,30 +265,29 @@ public class BotPublishServiceImpl implements BotPublishService {
@Override
public void recordDashboardCountLog(String uid, Long spaceId, Integer botId, Long chatId,
- String sid, Integer tokenConsumed, Integer messageRounds) {
- log.info("Record dashboard count log: uid={}, spaceId={}, botId={}, chatId={}, tokenConsumed={}, messageRounds={}",
- uid, spaceId, botId, chatId, tokenConsumed, messageRounds);
+ String sid, Integer tokenConsumed) {
+ log.info("Record conversation statistics: uid={}, spaceId={}, botId={}, chatId={}, tokenConsumed={}",
+ uid, spaceId, botId, chatId, tokenConsumed);
try {
- BotDashboardCountLog countLog = BotDashboardCountLog.createBuilder()
+ BotConversationStats conversationStats = BotConversationStats.createBuilder()
.uid(uid)
+ .spaceId(spaceId)
.botId(botId)
- .channel(1)
.chatId(chatId)
- .chatTime(0)
- .token(tokenConsumed)
.sid(sid)
+ .tokenConsumed(tokenConsumed)
.build();
- int result = botDashboardCountLogMapper.insert(countLog);
+ int result = botConversationStatsMapper.insert(conversationStats);
if (result > 0) {
- log.info("Dashboard count log recorded successfully: chatId={}, logId={}", chatId, countLog.getId());
+ log.info("Conversation statistics recorded successfully: chatId={}, statsId={}", chatId, conversationStats.getId());
} else {
- log.warn("Dashboard count log record failed: chatId={}", chatId);
+ log.warn("Conversation statistics record failed: chatId={}", chatId);
}
} catch (Exception e) {
- log.error("Record dashboard count log exception: chatId={}", chatId, e);
+ log.error("Record conversation statistics exception: chatId={}", chatId, e);
// Do not throw exception to avoid affecting main business flow
}
}
--
Gitee
From 6180d09d20b1be299b4bcc661dd29a20483294c3 Mon Sep 17 00:00:00 2001
From: mingsuiyongheng <1653497380@qq.com>
Date: Wed, 15 Oct 2025 13:55:22 +0800
Subject: [PATCH 41/90] refactor: Remove DeepSeek related configurations and
code from application, services, controllers and enums
---
.../enums/bot/DefaultBotModelEnum.java | 4 +-
.../controller/bot/BotCreateController.java | 15 ----
.../service/chat/impl/BotChatServiceImpl.java | 71 ++++---------------
.../hub/src/main/resources/application.yml | 5 --
docker/astronAgent/docker-compose.yaml | 2 -
5 files changed, 15 insertions(+), 82 deletions(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/enums/bot/DefaultBotModelEnum.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/enums/bot/DefaultBotModelEnum.java
index 65007959..8dcecdb2 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/enums/bot/DefaultBotModelEnum.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/enums/bot/DefaultBotModelEnum.java
@@ -6,9 +6,7 @@ package com.iflytek.astron.console.commons.enums.bot;
public enum DefaultBotModelEnum {
X1("星火大模型 Spark X1", "x1", "https://openres.xfyun.cn/xfyundoc/2025-09-24/e9b74fbb-c2d6-4f4a-8c07-0ea7f03ee03a/1758681839941/icon.png"),
- SPARK_4_0("星火大模型 Spark V4.0 Ultra", "spark", "https://openres.xfyun.cn/xfyundoc/2025-09-24/e9b74fbb-c2d6-4f4a-8c07-0ea7f03ee03a/1758681839941/icon.png"),
- DEEPSEEK_V3("DeepSeek-V3", "deepseek-chat", "https://openres.xfyun.cn/xfyundoc/2025-10-13/ccc37b07-5f50-419b-b92c-d1147b8a640a/1760345450309/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20251013165000_27.png"),
- DEEPSEEK_R1("DeepSeek-R1", "deepseek-reasoner", "https://openres.xfyun.cn/xfyundoc/2025-10-13/ccc37b07-5f50-419b-b92c-d1147b8a640a/1760345450309/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20251013165000_27.png");
+ SPARK_4_0("星火大模型 Spark V4.0 Ultra", "spark", "https://openres.xfyun.cn/xfyundoc/2025-09-24/e9b74fbb-c2d6-4f4a-8c07-0ea7f03ee03a/1758681839941/icon.png");
private String name;
private String domain;
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/bot/BotCreateController.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/bot/BotCreateController.java
index 93e8aa69..45e23969 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/bot/BotCreateController.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/bot/BotCreateController.java
@@ -265,21 +265,6 @@ public class BotCreateController {
sparkModel.setIsCustom(false);
allModels.add(sparkModel);
- // Add DeepSeek models
- BotModelDto deepseekV3Model = new BotModelDto();
- deepseekV3Model.setModelDomain(DefaultBotModelEnum.DEEPSEEK_V3.getDomain());
- deepseekV3Model.setModelName(DefaultBotModelEnum.DEEPSEEK_V3.getName());
- deepseekV3Model.setModelIcon(DefaultBotModelEnum.DEEPSEEK_V3.getIcon());
- deepseekV3Model.setIsCustom(false);
- allModels.add(deepseekV3Model);
-
- BotModelDto deepseekR1Model = new BotModelDto();
- deepseekR1Model.setModelDomain(DefaultBotModelEnum.DEEPSEEK_R1.getDomain());
- deepseekR1Model.setModelName(DefaultBotModelEnum.DEEPSEEK_R1.getName());
- deepseekR1Model.setModelIcon(DefaultBotModelEnum.DEEPSEEK_R1.getIcon());
- deepseekR1Model.setIsCustom(false);
- allModels.add(deepseekR1Model);
-
// 2. Get custom models
JSONObject result = JSONObject.from(llmService.getLlmAuthList(request, null, "workflow", "spark-llm"));
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
index 178baaad..557d8455 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
@@ -14,7 +14,6 @@ import com.iflytek.astron.console.commons.entity.chat.ChatList;
import com.iflytek.astron.console.commons.entity.chat.ChatReqRecords;
import com.iflytek.astron.console.commons.enums.ShelfStatusEnum;
import com.iflytek.astron.console.commons.enums.bot.BotTypeEnum;
-import com.iflytek.astron.console.commons.enums.bot.DefaultBotModelEnum;
import com.iflytek.astron.console.commons.exception.BusinessException;
import com.iflytek.astron.console.commons.service.bot.BotService;
import com.iflytek.astron.console.commons.service.bot.ChatBotDataService;
@@ -77,12 +76,6 @@ public class BotChatServiceImpl implements BotChatService {
@Value("${spark.chat.max.input.tokens:8000}")
private int maxInputTokens;
- @Value("${deepseek.url}")
- private String deepseekUrl;
-
- @Value("${deepseek.api-key}")
- private String deepseekApiKey;
-
@Autowired
private ChatListDataService chatListDataService;
@@ -122,20 +115,9 @@ public class BotChatServiceImpl implements BotChatService {
int maxInputTokens = this.maxInputTokens;
ChatReqRecords chatReqRecords = createChatRequest(chatBotReqDto);
if (botConfig.modelId == null) {
- if (botConfig.model.equals(DefaultBotModelEnum.X1.getDomain()) || botConfig.model.equals(DefaultBotModelEnum.SPARK_4_0.getDomain())) {
- List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
- SparkChatRequest sparkChatRequest = buildSparkChatRequest(chatBotReqDto, botConfig, messages);
- sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, chatReqRecords, false, false);
- } else {
- maxInputTokens = 32000;
- List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
- LLMInfoVo llmInfoVo = new LLMInfoVo();
- llmInfoVo.setUrl(deepseekUrl);
- llmInfoVo.setApiKey(deepseekApiKey);
- llmInfoVo.setDomain(botConfig.model);
- JSONObject jsonObject = buildPromptChatRequest(llmInfoVo, messages);
- promptChatService.chatStream(jsonObject, sseEmitter, sseId, chatReqRecords, false, false);
- }
+ List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
+ SparkChatRequest sparkChatRequest = buildSparkChatRequest(chatBotReqDto, botConfig, messages);
+ sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, chatReqRecords, false, false);
} else {
ModelConfigResult modelConfig = getModelConfiguration(botConfig.modelId, sseEmitter);
List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, modelConfig.maxInputTokens(), chatReqRecords.getId());
@@ -172,25 +154,11 @@ public class BotChatServiceImpl implements BotChatService {
chatBotReqDto.setEdit(true);
int maxInputTokens = this.maxInputTokens;
if (botConfig.modelId == null) {
- if (botConfig.model.equals(DefaultBotModelEnum.X1.getDomain()) || botConfig.model.equals(DefaultBotModelEnum.SPARK_4_0.getDomain())) {
- List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
- SparkChatRequest sparkChatRequest = buildSparkChatRequest(chatBotReqDto, botConfig, messages);
- sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, chatReqRecords, true, false);
- } else {
- maxInputTokens = 32000;
- List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
- LLMInfoVo llmInfoVo = new LLMInfoVo();
- llmInfoVo.setUrl(deepseekUrl);
- llmInfoVo.setApiKey(deepseekApiKey);
- llmInfoVo.setDomain(botConfig.model);
- JSONObject jsonObject = buildPromptChatRequest(llmInfoVo, messages);
- promptChatService.chatStream(jsonObject, sseEmitter, sseId, chatReqRecords, true, false);
- }
+ List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, maxInputTokens, chatReqRecords.getId());
+ SparkChatRequest sparkChatRequest = buildSparkChatRequest(chatBotReqDto, botConfig, messages);
+ sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, chatReqRecords, true, false);
} else {
ModelConfigResult modelConfig = getModelConfiguration(botConfig.modelId, sseEmitter);
- if (modelConfig == null) {
- return;
- }
List messages = buildMessageList(chatBotReqDto, botConfig.supportContext, botConfig.supportDocument, botConfig.prompt, modelConfig.maxInputTokens(), chatReqRecords.getId());
JSONObject jsonObject = buildPromptChatRequest(modelConfig.llmInfoVo, messages);
promptChatService.chatStream(jsonObject, sseEmitter, sseId, chatReqRecords, false, false);
@@ -222,25 +190,14 @@ public class BotChatServiceImpl implements BotChatService {
List messageList;
if (modelId == null) {
- if (model.equals(DefaultBotModelEnum.X1.getDomain()) || model.equals(DefaultBotModelEnum.SPARK_4_0.getDomain())) {
- messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
- SparkChatRequest sparkChatRequest = new SparkChatRequest();
- sparkChatRequest.setModel(model);
- sparkChatRequest.setMessages(messageList);
- sparkChatRequest.setChatId(null);
- sparkChatRequest.setUserId(uid);
- sparkChatRequest.setEnableWebSearch(enableWebSearch(openedTool));
- sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, null, false, true);
- } else {
- maxInputTokens = 32000;
- messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
- LLMInfoVo llmInfoVo = new LLMInfoVo();
- llmInfoVo.setUrl(deepseekUrl);
- llmInfoVo.setApiKey(deepseekApiKey);
- llmInfoVo.setDomain(model);
- JSONObject jsonObject = buildPromptChatRequest(llmInfoVo, messageList);
- promptChatService.chatStream(jsonObject, sseEmitter, sseId, null, false, true);
- }
+ messageList = buildDebugMessageList(text, prompt, messages, maxInputTokens, maasDatasetList);
+ SparkChatRequest sparkChatRequest = new SparkChatRequest();
+ sparkChatRequest.setModel(model);
+ sparkChatRequest.setMessages(messageList);
+ sparkChatRequest.setChatId(null);
+ sparkChatRequest.setUserId(uid);
+ sparkChatRequest.setEnableWebSearch(enableWebSearch(openedTool));
+ sparkChatService.chatStream(sparkChatRequest, sseEmitter, sseId, null, false, true);
} else {
ModelConfigResult modelConfig = getModelConfiguration(modelId, sseEmitter);
messageList = buildDebugMessageList(text, prompt, messages, modelConfig.maxInputTokens(), maasDatasetList);
diff --git a/console/backend/hub/src/main/resources/application.yml b/console/backend/hub/src/main/resources/application.yml
index 2a9fe83f..8563f5d4 100644
--- a/console/backend/hub/src/main/resources/application.yml
+++ b/console/backend/hub/src/main/resources/application.yml
@@ -86,11 +86,6 @@ spark:
image-apiKey: ${SPARK_IMAGE_API_KEY:xxx}
image-apiSecret: ${SPARK_IMAGE_API_SECRET:xxx}
-# DeepSeek configuration example
-deepseek:
- url: ${DEEPSEEK_URL:https://api.deepseek.com}
- api-key: ${DEEPSEEK_API_KEY:your-deepseek-api-key} # Write timeout
-
# Workflow configuration
workflow:
chatUrl: ${WORKFLOW_CHAT_URL:http://}
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index cb0c4bae..9944c610 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -575,8 +575,6 @@ services:
BOT_API_MAAS_BASE_URL: "${BOT_API_MAAS_BASE_URL:-}"
TENANT_CREATE_APP: "${TENANT_CREATE_APP:-}"
TENANT_GET_APP_DETAIL: "${TENANT_GET_APP_DETAIL:-}"
- DEEPSEEK_URL: "${DEEPSEEK_URL:-https://api.deepseek.com/chat/completions}"
- DEEPSEEK_API_KEY: "${DEEPSEEK_API_KEY:-}"
expose:
- "8080"
--
Gitee
From 1cf0f9cf57e15561368a573aabb3ad548e039ee4 Mon Sep 17 00:00:00 2001
From: clliu19
Date: Wed, 15 Oct 2025 16:20:14 +0800
Subject: [PATCH 42/90] !101 fix: make check fixed and rap issue fixed * Merge
branch 'feature/workflow-tool' into bd_build_lcl * fix: make check fixed and
rap issue fixed * fix: make check fixed and rap issue fixed * feat: rpa
version check latest * Merge branch 'bd_build' into bd_build_lcl * Merge
branch 'feature/workflow-tool' into bd_build_lcl * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * feat: sql fixed * Merge branch 'feature/workflow-tool' into
bd_build_lcl * merge code form bd-buid
---
.../astron/console/commons/util/MaasUtil.java | 25 +-
.../src/main/resources/messages_en.properties | 2 +-
.../workflow/WorkflowBotController.java | 4 +-
.../workflow/impl/BotChainServiceImpl.java | 2 +-
.../common/constant/WorkflowConst.java | 1 +
.../KnowledgeV2ServiceCallHandler.java | 12 +-
.../service/repo/KnowledgeService.java | 407 +++++++++---------
.../service/workflow/WorkflowService.java | 277 +++++++++++-
.../toolkit/tool/spark/SparkApiTool.java | 2 -
9 files changed, 488 insertions(+), 244 deletions(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
index e6129387..e01d797b 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
@@ -168,7 +168,7 @@ public class MaasUtil {
}
public JSONObject synchronizeWorkFlow(UserLangChainInfo userLangChainInfo, BotCreateForm botCreateForm,
- HttpServletRequest request, Long spaceId) {
+ HttpServletRequest request, Long spaceId) {
AdvancedConfig advancedConfig = new AdvancedConfig(botCreateForm.getPrologue(), botCreateForm.getInputExample(), botCreateForm.getAppBackground());
JSONObject param = new JSONObject();
param.put("avatarIcon", botCreateForm.getAvatar());
@@ -362,7 +362,7 @@ public class MaasUtil {
* Create API (without version)
*
* @param flowId Workflow ID
- * @param appid Application ID
+ * @param appid Application ID
* @return JSONObject response result
*/
public JSONObject createApi(String flowId, String appid) {
@@ -376,10 +376,10 @@ public class MaasUtil {
/**
* Create API (with version) - data parameter is not sent to workflow/v1/publish
*
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param flowId Workflow ID
+ * @param appid Application ID
* @param version Version number
- * @param data Version data (not used in publish request)
+ * @param data Version data (not used in publish request)
* @return JSONObject response result
*/
public JSONObject createApi(String flowId, String appid, String version, JSONObject data) {
@@ -390,14 +390,15 @@ public class MaasUtil {
/**
* Internal generic method for creating API
*
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param flowId Workflow ID
+ * @param appid Application ID
* @param version Version number (can be null)
* @return JSONObject response result
*/
private JSONObject createApiInternal(String flowId, String appid, String version, JSONObject data) {
log.info("----- Publishing maas workflow flowId: {}", flowId);
- MaasApi maasApi = new MaasApi(flowId, appid, version, data);
+ // Create MaasApi without data parameter for publish request
+ MaasApi maasApi = new MaasApi(flowId, appid, version);
// Execute publish request
String publishResponse = executeRequest(publishApi, maasApi);
@@ -413,7 +414,7 @@ public class MaasUtil {
/**
* Execute HTTP POST request and return response string
*
- * @param url Request URL
+ * @param url Request URL
* @param bodyData Request body data object
* @return String representation of response content
*/
@@ -447,9 +448,9 @@ public class MaasUtil {
* Validate whether the response is successful
*
* @param responseStr Response content string representation
- * @param action Description of current operation being performed (e.g., "publish", "bind")
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param action Description of current operation being performed (e.g., "publish", "bind")
+ * @param flowId Workflow ID
+ * @param appid Application ID
*/
private void validateResponse(String responseStr, String action, String flowId, String appid) {
log.info("----- {} maas api response: {}", action, responseStr);
diff --git a/console/backend/commons/src/main/resources/messages_en.properties b/console/backend/commons/src/main/resources/messages_en.properties
index 0e18aa7d..c710b78b 100644
--- a/console/backend/commons/src/main/resources/messages_en.properties
+++ b/console/backend/commons/src/main/resources/messages_en.properties
@@ -375,4 +375,4 @@ Please answer the question accurately based on the original text above and your
When answering user questions, please answer in the language the user asked\n\
If the above content cannot answer the user information, combine your knowledge to answer the user's question\n\
Answer the user's questions concisely and professionally, and do not add fabricated content to the answer.
-loose.suffix.prompt=\nMy next input is: {{}}
+loose.suffix.prompt=\nMy next input is: {{}}
\ No newline at end of file
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
index ee63f258..c0112c24 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
@@ -65,7 +65,7 @@ public class WorkflowBotController {
@PostMapping("/createFromTemplate")
@Transactional(rollbackFor = Exception.class)
public ApiResult createFromTemplate(HttpServletRequest request,
- @RequestBody MaasDuplicate maasDuplicate) {
+ @RequestBody MaasDuplicate maasDuplicate) {
String uid = RequestContextUtil.getUID();
return ApiResult.success(botMaasService.createFromTemplate(uid, maasDuplicate, request));
}
@@ -73,7 +73,7 @@ public class WorkflowBotController {
@PostMapping("/templateList")
@Operation(summary = "work flow template", description = "Get workflow templates")
public ApiResult> templateList(HttpServletRequest request,
- @RequestBody WorkflowTemplateQueryDto queryDto) {
+ @RequestBody WorkflowTemplateQueryDto queryDto) {
return ApiResult.success(botMaasService.templateList(queryDto));
}
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
index 47363302..1646f729 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
@@ -127,7 +127,7 @@ public class BotChainServiceImpl implements BotChainService {
*
* @param original Original node ID string
* @return New node ID string, if the original string contains a colon, add a random UUID after the
- * colon, otherwise throw an exception
+ * colon, otherwise throw an exception
*/
public static String getNewNodeId(String original) {
int colonIndex = original.indexOf(':');
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/common/constant/WorkflowConst.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/common/constant/WorkflowConst.java
index fbe206c0..83388217 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/common/constant/WorkflowConst.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/common/constant/WorkflowConst.java
@@ -24,6 +24,7 @@ public class WorkflowConst {
public static final String AGENT = "agent";
public static final String FLOW_END = "flow_obj";
public static final String DATABASE = "database";
+ public static final String RPA = "rpa";
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
index f21eac6b..8859c0cf 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
@@ -49,14 +49,14 @@ public class KnowledgeV2ServiceCallHandler {
* @return KnowledgeResponse
*/
public KnowledgeResponse documentUpload(MultipartFile multipartFile,
- List lengthRange, List separator,
+ List lengthRange, List separator,
String ragType, Integer resourceType) {
String url = apiUrl.getKnowledgeUrl().concat("/v1/document/upload");
-
+
try {
- log.info("documentUpload fileName: {}, fileSize: {} bytes",
- multipartFile.getOriginalFilename(), multipartFile.getSize());
-
+ log.info("documentUpload fileName: {}, fileSize: {} bytes",
+ multipartFile.getOriginalFilename(), multipartFile.getSize());
+
Map params = new HashMap<>();
params.put("file", multipartFile);
if (lengthRange != null) {
@@ -69,7 +69,7 @@ public class KnowledgeV2ServiceCallHandler {
if (resourceType != null) {
params.put("resourceType", resourceType.toString());
}
-
+
log.info("documentUpload url = {}, ragType = {}, resourceType = {}", url, ragType, resourceType);
String post = OkHttpUtil.postMultipart(url, new HashMap<>(), null, params, null);
log.info("documentUpload response = {}", post);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
index b2ff4520..852006cf 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
@@ -410,7 +410,7 @@ public class KnowledgeService {
// try {
// session.startTransaction();
// knowledgeRepository.deleteById(id);
- if(mysqlKnowledge.getEnabled().equals(1)) {
+ if (mysqlKnowledge.getEnabled().equals(1)) {
JSONArray delKbList = new JSONArray();
delKbList.add(knowledge.getId());
this.deleteKnowledgeChunks(uuids.getFirst(), delKbList);
@@ -430,278 +430,261 @@ public class KnowledgeService {
/**
- * Asynchronously extract knowledge from document content
+ * Asynchronously extract knowledge from document content.
+ *
+ * Behavior keeps parity with original implementation:
+ *
+ * - CBG source ⇒ upload file stream from S3; others ⇒ split by URL.
+ * - On non-zero response code, extract inner message when code==11111 and mark task as
+ * failed.
+ * - On empty chunks, set user-friendly reason based on contentType (image vs non-image).
+ * - Persist preview chunks to DB, update charCount and lastUuid rules, then update statuses.
+ *
+ *
*
- * @param contentType the MIME type of the document content
- * @param url the URL of the document to be processed
- * @param sliceConfig configuration for document slicing/chunking
- * @param fileInfoV2 file information object containing metadata
- * @param extractKnowledgeTask task object to track extraction progress
- * @throws BusinessException if document processing fails or file doesn't meet requirements
+ * @param contentType MIME type of the document
+ * @param url original file URL
+ * @param sliceConfig chunking configuration
+ * @param fileInfoV2 file info
+ * @param extractKnowledgeTask task status carrier
*/
@Async
- public void knowledgeExtractAsync(String contentType, String url, SliceConfig sliceConfig, FileInfoV2 fileInfoV2, ExtractKnowledgeTask extractKnowledgeTask) {
- // 1/2: Parse the user-provided text and perform chunking (completed in one interface)
- String source = fileInfoV2.getSource();
+ public void knowledgeExtractAsync(String contentType, String url,
+ SliceConfig sliceConfig,
+ FileInfoV2 fileInfoV2,
+ ExtractKnowledgeTask extractKnowledgeTask) {
+
+ final String source = fileInfoV2.getSource();
KnowledgeResponse response;
-
- // Compatibility for old and new knowledge bases, handled by new CBG knowledge base
+
+ // 1) Split: CBG=upload, others=URL split
if (ProjectContent.isCbgRagCompatible(source)) {
- // Use upload mode for CBG compatible sources
- try {
- // Get file from S3
- String s3Key = fileInfoV2.getAddress();
- InputStream fileStream = s3Util.getObject(s3Key);
- if (fileStream == null) {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get file from S3", false);
- return;
- }
-
- // Convert InputStream to MultipartFile
- byte[] fileBytes = inputStreamToByteArray(fileStream);
- MultipartFile multipartFile = new MockMultipartFile(
- "file",
- fileInfoV2.getName(),
- "application/octet-stream",
- fileBytes
- );
-
- try {
- fileStream.close();
- } catch (Exception e) {
- log.warn("Failed to close file stream", e);
- }
-
- List sliceConf = sliceConfig.getSeperator();
- List separator = (sliceConf != null && !sliceConf.isEmpty())
- ? Collections.singletonList(sliceConf.get(0))
- : Collections.singletonList("\n");
-
- Integer resourceType = ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType()) ? 1 : 0;
-
- response = knowledgeV2ServiceCallHandler.documentUpload(
- multipartFile,
- sliceConfig.getLengthRange(),
- separator,
- source,
- resourceType
- );
- } catch (Exception e) {
- log.error("Failed to upload file for chunking: {}", e.getMessage(), e);
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to upload file: " + e.getMessage(), false);
- return;
- }
+ response = doCbgUploadSplit(sliceConfig, fileInfoV2, extractKnowledgeTask);
+ if (response == null)
+ return; // already updated status on failure
} else {
- // Use original URL mode for other sources
- SplitRequest request = new SplitRequest();
- request.setFile(url.replaceAll("\\+", "%20"));
- request.setLengthRange(sliceConfig.getLengthRange());
- request.setCutOff(sliceConfig.getSeperator());
- if (ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType())) {
- request.setResourceType(1);
- }
- request.setRagType(source);
- response = knowledgeV2ServiceCallHandler.documentSplit(request);
+ response = doUrlSplit(url, sliceConfig, fileInfoV2);
}
-
+
+ // 2) Check response code & normalize message when needed
if (response.getCode() != 0) {
- String errMsg = response.getMessage();
+ String errMsg = normalizeErrMsgIfParenthesized(response.getCode(), response.getMessage());
log.error("Document chunking failed : {}", errMsg);
- // Temporary solution
- if (response.getCode() == 11111) {
- String regex = "[((](.*?)[))]";
- Pattern pattern = Pattern.compile(regex);
- Matcher matcher = pattern.matcher(errMsg);
- if (matcher.find()) {
- errMsg = matcher.group(1);
- }
- }
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document chunking failed, " + errMsg, false);
- return;
- // throw new CustomException("Document chunking failed : { " + response.getMessage() + " }");
- }
- List chunkInfos;
- try {
- chunkInfos = ((JSONArray) response.getData()).toJavaList(ChunkInfo.class);
- } catch (Exception e) {
- log.error("Failed to get document chunking result : {}", e.getMessage(), e);
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get document chunking result:" + e.getMessage(), false);
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask,
+ "Document chunking failed, " + errMsg, false);
return;
}
+ // 3) Parse data -> List
+ final List chunkInfos = parseChunkInfosOrFail(response, fileInfoV2, extractKnowledgeTask);
+ if (chunkInfos == null)
+ return; // status updated inside on failure
+
+ // 4) Empty result guard with image-specific hint
if (chunkInfos.isEmpty()) {
- if (contentType.equals(ProjectContent.JPEG_FILE_TYPE) || contentType.equals(ProjectContent.JPG_FILE_TYPE) || contentType.equals(ProjectContent.PNG_FILE_TYPE) || contentType.equals(ProjectContent.BMP_FILE_TYPE)) {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document cannot be chunked, please check if the image contains text", false);
- } else {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document cannot be chunked, please check if the file meets upload requirements", false);
- }
+ String reason = isImage(contentType)
+ ? "Document cannot be chunked, please check if the image contains text"
+ : "Document cannot be chunked, please check if the file meets upload requirements";
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, reason, false);
return;
}
- // 3. Store data in database
- this.storagePreviewKnowledge(fileInfoV2.getUuid(), fileInfoV2.getId(), chunkInfos);
+ // 5) Persist preview chunks
+ storagePreviewKnowledge(fileInfoV2.getUuid(), fileInfoV2.getId(), chunkInfos);
- int charCount = 0;
- for (ChunkInfo previewKnowledgeObject : chunkInfos) {
- String knowledgeStr = previewKnowledgeObject.getContent();
- if (!StringUtils.isEmpty(knowledgeStr)) {
- charCount += knowledgeStr.length();
+ // 6) Update char count
+ int charCount = countTotalChars(chunkInfos);
+ if (charCount > 0) {
+ fileInfoV2.setCharCount((long) charCount);
+ }
+
+ // 7) Update lastUuid rule (CBG uses chunk's docId)
+ updateLastUuidBySource(fileInfoV2, chunkInfos);
+
+ // 8) Final success status
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, null, true);
+ }
+
+ /** CBG: upload S3 stream and call documentUpload; on failure update status & return null. */
+ private KnowledgeResponse doCbgUploadSplit(SliceConfig sliceConfig,
+ FileInfoV2 fileInfoV2,
+ ExtractKnowledgeTask extractKnowledgeTask) {
+ try (InputStream fileStream = s3Util.getObject(fileInfoV2.getAddress())) {
+ if (fileStream == null) {
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get file from S3", false);
+ return null;
}
+ byte[] fileBytes = inputStreamToByteArray(fileStream);
+ MultipartFile multipartFile = new MockMultipartFile(
+ "file", fileInfoV2.getName(), "application/octet-stream", fileBytes);
+
+ List sliceConf = sliceConfig.getSeperator();
+ List separator = (sliceConf != null && !sliceConf.isEmpty())
+ ? Collections.singletonList(sliceConf.get(0))
+ : Collections.singletonList("\n");
+
+ Integer resourceType = ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType()) ? 1 : 0;
+
+ return knowledgeV2ServiceCallHandler.documentUpload(
+ multipartFile, sliceConfig.getLengthRange(), separator,
+ fileInfoV2.getSource(), resourceType);
+
+ } catch (Exception e) {
+ log.error("Failed to upload file for chunking: {}", e.getMessage(), e);
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask,
+ "Failed to upload file: " + e.getMessage(), false);
+ return null;
}
+ }
- if (charCount > 0) {
- fileInfoV2.setCharCount((long) charCount);
+ /** Non-CBG: split by URL. */
+ private KnowledgeResponse doUrlSplit(String url, SliceConfig sliceConfig, FileInfoV2 fileInfoV2) {
+ SplitRequest request = new SplitRequest();
+ request.setFile(url.replaceAll("\\+", "%20"));
+ request.setLengthRange(sliceConfig.getLengthRange());
+ request.setCutOff(sliceConfig.getSeperator());
+ if (ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType())) {
+ request.setResourceType(1);
+ }
+ request.setRagType(fileInfoV2.getSource());
+ return knowledgeV2ServiceCallHandler.documentSplit(request);
+ }
+
+ /** When code==11111 extract inner parentheses text, keep original message otherwise. */
+ private String normalizeErrMsgIfParenthesized(int code, String message) {
+ if (code != 11111 || message == null)
+ return message;
+ try {
+ Matcher m = Pattern.compile("[((](.*?)[))]").matcher(message);
+ return m.find() ? m.group(1) : message;
+ } catch (Exception ignore) {
+ return message;
+ }
+ }
+
+ /** Parse chunk list with failure reporting. Returns null on failure (already updated status). */
+ private List parseChunkInfosOrFail(KnowledgeResponse response,
+ FileInfoV2 fileInfoV2,
+ ExtractKnowledgeTask task) {
+ try {
+ return ((JSONArray) response.getData()).toJavaList(ChunkInfo.class);
+ } catch (Exception e) {
+ log.error("Failed to get document chunking result : {}", e.getMessage(), e);
+ updateTaskAndFileStatus(fileInfoV2, task,
+ "Failed to get document chunking result:" + e.getMessage(), false);
+ return null;
+ }
+ }
+
+ /** Image file types used by original logic. */
+ private boolean isImage(String contentType) {
+ return ProjectContent.JPEG_FILE_TYPE.equals(contentType)
+ || ProjectContent.JPG_FILE_TYPE.equals(contentType)
+ || ProjectContent.PNG_FILE_TYPE.equals(contentType)
+ || ProjectContent.BMP_FILE_TYPE.equals(contentType);
+ }
+
+ /** Sum of text length in all chunks; null-safe as in original semantics. */
+ private int countTotalChars(List chunkInfos) {
+ int total = 0;
+ for (ChunkInfo c : chunkInfos) {
+ String s = c.getContent();
+ if (!StringUtils.isEmpty(s)) {
+ total += s.length();
+ }
}
+ return total;
+ }
- // CBG needs to use chunk's fileId as fileId
+ /** Preserve original lastUuid rules: CBG uses chunk's docId; others keep file uuid. */
+ private void updateLastUuidBySource(FileInfoV2 fileInfoV2, List chunkInfos) {
if (ProjectContent.isCbgRagCompatible(fileInfoV2.getSource())) {
if (fileInfoV2.getLastUuid() == null) {
- fileInfoV2.setUuid(chunkInfos.get(0).getDocId());
+ fileInfoV2.setUuid(chunkInfos.get(0).getDocId()); // keep parity with original first-set logic
}
fileInfoV2.setLastUuid(chunkInfos.get(0).getDocId());
} else {
fileInfoV2.setLastUuid(fileInfoV2.getUuid());
}
-
-
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, null, true);
}
/**
- * Asynchronously extract and embed knowledge from document content
+ * Asynchronously extract and embed knowledge from document content.
+ *
+ * Behavior parity with the original:
+ *
+ *
+ * - CBG source ⇒ upload file stream from S3; others ⇒ split by URL.
+ * - On non-zero response code, when code==11111 extract the inner message.
+ * - On empty chunks, set image/non-image specific friendly reason.
+ * - Persist preview chunks, update charCount & lastUuid, then update statuses.
+ * - On success, call {@code saveTaskAndUpdateFileStatus} and {@code embeddingFile}.
+ *
*
- * @param contentType the MIME type of the document content
+ * @param contentType MIME type of the document content
* @param url the URL of the document to be processed
* @param sliceConfig configuration for document slicing/chunking
* @param fileInfoV2 file information object containing metadata
* @param extractKnowledgeTask task object to track extraction progress
* @param fileInfoV2Service service for file information operations
- * @throws BusinessException if document processing, embedding, or file operations fail
*/
@Async
- public void knowledgeEmbeddingExtractAsync(String contentType, String url, SliceConfig sliceConfig, FileInfoV2 fileInfoV2,
- ExtractKnowledgeTask extractKnowledgeTask, FileInfoV2Service fileInfoV2Service) {
- // 1/2: Parse the user-provided text and perform chunking (completed in one interface)
- String source = fileInfoV2.getSource();
+ public void knowledgeEmbeddingExtractAsync(String contentType, String url,
+ SliceConfig sliceConfig,
+ FileInfoV2 fileInfoV2,
+ ExtractKnowledgeTask extractKnowledgeTask,
+ FileInfoV2Service fileInfoV2Service) {
+ final String source = fileInfoV2.getSource();
KnowledgeResponse response;
-
- // Compatibility for old and new knowledge bases, handled by new CBG knowledge base
+
+ // 1) Split: CBG=upload, others=URL split
if (ProjectContent.isCbgRagCompatible(source)) {
- // Use upload mode for CBG compatible sources
- try {
- // Get file from S3
- String s3Key = fileInfoV2.getAddress();
- InputStream fileStream = s3Util.getObject(s3Key);
- if (fileStream == null) {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get file from S3", false);
- return;
- }
-
- // Convert InputStream to MultipartFile
- byte[] fileBytes = inputStreamToByteArray(fileStream);
- MultipartFile multipartFile = new MockMultipartFile(
- "file",
- fileInfoV2.getName(),
- "application/octet-stream",
- fileBytes
- );
-
- try {
- fileStream.close();
- } catch (Exception e) {
- log.warn("Failed to close file stream", e);
- }
-
- List sliceConf = sliceConfig.getSeperator();
- List separator = (sliceConf != null && !sliceConf.isEmpty())
- ? Collections.singletonList(sliceConf.get(0))
- : Collections.singletonList("\n");
-
- Integer resourceType = ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType()) ? 1 : 0;
-
- response = knowledgeV2ServiceCallHandler.documentUpload(
- multipartFile,
- sliceConfig.getLengthRange(),
- separator,
- source,
- resourceType
- );
- } catch (Exception e) {
- log.error("Failed to upload file for chunking: {}", e.getMessage(), e);
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to upload file: " + e.getMessage(), false);
- return;
+ response = doCbgUploadSplit(sliceConfig, fileInfoV2, extractKnowledgeTask);
+ if (response == null) {
+ return; // already updated status on failure
}
} else {
- // Use original URL mode for other sources
- SplitRequest request = new SplitRequest();
- request.setFile(url.replaceAll("\\+", "%20"));
- request.setLengthRange(sliceConfig.getLengthRange());
- request.setCutOff(sliceConfig.getSeperator());
- if (ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType())) {
- request.setResourceType(1);
- }
- request.setRagType(source);
- response = knowledgeV2ServiceCallHandler.documentSplit(request);
+ response = doUrlSplit(url, sliceConfig, fileInfoV2);
}
-
+
+ // 2) Check response code & normalize message for code==11111
if (response.getCode() != 0) {
- String errMsg = response.getMessage();
+ String errMsg = normalizeErrMsgIfParenthesized(response.getCode(), response.getMessage());
log.error("Document chunking failed : {}", errMsg);
- // Temporary solution
- if (response.getCode() == 11111) {
- String regex = "[((](.*?)[))]";
- Pattern pattern = Pattern.compile(regex);
- Matcher matcher = pattern.matcher(errMsg);
- if (matcher.find()) {
- errMsg = matcher.group(1);
- }
- }
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document chunking failed, " + errMsg, false);
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask,
+ "Document chunking failed, " + errMsg, false);
return;
- // throw new CustomException("Document chunking failed : { " + response.getMessage() + " }");
}
- List chunkInfos;
- try {
- chunkInfos = ((JSONArray) response.getData()).toJavaList(ChunkInfo.class);
- } catch (Exception e) {
- log.error("Failed to get document chunking result : {}", e.getMessage(), e);
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get document chunking result:" + e.getMessage(), false);
- return;
+
+ // 3) Parse data -> List
+ final List chunkInfos = parseChunkInfosOrFail(response, fileInfoV2, extractKnowledgeTask);
+ if (chunkInfos == null) {
+ return; // status updated inside on failure
}
+ // 4) Empty result guard with image-specific hint
if (chunkInfos.isEmpty()) {
- if (contentType.equals(ProjectContent.JPEG_FILE_TYPE) || contentType.equals(ProjectContent.JPG_FILE_TYPE) || contentType.equals(ProjectContent.PNG_FILE_TYPE) || contentType.equals(ProjectContent.BMP_FILE_TYPE)) {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document cannot be chunked, please check if the image contains text", false);
- } else {
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Document cannot be chunked, please check if the file meets upload requirements", false);
- }
+ String reason = isImage(contentType)
+ ? "Document cannot be chunked, please check if the image contains text"
+ : "Document cannot be chunked, please check if the file meets upload requirements";
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, reason, false);
return;
}
- // 3. Store data in database
- this.storagePreviewKnowledge(fileInfoV2.getUuid(), fileInfoV2.getId(), chunkInfos);
-
- int charCount = 0;
- for (ChunkInfo previewKnowledgeObject : chunkInfos) {
- String knowledgeStr = previewKnowledgeObject.getContent();
- if (!StringUtils.isEmpty(knowledgeStr)) {
- charCount += knowledgeStr.length();
- }
- }
+ // 5) Persist preview chunks
+ storagePreviewKnowledge(fileInfoV2.getUuid(), fileInfoV2.getId(), chunkInfos);
+ // 6) Update char count
+ int charCount = countTotalChars(chunkInfos);
if (charCount > 0) {
fileInfoV2.setCharCount((long) charCount);
}
- // CBG needs to use chunk's fileId as fileId
- // CBG needs to use chunk's fileId as fileId
- if (ProjectContent.isCbgRagCompatible(fileInfoV2.getSource())) {
- fileInfoV2.setLastUuid(chunkInfos.get(0).getDocId());
- } else {
- fileInfoV2.setLastUuid(fileInfoV2.getUuid());
- }
+ // 7) Update lastUuid rule (CBG uses chunk's docId)
+ updateLastUuidBySource(fileInfoV2, chunkInfos);
- this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, null, true);
+ // 8) Final success status + embedding-trigger
+ updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, null, true);
fileInfoV2Service.saveTaskAndUpdateFileStatus(fileInfoV2.getId());
fileInfoV2Service.embeddingFile(fileInfoV2.getId(), fileInfoV2.getSpaceId());
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
index 2d6f00f6..20022425 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
@@ -65,14 +65,12 @@ import com.iflytek.astron.console.toolkit.entity.table.relation.FlowDbRel;
import com.iflytek.astron.console.toolkit.entity.table.relation.FlowRepoRel;
import com.iflytek.astron.console.toolkit.entity.table.relation.FlowToolRel;
import com.iflytek.astron.console.toolkit.entity.table.repo.FileInfoV2;
-import com.iflytek.astron.console.toolkit.entity.table.tool.ToolBox;
-import com.iflytek.astron.console.toolkit.entity.table.tool.ToolBoxOperateHistory;
+import com.iflytek.astron.console.toolkit.entity.table.tool.*;
import com.iflytek.astron.console.toolkit.entity.table.workflow.*;
import com.iflytek.astron.console.toolkit.entity.tool.McpServerTool;
import com.iflytek.astron.console.toolkit.entity.vo.*;
import com.iflytek.astron.console.toolkit.entity.vo.eval.EvalSetVerDataVo;
-import com.iflytek.astron.console.toolkit.handler.McpServerHandler;
-import com.iflytek.astron.console.toolkit.handler.UserInfoManagerHandler;
+import com.iflytek.astron.console.toolkit.handler.*;
import com.iflytek.astron.console.toolkit.mapper.ConfigInfoMapper;
import com.iflytek.astron.console.toolkit.mapper.database.DbTableMapper;
import com.iflytek.astron.console.toolkit.mapper.eval.EvalSetMapper;
@@ -82,8 +80,7 @@ import com.iflytek.astron.console.toolkit.mapper.relation.FlowDbRelMapper;
import com.iflytek.astron.console.toolkit.mapper.relation.FlowRepoRelMapper;
import com.iflytek.astron.console.toolkit.mapper.relation.FlowToolRelMapper;
import com.iflytek.astron.console.toolkit.mapper.repo.FileInfoV2Mapper;
-import com.iflytek.astron.console.toolkit.mapper.tool.ToolBoxMapper;
-import com.iflytek.astron.console.toolkit.mapper.tool.ToolBoxOperateHistoryMapper;
+import com.iflytek.astron.console.toolkit.mapper.tool.*;
import com.iflytek.astron.console.toolkit.mapper.trace.ChatInfoMapper;
import com.iflytek.astron.console.toolkit.mapper.trace.NodeInfoMapper;
import com.iflytek.astron.console.toolkit.mapper.workflow.*;
@@ -91,6 +88,7 @@ import com.iflytek.astron.console.toolkit.service.extra.AppService;
import com.iflytek.astron.console.toolkit.service.extra.CoreSystemService;
import com.iflytek.astron.console.toolkit.service.extra.OpenPlatformService;
import com.iflytek.astron.console.toolkit.service.model.ModelService;
+import com.iflytek.astron.console.toolkit.service.tool.RpaAssistantService;
import com.iflytek.astron.console.toolkit.sse.WorkflowSseEventSourceListener;
import com.iflytek.astron.console.toolkit.tool.DataPermissionCheckTool;
import com.iflytek.astron.console.toolkit.tool.JsonConverter;
@@ -169,6 +167,12 @@ public class WorkflowService extends ServiceImpl {
private static final String JSON_KEY_BOT_ID = "botId";
private static final String PUBLISH_SUCCESS = "成功";
private static final int DEFAULT_ORDER = 0;
+ private static final String NP_PROJECT_ID = "projectId";
+ private static final String NP_ASSISTANT_ID = "assistantId";
+ private static final String NP_VERSION = "version";
+ private static final String FIELD_IS_LATEST = "isLatest";
+ private static final String FIELD_LATEST_VER = "latestVersion";
+ private static final String FIELD_CURR_VER = "currentVersion";
@Value("${spring.profiles.active}")
String env;
@@ -262,6 +266,12 @@ public class WorkflowService extends ServiceImpl {
private CommonConfig commonConfig;
@Autowired
private UserInfoDataService userInfoDataService;
+ @Autowired
+ private RpaAssistantService rpaAssistantService;
+ @Autowired
+ private RpaUserAssistantFieldMapper rpaUserAssistantFieldMapper;
+ @Autowired
+ private RpaHandler rpaHandler;
/**
* Query workflow list with pagination (in-memory pagination, can be replaced with database
@@ -477,7 +487,7 @@ public class WorkflowService extends ServiceImpl {
// Tool/node version tips
workflow.setData(buildFlowToolLastVersion(workflow.getData()));
workflow.setData(buildFlowLastVersion(workflow.getData()));
-
+ workflow.setData(buildFlowRpaLastVersion(workflow.getData()));
WorkflowVo vo = new WorkflowVo();
org.springframework.beans.BeanUtils.copyProperties(workflow, vo);
vo.setAddress(s3Util.getS3Prefix());
@@ -515,6 +525,257 @@ public class WorkflowService extends ServiceImpl {
return vo;
}
+ /**
+ * 判断rpa是否是最新的版本
+ *
+ * @param data
+ * @return
+ */
+
+ /**
+ * Holder for temporary data when scanning RPA nodes in a workflow. Aggregates current versions in
+ * the flow, involved RPA nodes, and assistant IDs.
+ */
+ private static final class RpaScan {
+ /** Mapping of projectId -> current version found in the workflow protocol. */
+ final Map flowProjectVersionMap = new HashMap<>();
+ /** All RPA nodes in the workflow protocol. */
+ final List rpaNodes = new ArrayList<>();
+ /** Assistant IDs collected from RPA nodes (used to look up API keys). */
+ final Set assistantIds = new HashSet<>();
+
+ /**
+ * Whether the scan result is insufficient to proceed (no nodes or missing key fields).
+ *
+ * @return true if any of the required collections is empty; false otherwise
+ */
+ boolean isEmpty() {
+ return flowProjectVersionMap.isEmpty() || assistantIds.isEmpty() || rpaNodes.isEmpty();
+ }
+ }
+
+ /**
+ * Determine whether each RPA node is on the latest version and backfill flags into node params.
+ *
+ * Behavior:
+ *
+ *
+ * - On parse failure, no RPA nodes, or insufficient info, return the original JSON.
+ * - On remote/query failures, mark nodes conservatively as {@code isLatest=false} but do not
+ * abort.
+ * - Write back {@code isLatest}, {@code latestVersion}, and {@code currentVersion} for RPA
+ * nodes.
+ *
+ *
+ * @param data workflow data JSON string (BizWorkflowData)
+ * @return original JSON if no effective change, otherwise the updated JSON string
+ */
+ private String buildFlowRpaLastVersion(String data) {
+ if (StringUtils.isBlank(data)) {
+ return data;
+ }
+ final BizWorkflowData biz = parseWorkflowDataSafe(data);
+ if (biz == null || CollUtil.isEmpty(biz.getNodes())) {
+ return data;
+ }
+
+ // (1) Scan RPA nodes and collect projectId/version/assistantId
+ final RpaScan scan = scanRpaNodes(biz.getNodes());
+ if (scan.isEmpty()) {
+ return data;
+ }
+
+ // (2) Fetch API keys by assistantId (deduplicated)
+ final Set apiKeys = fetchApiKeysByAssistants(scan.assistantIds);
+ if (apiKeys.isEmpty()) {
+ // No keys available: conservatively mark as not latest and write back
+ markWithoutOnlineData(scan.rpaNodes);
+ return JSONObject.toJSONString(biz);
+ }
+
+ // (3) Fetch online "latest version" map (if multiple keys return versions, use the maximum)
+ final Map latestMap = fetchLatestVersionMap(apiKeys);
+
+ // (4) Mark nodes and serialize only when changes are made
+ final boolean changed = markNodesWithLatest(biz, scan.rpaNodes, latestMap);
+ return changed ? JSONObject.toJSONString(biz) : data;
+ }
+
+ /**
+ * Parse workflow JSON (BizWorkflowData) safely.
+ *
+ * @param json workflow data JSON string
+ * @return parsed BizWorkflowData instance, or null if parsing fails
+ */
+ private @Nullable BizWorkflowData parseWorkflowDataSafe(String json) {
+ try {
+ return JSON.parseObject(json, BizWorkflowData.class);
+ } catch (Exception ex) {
+ log.warn("buildFlowRpaLastVersion: failed to parse workflow data", ex);
+ return null;
+ }
+ }
+
+ /**
+ * Scan workflow nodes to extract RPA nodes and collect key fields.
+ *
+ * @param nodes workflow node list
+ * @return RpaScan aggregated result including RPA nodes, project/version map, and assistant IDs
+ */
+ private RpaScan scanRpaNodes(List nodes) {
+ final RpaScan scan = new RpaScan();
+ for (BizWorkflowNode n : nodes) {
+ if (n == null || n.getId() == null)
+ continue;
+ if (!n.getId().startsWith(WorkflowConst.NodeType.RPA))
+ continue;
+
+ scan.rpaNodes.add(n);
+ final JSONObject np = Optional.ofNullable(n.getData()).map(BizNodeData::getNodeParam).orElse(null);
+ if (np == null)
+ continue;
+
+ final String projectId = np.getString(NP_PROJECT_ID);
+ final Integer version = np.getInteger(NP_VERSION);
+ final Long assistantId = np.getLong(NP_ASSISTANT_ID);
+
+ if (StringUtils.isNotBlank(projectId) && version != null) {
+ // If the same projectId appears multiple times in the flow, keep the maximum version as the current
+ // baseline.
+ scan.flowProjectVersionMap.merge(projectId, version, Math::max);
+ }
+ if (assistantId != null) {
+ scan.assistantIds.add(assistantId);
+ }
+ }
+ return scan;
+ }
+
+ /**
+ * Fetch API keys for the given assistant IDs.
+ *
+ * On any exception, returns an empty set and logs a warning.
+ *
+ *
+ * @param assistantIds assistant IDs collected from RPA nodes
+ * @return deduplicated API key set; never null
+ */
+ private Set fetchApiKeysByAssistants(Set assistantIds) {
+ try {
+ List fields = rpaUserAssistantFieldMapper.selectList(
+ new LambdaQueryWrapper()
+ .select(RpaUserAssistantField::getFieldValue)
+ .in(RpaUserAssistantField::getAssistantId, assistantIds));
+ if (CollectionUtil.isEmpty(fields))
+ return Collections.emptySet();
+ return fields.stream()
+ .map(RpaUserAssistantField::getFieldValue)
+ .filter(StringUtils::isNotBlank)
+ .collect(Collectors.toSet());
+ } catch (Exception ex) {
+ log.warn("buildFlowRpaLastVersion: failed to query assistant fields, assistantIds={}", assistantIds, ex);
+ return Collections.emptySet();
+ }
+ }
+
+ /**
+ * Build an online latest-version map for RPA projects: projectId -> latestVersion. If multiple
+ * API keys return different versions for the same project, the maximum is taken.
+ *
+ * Note: If the remote endpoint is paginated, extend this method to iterate pages.
+ *
+ *
+ * @param apiKeys API keys used to query the remote RPA list
+ * @return mapping from projectId to its latest version; never null
+ */
+ private Map fetchLatestVersionMap(Set apiKeys) {
+ final Map latest = new HashMap<>();
+ for (String key : apiKeys) {
+ try {
+ // If the remote API is paginated, extend here with a loop to fetch all pages.
+ JSONObject rpaList = rpaHandler.getRpaList(1, 999, key);
+ if (rpaList == null)
+ continue;
+ JSONArray records = rpaList.getJSONArray("records");
+ if (records == null || records.isEmpty())
+ continue;
+
+ for (int i = 0; i < records.size(); i++) {
+ JSONObject rec = records.getJSONObject(i);
+ if (rec == null)
+ continue;
+ String projectId = rec.getString("project_id");
+ Integer version = rec.getInteger("version");
+ if (StringUtils.isBlank(projectId) || version == null)
+ continue;
+ latest.merge(projectId, version, Math::max);
+ }
+ } catch (Exception ex) {
+ log.warn("buildFlowRpaLastVersion: failed to fetch RPA list for one apiKey", ex);
+ }
+ }
+ return latest;
+ }
+
+ /**
+ * When no online data is available, conservatively mark nodes as not latest and backfill
+ * {@code currentVersion} for display convenience.
+ *
+ * @param rpaNodes RPA nodes to mark
+ */
+ private void markWithoutOnlineData(List rpaNodes) {
+ for (BizWorkflowNode n : rpaNodes) {
+ final JSONObject np = Optional.ofNullable(n.getData()).map(BizNodeData::getNodeParam).orElse(null);
+ if (np == null)
+ continue;
+ final Integer curr = np.getInteger(NP_VERSION);
+ if (curr != null)
+ np.put(FIELD_CURR_VER, curr);
+ if (n.getData() != null)
+ n.getData().setIsLatest(false); // Conservative: unknown treated as not latest
+ }
+ }
+
+ /**
+ * Mark each RPA node with latest flags and optional latest/current version values.
+ *
+ * @param biz whole BizWorkflowData (used to decide whether serialization is needed)
+ * @param rpaNodes RPA nodes to annotate
+ * @param latestMap mapping of projectId to its latest online version
+ * @return true if any node state changed; false otherwise
+ */
+ private boolean markNodesWithLatest(BizWorkflowData biz,
+ List rpaNodes,
+ Map latestMap) {
+ boolean changed = false;
+ for (BizWorkflowNode n : rpaNodes) {
+ final BizNodeData d = n.getData();
+ final JSONObject np = Optional.ofNullable(d).map(BizNodeData::getNodeParam).orElse(null);
+ if (np == null)
+ continue;
+
+ final String projectId = np.getString(NP_PROJECT_ID);
+ final Integer currVer = np.getInteger(NP_VERSION);
+ if (currVer != null)
+ np.put(FIELD_CURR_VER, currVer);
+
+ boolean isLatest = false;
+ Integer latestVer = latestMap.get(projectId);
+ if (latestVer != null) {
+ np.put(FIELD_LATEST_VER, latestVer);
+ isLatest = Objects.equals(currVer, latestVer);
+ } else {
+ // No online version found: conservative false
+ isLatest = false;
+ }
+ if (d != null && !Objects.equals(d.getIsLatest(), isLatest)) {
+ d.setIsLatest(isLatest);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
/**
* Mark tool nodes based on current tool information in data, whether it's the "latest version", and
* backfill tool name when not latest.
@@ -1991,7 +2252,7 @@ public class WorkflowService extends ServiceImpl {
return;
}
JSONArray mcpServerIds = plugin.getJSONArray("mcpServerIds");
- if(mcpServerIds != null || !mcpServerIds.isEmpty()){
+ if (mcpServerIds != null || !mcpServerIds.isEmpty()) {
JSONArray mcpServerUrls = plugin.getJSONArray("mcpServerUrls");
for (Object mcpServerId : mcpServerIds) {
String server = (String) mcpServerId;
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
index 1de38576..be64f5bf 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
@@ -4,7 +4,6 @@ import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.exception.BusinessException;
-import com.iflytek.astron.console.toolkit.config.properties.CommonConfig;
import com.iflytek.astron.console.toolkit.entity.spark.SparkApiProtocol;
import com.iflytek.astron.console.toolkit.entity.spark.Text;
import com.iflytek.astron.console.toolkit.entity.spark.chat.ChatResponse;
@@ -16,7 +15,6 @@ import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
--
Gitee
From 40fd11756f4c3d6e55981ff11eb9ef5cb7a45613 Mon Sep 17 00:00:00 2001
From: yun-zhi-ztl <15071461069@163.com>
Date: Wed, 15 Oct 2025 16:37:14 +0800
Subject: [PATCH 43/90] feat: fmt code
---
.../com/iflytek/astron/console/commons/util/MaasUtil.java | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
index e6129387..9b469267 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
@@ -623,11 +623,7 @@ public class MaasUtil {
*/
public static boolean isFileArray(JSONObject param) {
try {
- if ("array-string".equalsIgnoreCase(param.getJSONObject("schema").getString("type"))) {
- return true;
- } else {
- return false;
- }
+ return "array-string".equalsIgnoreCase(param.getJSONObject("schema").getString("type"));
} catch (Exception e) {
log.error("Exception determining if parameter is array type: {}", e.getMessage());
return false;
--
Gitee
From 7ffb261abb7fd564192a841fed51f7899a15be60 Mon Sep 17 00:00:00 2001
From: mingsuiyongheng <1653497380@qq.com>
Date: Wed, 15 Oct 2025 16:42:44 +0800
Subject: [PATCH 44/90] refactor: Handle null modelId explicitly when updating
bot data
---
.../service/bot/impl/ChatBotDataServiceImpl.java | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/ChatBotDataServiceImpl.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/ChatBotDataServiceImpl.java
index b8e443a6..a50ae2f2 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/ChatBotDataServiceImpl.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/service/bot/impl/ChatBotDataServiceImpl.java
@@ -165,6 +165,16 @@ public class ChatBotDataServiceImpl implements ChatBotDataService {
@Override
public ChatBotBase updateBot(ChatBotBase chatBotBase) {
+ // If modelId is null, need to explicitly set it to null in database
+ if (chatBotBase.getModelId() == null) {
+ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();
+ updateWrapper.eq(ChatBotBase::getId, chatBotBase.getId())
+ .set(ChatBotBase::getModelId, null);
+
+ // Update other fields using updateById (it will skip null fields by default)
+ chatBotBaseMapper.update(null, updateWrapper);
+ }
+ // Then update other non-null fields
chatBotBaseMapper.updateById(chatBotBase);
return chatBotBase;
}
--
Gitee
From 138849618f2d1ab287db170ccebcc1527db8bc7a Mon Sep 17 00:00:00 2001
From: mingsuiyongheng <1653497380@qq.com>
Date: Wed, 15 Oct 2025 16:48:25 +0800
Subject: [PATCH 45/90] refactor: fmt java
---
.../console/hub/service/chat/impl/BotChatServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
index 557d8455..977c78e3 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
@@ -286,7 +286,7 @@ public class BotChatServiceImpl implements BotChatService {
* @param botId Bot ID
* @return Returns BotConfiguration object
*/
- private BotConfiguration getBotConfiguration(Integer botId) throws BusinessException{
+ private BotConfiguration getBotConfiguration(Integer botId) throws BusinessException {
ChatBotMarket chatBotMarket = chatBotDataService.findMarketBotByBotId(botId);
if (chatBotMarket != null && ShelfStatusEnum.isOnShelf(chatBotMarket.getBotStatus())) {
--
Gitee
From eca29ad44d0b1e2ff13a336a457110173fc7697d Mon Sep 17 00:00:00 2001
From: clliu19
Date: Wed, 15 Oct 2025 17:39:30 +0800
Subject: [PATCH 46/90] !105 fix: depedency fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * fix: depedency fixed * Merge branch 'feature/workflow-tool'
into bd_build_lcl * fix: make check fixed and rap issue fixed * fix: make
check fixed and rap issue fixed * feat: rpa version check latest * Merge
branch 'bd_build' into bd_build_lcl * Merge branch 'feature/workflow-tool'
into bd_build_lcl * Merge branch 'feature/workflow-tool' into bd_build_lcl *
Merge branch 'bd_build' into bd_build_lcl * feat: sql fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * merge code form bd-buid
---
.../console/toolkit/service/workflow/WorkflowService.java | 3 ---
1 file changed, 3 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
index 20022425..2f6662c3 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
@@ -88,7 +88,6 @@ import com.iflytek.astron.console.toolkit.service.extra.AppService;
import com.iflytek.astron.console.toolkit.service.extra.CoreSystemService;
import com.iflytek.astron.console.toolkit.service.extra.OpenPlatformService;
import com.iflytek.astron.console.toolkit.service.model.ModelService;
-import com.iflytek.astron.console.toolkit.service.tool.RpaAssistantService;
import com.iflytek.astron.console.toolkit.sse.WorkflowSseEventSourceListener;
import com.iflytek.astron.console.toolkit.tool.DataPermissionCheckTool;
import com.iflytek.astron.console.toolkit.tool.JsonConverter;
@@ -267,8 +266,6 @@ public class WorkflowService extends ServiceImpl {
@Autowired
private UserInfoDataService userInfoDataService;
@Autowired
- private RpaAssistantService rpaAssistantService;
- @Autowired
private RpaUserAssistantFieldMapper rpaUserAssistantFieldMapper;
@Autowired
private RpaHandler rpaHandler;
--
Gitee
From dc285ed4393be2c969cdcbc9fd6b1e680521d77c Mon Sep 17 00:00:00 2001
From: mingsuiyongheng <1653497380@qq.com>
Date: Wed, 15 Oct 2025 19:34:21 +0800
Subject: [PATCH 47/90] feat: Add the code field to StopStreamResponse and set
its default value
---
.../astron/console/hub/dto/chat/StopStreamResponse.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/dto/chat/StopStreamResponse.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/dto/chat/StopStreamResponse.java
index 2c38ef9c..b1a3c60e 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/dto/chat/StopStreamResponse.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/dto/chat/StopStreamResponse.java
@@ -18,6 +18,8 @@ import java.time.LocalDateTime;
@Schema(description = "Stop SSE stream response")
public class StopStreamResponse {
+ private Integer code;
+
@Schema(description = "Whether the operation was successful", example = "true")
private Boolean success;
@@ -38,6 +40,7 @@ public class StopStreamResponse {
*/
public static StopStreamResponse success(String streamId) {
return StopStreamResponse.builder()
+ .code(0)
.success(true)
.message("Stream stopped")
.streamId(streamId)
--
Gitee
From c6845b0fea72c55aeeaab71260e12642727b608f Mon Sep 17 00:00:00 2001
From: clliu19
Date: Thu, 16 Oct 2025 09:58:37 +0800
Subject: [PATCH 48/90] !107 fix: rpa version check * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * fix: rpa version check fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * fix: depedency fixed * Merge branch 'feature/workflow-tool'
into bd_build_lcl * fix: make check fixed and rap issue fixed * fix: make
check fixed and rap issue fixed * feat: rpa version check latest * Merge
branch 'bd_build' into bd_build_lcl * Merge branch 'feature/workflow-tool'
into bd_build_lcl * Merge branch 'feature/workflow-tool' into bd_build_lcl *
Merge branch 'bd_build' into bd_build_lcl * feat: sql fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * merge code form bd-buid
---
.../console/toolkit/service/workflow/WorkflowService.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
index 2f6662c3..bca20228 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
@@ -690,7 +690,7 @@ public class WorkflowService extends ServiceImpl {
for (String key : apiKeys) {
try {
// If the remote API is paginated, extend here with a loop to fetch all pages.
- JSONObject rpaList = rpaHandler.getRpaList(1, 999, key);
+ JSONObject rpaList = rpaHandler.getRpaList(1, 99, key);
if (rpaList == null)
continue;
JSONArray records = rpaList.getJSONArray("records");
@@ -763,7 +763,7 @@ public class WorkflowService extends ServiceImpl {
isLatest = Objects.equals(currVer, latestVer);
} else {
// No online version found: conservative false
- isLatest = false;
+ isLatest = true;
}
if (d != null && !Objects.equals(d.getIsLatest(), isLatest)) {
d.setIsLatest(isLatest);
--
Gitee
From 0894c4f32aca46cc5e1fbeecccc82523907142ec Mon Sep 17 00:00:00 2001
From: clliu19
Date: Thu, 16 Oct 2025 10:08:37 +0800
Subject: [PATCH 49/90] !109 fix: model name check in space fix * Merge branch
'bd_build' into bd_build_lcl * Merge branch 'feature/workflow-tool' into
bd_build_lcl * fix: duplicat midel check fix * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * fix: rpa version check fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * Merge branch 'bd_build' into
bd_build_lcl * fix: depedency fixed * Merge branch 'feature/workflow-tool'
into bd_build_lcl * fix: make check fixed and rap issue fixed * fix: make
check fixed and rap issue fixed * feat: rpa version check latest * Merge
branch 'bd_build' into bd_build_lcl * Merge branch 'feature/workflow-tool'
into bd_build_lcl * Merge branch 'feature/workflow-tool' into bd_build_lcl *
Merge branch 'bd_build' into bd_build_lcl * feat: sql fixed * Merge branch
'feature/workflow-tool' into bd_build_lcl * merge code form bd-buid
---
.../toolkit/service/model/ModelService.java | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
index 14f138a7..0aa722e2 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
@@ -295,12 +295,15 @@ public class ModelService extends ServiceImpl {
Model model;
if (isNew) {
// Duplicate name validation
- Model exist =
- this.getOne(
- new LambdaQueryWrapper()
- .eq(Model::getUid, request.getUid())
- .eq(Model::getName, request.getModelName())
- .eq(Model::getIsDeleted, 0));
+ LambdaQueryWrapper lqw = new LambdaQueryWrapper()
+ .eq(Model::getName, request.getModelName())
+ .eq(Model::getIsDeleted, 0);
+ if(spaceId != null){
+ lqw.eq(Model::getSpaceId, spaceId);
+ }else{
+ lqw.eq(Model::getUid, request.getUid()).isNull(Model::getSpaceId);
+ }
+ Model exist = this.getOne(lqw);
if (exist != null) {
throw new BusinessException(ResponseEnum.MODEL_NAME_EXISTED);
}
--
Gitee
From 5d724c602677c26a85fa6966ec5c29e8ec11794d Mon Sep 17 00:00:00 2001
From: cherry
Date: Thu, 16 Oct 2025 10:55:03 +0800
Subject: [PATCH 50/90] fix(hub): fix error
---
.../console/hub/service/workflow/impl/BotMaasServiceImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotMaasServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotMaasServiceImpl.java
index fca3a6cb..8fce93b8 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotMaasServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotMaasServiceImpl.java
@@ -74,7 +74,7 @@ public class BotMaasServiceImpl implements BotMaasService {
}
Integer botId = botInfoDto.getBotId();
botService.addMaasInfo(uid, res, botId, spaceId);
- botInfoDto.setFlowId((Long) res.getByPath("data.id"));
+ botInfoDto.setFlowId(res.getJSONObject("data").getLong("id"));
return botInfoDto;
}
--
Gitee
From 59d42aee6dc61b270799da3c892e639e68094a42 Mon Sep 17 00:00:00 2001
From: zyzy0116 <727517347@qq.com>
Date: Thu, 16 Oct 2025 18:07:18 +0800
Subject: [PATCH 51/90] feat: api workflow-io-info-list
---
.../astron/console/commons/util/MaasUtil.java | 22 +--
.../workflow/WorkflowBotController.java | 4 +-
.../hub/entity/BotConversationStats.java | 3 +-
.../service/chat/impl/BotChatServiceImpl.java | 2 +-
.../workflow/impl/BotChainServiceImpl.java | 2 +-
.../hub/service/OpenApiServiceTest.java | 33 ++++
.../controller/open/OpenApiController.java | 66 +++++++
.../entity/dto/external/AppInfoResponse.java | 24 +++
.../dto/openapi/WorkflowIoTransRequest.java | 20 ++
.../entity/vo/openapi/WorkflowIoTransVo.java | 28 +++
.../KnowledgeV2ServiceCallHandler.java | 12 +-
.../service/external/ExternalApiService.java | 77 ++++++++
.../service/openapi/OpenApiService.java | 20 ++
.../openapi/impl/OpenApiServiceImpl.java | 178 ++++++++++++++++++
.../service/repo/KnowledgeService.java | 82 ++++----
.../service/workflow/WorkflowService.java | 4 +-
.../toolkit/tool/spark/SparkApiTool.java | 2 -
.../main/resources/application-toolkit.yml | 1 +
18 files changed, 510 insertions(+), 70 deletions(-)
create mode 100644 console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/external/AppInfoResponse.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/openapi/WorkflowIoTransRequest.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/openapi/WorkflowIoTransVo.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/OpenApiService.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/impl/OpenApiServiceImpl.java
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
index e6129387..48f263cd 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/MaasUtil.java
@@ -168,7 +168,7 @@ public class MaasUtil {
}
public JSONObject synchronizeWorkFlow(UserLangChainInfo userLangChainInfo, BotCreateForm botCreateForm,
- HttpServletRequest request, Long spaceId) {
+ HttpServletRequest request, Long spaceId) {
AdvancedConfig advancedConfig = new AdvancedConfig(botCreateForm.getPrologue(), botCreateForm.getInputExample(), botCreateForm.getAppBackground());
JSONObject param = new JSONObject();
param.put("avatarIcon", botCreateForm.getAvatar());
@@ -362,7 +362,7 @@ public class MaasUtil {
* Create API (without version)
*
* @param flowId Workflow ID
- * @param appid Application ID
+ * @param appid Application ID
* @return JSONObject response result
*/
public JSONObject createApi(String flowId, String appid) {
@@ -376,10 +376,10 @@ public class MaasUtil {
/**
* Create API (with version) - data parameter is not sent to workflow/v1/publish
*
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param flowId Workflow ID
+ * @param appid Application ID
* @param version Version number
- * @param data Version data (not used in publish request)
+ * @param data Version data (not used in publish request)
* @return JSONObject response result
*/
public JSONObject createApi(String flowId, String appid, String version, JSONObject data) {
@@ -390,8 +390,8 @@ public class MaasUtil {
/**
* Internal generic method for creating API
*
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param flowId Workflow ID
+ * @param appid Application ID
* @param version Version number (can be null)
* @return JSONObject response result
*/
@@ -413,7 +413,7 @@ public class MaasUtil {
/**
* Execute HTTP POST request and return response string
*
- * @param url Request URL
+ * @param url Request URL
* @param bodyData Request body data object
* @return String representation of response content
*/
@@ -447,9 +447,9 @@ public class MaasUtil {
* Validate whether the response is successful
*
* @param responseStr Response content string representation
- * @param action Description of current operation being performed (e.g., "publish", "bind")
- * @param flowId Workflow ID
- * @param appid Application ID
+ * @param action Description of current operation being performed (e.g., "publish", "bind")
+ * @param flowId Workflow ID
+ * @param appid Application ID
*/
private void validateResponse(String responseStr, String action, String flowId, String appid) {
log.info("----- {} maas api response: {}", action, responseStr);
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
index ee63f258..c0112c24 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/controller/workflow/WorkflowBotController.java
@@ -65,7 +65,7 @@ public class WorkflowBotController {
@PostMapping("/createFromTemplate")
@Transactional(rollbackFor = Exception.class)
public ApiResult createFromTemplate(HttpServletRequest request,
- @RequestBody MaasDuplicate maasDuplicate) {
+ @RequestBody MaasDuplicate maasDuplicate) {
String uid = RequestContextUtil.getUID();
return ApiResult.success(botMaasService.createFromTemplate(uid, maasDuplicate, request));
}
@@ -73,7 +73,7 @@ public class WorkflowBotController {
@PostMapping("/templateList")
@Operation(summary = "work flow template", description = "Get workflow templates")
public ApiResult> templateList(HttpServletRequest request,
- @RequestBody WorkflowTemplateQueryDto queryDto) {
+ @RequestBody WorkflowTemplateQueryDto queryDto) {
return ApiResult.success(botMaasService.templateList(queryDto));
}
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/entity/BotConversationStats.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/entity/BotConversationStats.java
index 2bc43e7f..a5aeeea4 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/entity/BotConversationStats.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/entity/BotConversationStats.java
@@ -9,8 +9,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
/**
- * Bot Conversation Statistics Entity
- * Corresponds to bot_conversation_stats table
+ * Bot Conversation Statistics Entity Corresponds to bot_conversation_stats table
*
* @author Omuigix
*/
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
index 557d8455..977c78e3 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/impl/BotChatServiceImpl.java
@@ -286,7 +286,7 @@ public class BotChatServiceImpl implements BotChatService {
* @param botId Bot ID
* @return Returns BotConfiguration object
*/
- private BotConfiguration getBotConfiguration(Integer botId) throws BusinessException{
+ private BotConfiguration getBotConfiguration(Integer botId) throws BusinessException {
ChatBotMarket chatBotMarket = chatBotDataService.findMarketBotByBotId(botId);
if (chatBotMarket != null && ShelfStatusEnum.isOnShelf(chatBotMarket.getBotStatus())) {
diff --git a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
index 47363302..1646f729 100644
--- a/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
+++ b/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/workflow/impl/BotChainServiceImpl.java
@@ -127,7 +127,7 @@ public class BotChainServiceImpl implements BotChainService {
*
* @param original Original node ID string
* @return New node ID string, if the original string contains a colon, add a random UUID after the
- * colon, otherwise throw an exception
+ * colon, otherwise throw an exception
*/
public static String getNewNodeId(String original) {
int colonIndex = original.indexOf(':');
diff --git a/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
new file mode 100644
index 00000000..337968cf
--- /dev/null
+++ b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
@@ -0,0 +1,33 @@
+package com.iflytek.astron.console.hub.service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.iflytek.astron.console.commons.response.ApiResult;
+import com.iflytek.astron.console.toolkit.controller.open.OpenApiController;
+import com.iflytek.astron.console.toolkit.service.openapi.OpenApiService;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.util.List;
+
+/**
+ * @Description:
+ */
+@Slf4j
+@SpringBootTest
+public class OpenApiServiceTest {
+ @Autowired
+ private OpenApiService openApiService;
+ @Autowired
+ private OpenApiController openApiController;
+
+ @Test
+ void getOpenApi() {
+ ApiResult> result = openApiController.getWorkflowIoTransformations("Bearer 17e9c079c3a70c42e45526c2b9ba176f:ZGM3ZWFlZmI4YWU0Y2NlOGViMGY0ZDJi");
+ List workflowIoTransformations = result.data();
+ for (JSONObject workflowIoTransformation : workflowIoTransformations) {
+ log.info("workflowIoTransformation:{}", workflowIoTransformation.toString());
+ }
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
new file mode 100644
index 00000000..71600a2a
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
@@ -0,0 +1,66 @@
+package com.iflytek.astron.console.toolkit.controller.open;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.iflytek.astron.console.commons.constant.ResponseEnum;
+import com.iflytek.astron.console.commons.response.ApiResult;
+import com.iflytek.astron.console.toolkit.common.anno.ResponseResultBody;
+import com.iflytek.astron.console.toolkit.entity.dto.openapi.WorkflowIoTransRequest;
+import com.iflytek.astron.console.toolkit.service.openapi.OpenApiService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * Open API Controller for external service integration
+ */
+@RestController
+@RequestMapping("/open-api")
+@Slf4j
+@ResponseResultBody
+@Tag(name = "Open API interface")
+public class OpenApiController {
+
+ @Autowired
+ private OpenApiService openApiService;
+
+ private static final String AUTHORIZATION_PREFIX = "Bearer ";
+
+ /**
+ * Get workflow IO transformation data by API key
+ *
+ * @param authorization Authorization header in format "Bearer apiKey:apiSecret"
+ * @return List of IO transformation data
+ */
+ @GetMapping("/workflow-io-transformations")
+ @Operation(summary = "Get workflow IO transformations",
+ description = "Retrieve workflow IO transformation data using API key authentication")
+ public ApiResult> getWorkflowIoTransformations(
+ @RequestHeader("authorization") String authorization) {
+
+ // Parse authorization header
+ if (!StringUtils.hasText(authorization) || !authorization.startsWith("Bearer ")) {
+ return ApiResult.error(ResponseEnum.UNAUTHORIZED);
+ }
+
+ String credentials = authorization.substring(AUTHORIZATION_PREFIX.length());
+ String[] parts = credentials.split(":");
+ if (parts.length != 2) {
+ return ApiResult.error(ResponseEnum.UNAUTHORIZED);
+ }
+
+ // Build request DTO
+ WorkflowIoTransRequest request = new WorkflowIoTransRequest();
+ request.setApiKey(parts[0]);
+ request.setApiSecret(parts[1]);
+
+ // Call service layer
+ List result = openApiService.getWorkflowIoTransformations(request);
+
+ return ApiResult.success(result);
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/external/AppInfoResponse.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/external/AppInfoResponse.java
new file mode 100644
index 00000000..3d1f19dc
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/external/AppInfoResponse.java
@@ -0,0 +1,24 @@
+package com.iflytek.astron.console.toolkit.entity.dto.external;
+
+import lombok.Data;
+
+/**
+ * Response DTO for third-party API app info query
+ */
+@Data
+public class AppInfoResponse {
+
+ private String sid;
+ private Integer code;
+ private String message;
+ private AppInfoData data;
+
+ @Data
+ public static class AppInfoData {
+ private String appid;
+ private String name;
+ private String source;
+ private String desc;
+ private String createTime;
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/openapi/WorkflowIoTransRequest.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/openapi/WorkflowIoTransRequest.java
new file mode 100644
index 00000000..dc7c4115
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/openapi/WorkflowIoTransRequest.java
@@ -0,0 +1,20 @@
+package com.iflytek.astron.console.toolkit.entity.dto.openapi;
+
+import lombok.Data;
+
+/**
+ * Request DTO for workflow IO transformation query
+ */
+@Data
+public class WorkflowIoTransRequest {
+
+ /**
+ * API Key extracted from authorization header
+ */
+ private String apiKey;
+
+ /**
+ * API Secret extracted from authorization header
+ */
+ private String apiSecret;
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/openapi/WorkflowIoTransVo.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/openapi/WorkflowIoTransVo.java
new file mode 100644
index 00000000..d98f8199
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/openapi/WorkflowIoTransVo.java
@@ -0,0 +1,28 @@
+package com.iflytek.astron.console.toolkit.entity.vo.openapi;
+
+import com.alibaba.fastjson2.JSONObject;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * Response VO for workflow IO transformation query
+ */
+@Data
+public class WorkflowIoTransVo {
+
+ /**
+ * List of workflow IO transformation data
+ */
+ private List transformations;
+
+ /**
+ * Total count of transformations found
+ */
+ private Integer count;
+
+ /**
+ * Application ID that was queried
+ */
+ private String appId;
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
index f21eac6b..8859c0cf 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/KnowledgeV2ServiceCallHandler.java
@@ -49,14 +49,14 @@ public class KnowledgeV2ServiceCallHandler {
* @return KnowledgeResponse
*/
public KnowledgeResponse documentUpload(MultipartFile multipartFile,
- List lengthRange, List separator,
+ List lengthRange, List separator,
String ragType, Integer resourceType) {
String url = apiUrl.getKnowledgeUrl().concat("/v1/document/upload");
-
+
try {
- log.info("documentUpload fileName: {}, fileSize: {} bytes",
- multipartFile.getOriginalFilename(), multipartFile.getSize());
-
+ log.info("documentUpload fileName: {}, fileSize: {} bytes",
+ multipartFile.getOriginalFilename(), multipartFile.getSize());
+
Map params = new HashMap<>();
params.put("file", multipartFile);
if (lengthRange != null) {
@@ -69,7 +69,7 @@ public class KnowledgeV2ServiceCallHandler {
if (resourceType != null) {
params.put("resourceType", resourceType.toString());
}
-
+
log.info("documentUpload url = {}, ragType = {}, resourceType = {}", url, ragType, resourceType);
String post = OkHttpUtil.postMultipart(url, new HashMap<>(), null, params, null);
log.info("documentUpload response = {}", post);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
new file mode 100644
index 00000000..dfdb404f
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
@@ -0,0 +1,77 @@
+package com.iflytek.astron.console.toolkit.service.external;
+
+import com.alibaba.fastjson2.JSON;
+import com.iflytek.astron.console.toolkit.entity.dto.external.AppInfoResponse;
+import com.iflytek.astron.console.toolkit.util.OkHttpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * Service for calling external third-party APIs
+ */
+@Service
+@Slf4j
+public class ExternalApiService {
+ @Value("${api.url.appIdQryUrl}")
+ private String appIdQryUrl;
+
+
+ /**
+ * Query app info by API key
+ *
+ * @param apiKey API key
+ * @return AppInfoResponse
+ */
+ public AppInfoResponse getAppInfoByApiKey(String apiKey) {
+ String url = appIdQryUrl + "/v2/app/key/api_key/" + apiKey;
+ log.debug("Calling external API: {}", url);
+
+ try {
+ String response = OkHttpUtil.get(url);
+ log.debug("External API response: {}", response);
+
+ // Check if response is valid JSON format
+ if (response == null || response.trim().isEmpty()) {
+ log.error("Empty response from external API for apiKey: {}", apiKey);
+ return createMockResponse(apiKey);
+ }
+
+ // Check for common error responses
+ if (response.contains("404") || response.contains("not found") ||
+ response.contains("error") || !response.trim().startsWith("{")) {
+ log.warn("External API not available (response: {}), using mock data for apiKey: {}",
+ response.trim(), apiKey);
+ return createMockResponse(apiKey);
+ }
+
+ return JSON.parseObject(response, AppInfoResponse.class);
+ } catch (Exception e) {
+ log.warn("Failed to query external API ({}), using mock data for apiKey: {}",
+ e.getMessage(), apiKey);
+ return createMockResponse(apiKey);
+ }
+ }
+
+ /**
+ * Create mock response when external API is not available
+ * TODO: Remove this when external API is fixed
+ */
+ private AppInfoResponse createMockResponse(String apiKey) {
+ AppInfoResponse response = new AppInfoResponse();
+ response.setCode(0);
+ response.setMessage("Success (Mock Data)");
+
+ AppInfoResponse.AppInfoData data = new AppInfoResponse.AppInfoData();
+ // Use a deterministic appId based on apiKey for consistency
+ data.setAppid("mock-app-" + apiKey.substring(0, Math.min(8, apiKey.length())));
+ data.setName("Mock Application");
+ data.setSource("mock");
+ data.setDesc("Mock application for testing");
+
+ response.setData(data);
+
+ log.info("Generated mock response for apiKey: {}, appId: {}", apiKey, data.getAppid());
+ return response;
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/OpenApiService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/OpenApiService.java
new file mode 100644
index 00000000..fec577f7
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/OpenApiService.java
@@ -0,0 +1,20 @@
+package com.iflytek.astron.console.toolkit.service.openapi;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.iflytek.astron.console.toolkit.entity.dto.openapi.WorkflowIoTransRequest;
+
+import java.util.List;
+
+/**
+ * Open API Service Interface
+ */
+public interface OpenApiService {
+
+ /**
+ * Get workflow IO transformations by API key
+ *
+ * @param request Request containing API key and secret
+ * @return Workflow IO transformation data
+ */
+ List getWorkflowIoTransformations(WorkflowIoTransRequest request);
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/impl/OpenApiServiceImpl.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/impl/OpenApiServiceImpl.java
new file mode 100644
index 00000000..9ff821df
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/openapi/impl/OpenApiServiceImpl.java
@@ -0,0 +1,178 @@
+package com.iflytek.astron.console.toolkit.service.openapi.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.iflytek.astron.console.commons.constant.ResponseEnum;
+import com.iflytek.astron.console.commons.dto.bot.ChatBotApi;
+import com.iflytek.astron.console.commons.entity.workflow.Workflow;
+import com.iflytek.astron.console.commons.exception.BusinessException;
+import com.iflytek.astron.console.commons.mapper.bot.ChatBotApiMapper;
+import com.iflytek.astron.console.toolkit.entity.biz.workflow.BizWorkflowData;
+import com.iflytek.astron.console.toolkit.entity.dto.external.AppInfoResponse;
+import com.iflytek.astron.console.toolkit.entity.dto.openapi.WorkflowIoTransRequest;
+import com.iflytek.astron.console.toolkit.service.external.ExternalApiService;
+import com.iflytek.astron.console.toolkit.service.openapi.OpenApiService;
+import com.iflytek.astron.console.toolkit.service.workflow.WorkflowService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Open API Service Implementation
+ */
+@Service
+@Slf4j
+public class OpenApiServiceImpl implements OpenApiService {
+
+ @Autowired
+ private ExternalApiService externalApiService;
+
+ @Autowired
+ private WorkflowService workflowService;
+
+ @Autowired
+ private ChatBotApiMapper chatBotApiMapper;
+
+ @Override
+ public List getWorkflowIoTransformations(WorkflowIoTransRequest request) {
+ try {
+ String appId = getAppIdByApiKey(request.getApiKey());
+
+ if (!StringUtils.hasText(appId)) {
+ log.error("appId is empty, apiKey:{}", request.getApiKey());
+ throw new BusinessException(ResponseEnum.UNAUTHORIZED);
+ }
+
+ // String appId = "663777f0";
+
+ List chatBotApiList = getChatBotApiByAppId(appId);
+
+ if (chatBotApiList.isEmpty()) {
+ log.info("No ChatBotApi records found for appId: {}", appId);
+ return null;
+ }
+
+ return processWorkflowTransformations(chatBotApiList);
+
+ } catch (BusinessException e) {
+ log.error("Business error in getWorkflowIoTransformations: {}", e.getMessage());
+ throw e;
+ } catch (Exception e) {
+ log.error("Unexpected error in getWorkflowIoTransformations", e);
+ throw new BusinessException(ResponseEnum.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ private List getChatBotApiByAppId(String appId) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(ChatBotApi::getAppId, appId);
+ return chatBotApiMapper.selectList(queryWrapper);
+ }
+
+ /**
+ * Get appId by calling external API with apiKey
+ */
+ private String getAppIdByApiKey(String apiKey) {
+ AppInfoResponse appInfoResponse = externalApiService.getAppInfoByApiKey(apiKey);
+ if (appInfoResponse.getCode() != 0 || appInfoResponse.getData() == null) {
+ log.error("Failed to get app info from external API: code={}, message={}",
+ appInfoResponse.getCode(), appInfoResponse.getMessage());
+ throw new BusinessException(ResponseEnum.DATA_NOT_FOUND);
+ }
+
+ String appId = appInfoResponse.getData().getAppid();
+ log.info("Successfully retrieved appId: {} for apiKey: {}", appId, apiKey);
+ return appId;
+ }
+
+ /**
+ * Process workflow transformations for all ChatBotApi records
+ */
+ private List processWorkflowTransformations(List chatBotApiList) {
+
+ List workflowIds = chatBotApiList.stream()
+ .map(ChatBotApi::getAssistantId)
+ .filter(StringUtils::hasText)
+ .toList();
+
+ if (workflowIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ List workflowList = getWorkflowsById(workflowIds);
+
+ return processWorkflowList(workflowList);
+ }
+
+ /**
+ *
+ */
+ private List getWorkflowsById(List workflowIds) {
+ LambdaQueryWrapper workflowQueryWrapper = new LambdaQueryWrapper<>();
+ workflowQueryWrapper.in(Workflow::getFlowId, workflowIds)
+ .eq(Workflow::getDeleted, false);
+
+ return workflowService.list(workflowQueryWrapper);
+ }
+
+ /**
+ * Process a list of workflows to extract IO transformations
+ */
+ private List processWorkflowList(List workflows) {
+ List results = new ArrayList<>();
+
+ for (Workflow workflow : workflows) {
+ JSONObject transformation = processSingleWorkflow(workflow);
+ if (transformation != null) {
+ results.add(transformation);
+ }
+ }
+
+ return results;
+ }
+
+ /**
+ * Process a single workflow to extract IO transformation
+ */
+ private JSONObject processSingleWorkflow(Workflow workflow) {
+ if (!StringUtils.hasText(workflow.getData())) {
+ return null;
+ }
+
+ try {
+ BizWorkflowData bizWorkflowData = JSON.parseObject(workflow.getData(), BizWorkflowData.class);
+ if (bizWorkflowData == null || bizWorkflowData.getNodes() == null) {
+ return null;
+ }
+
+ JSONObject ioTransformation = workflowService.getIoTrans(bizWorkflowData.getNodes());
+ if (ioTransformation != null) {
+ enrichTransformationWithMetadata(ioTransformation, workflow);
+ }
+
+ return ioTransformation;
+ } catch (Exception e) {
+ log.error("Error processing workflow data for workflow id: {}", workflow.getId(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Add workflow metadata to transformation object
+ */
+ private void enrichTransformationWithMetadata(JSONObject transformation, Workflow workflow) {
+ transformation.put("workflowId", workflow.getId());
+ transformation.put("workflowName", workflow.getName());
+ transformation.put("workDescription", workflow.getDescription());
+ transformation.put("uid", workflow.getUid());
+ transformation.put("spaceId", workflow.getSpaceId());
+ transformation.put("createTime", workflow.getCreateTime());
+ transformation.put("updateTime", workflow.getUpdateTime());
+ }
+
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
index b2ff4520..16cbaf77 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/KnowledgeService.java
@@ -410,7 +410,7 @@ public class KnowledgeService {
// try {
// session.startTransaction();
// knowledgeRepository.deleteById(id);
- if(mysqlKnowledge.getEnabled().equals(1)) {
+ if (mysqlKnowledge.getEnabled().equals(1)) {
JSONArray delKbList = new JSONArray();
delKbList.add(knowledge.getId());
this.deleteKnowledgeChunks(uuids.getFirst(), delKbList);
@@ -444,7 +444,7 @@ public class KnowledgeService {
// 1/2: Parse the user-provided text and perform chunking (completed in one interface)
String source = fileInfoV2.getSource();
KnowledgeResponse response;
-
+
// Compatibility for old and new knowledge bases, handled by new CBG knowledge base
if (ProjectContent.isCbgRagCompatible(source)) {
// Use upload mode for CBG compatible sources
@@ -456,36 +456,34 @@ public class KnowledgeService {
this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get file from S3", false);
return;
}
-
+
// Convert InputStream to MultipartFile
byte[] fileBytes = inputStreamToByteArray(fileStream);
MultipartFile multipartFile = new MockMultipartFile(
- "file",
- fileInfoV2.getName(),
- "application/octet-stream",
- fileBytes
- );
-
+ "file",
+ fileInfoV2.getName(),
+ "application/octet-stream",
+ fileBytes);
+
try {
fileStream.close();
} catch (Exception e) {
log.warn("Failed to close file stream", e);
}
-
+
List sliceConf = sliceConfig.getSeperator();
- List separator = (sliceConf != null && !sliceConf.isEmpty())
- ? Collections.singletonList(sliceConf.get(0))
- : Collections.singletonList("\n");
-
+ List separator = (sliceConf != null && !sliceConf.isEmpty())
+ ? Collections.singletonList(sliceConf.get(0))
+ : Collections.singletonList("\n");
+
Integer resourceType = ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType()) ? 1 : 0;
-
+
response = knowledgeV2ServiceCallHandler.documentUpload(
- multipartFile,
- sliceConfig.getLengthRange(),
- separator,
- source,
- resourceType
- );
+ multipartFile,
+ sliceConfig.getLengthRange(),
+ separator,
+ source,
+ resourceType);
} catch (Exception e) {
log.error("Failed to upload file for chunking: {}", e.getMessage(), e);
this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to upload file: " + e.getMessage(), false);
@@ -503,7 +501,7 @@ public class KnowledgeService {
request.setRagType(source);
response = knowledgeV2ServiceCallHandler.documentSplit(request);
}
-
+
if (response.getCode() != 0) {
String errMsg = response.getMessage();
log.error("Document chunking failed : {}", errMsg);
@@ -584,7 +582,7 @@ public class KnowledgeService {
// 1/2: Parse the user-provided text and perform chunking (completed in one interface)
String source = fileInfoV2.getSource();
KnowledgeResponse response;
-
+
// Compatibility for old and new knowledge bases, handled by new CBG knowledge base
if (ProjectContent.isCbgRagCompatible(source)) {
// Use upload mode for CBG compatible sources
@@ -596,36 +594,34 @@ public class KnowledgeService {
this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to get file from S3", false);
return;
}
-
+
// Convert InputStream to MultipartFile
byte[] fileBytes = inputStreamToByteArray(fileStream);
MultipartFile multipartFile = new MockMultipartFile(
- "file",
- fileInfoV2.getName(),
- "application/octet-stream",
- fileBytes
- );
-
+ "file",
+ fileInfoV2.getName(),
+ "application/octet-stream",
+ fileBytes);
+
try {
fileStream.close();
} catch (Exception e) {
log.warn("Failed to close file stream", e);
}
-
+
List sliceConf = sliceConfig.getSeperator();
- List separator = (sliceConf != null && !sliceConf.isEmpty())
- ? Collections.singletonList(sliceConf.get(0))
- : Collections.singletonList("\n");
-
+ List separator = (sliceConf != null && !sliceConf.isEmpty())
+ ? Collections.singletonList(sliceConf.get(0))
+ : Collections.singletonList("\n");
+
Integer resourceType = ProjectContent.HTML_FILE_TYPE.equals(fileInfoV2.getType()) ? 1 : 0;
-
+
response = knowledgeV2ServiceCallHandler.documentUpload(
- multipartFile,
- sliceConfig.getLengthRange(),
- separator,
- source,
- resourceType
- );
+ multipartFile,
+ sliceConfig.getLengthRange(),
+ separator,
+ source,
+ resourceType);
} catch (Exception e) {
log.error("Failed to upload file for chunking: {}", e.getMessage(), e);
this.updateTaskAndFileStatus(fileInfoV2, extractKnowledgeTask, "Failed to upload file: " + e.getMessage(), false);
@@ -643,7 +639,7 @@ public class KnowledgeService {
request.setRagType(source);
response = knowledgeV2ServiceCallHandler.documentSplit(request);
}
-
+
if (response.getCode() != 0) {
String errMsg = response.getMessage();
log.error("Document chunking failed : {}", errMsg);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
index 2d6f00f6..60f1c059 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/workflow/WorkflowService.java
@@ -1991,7 +1991,7 @@ public class WorkflowService extends ServiceImpl {
return;
}
JSONArray mcpServerIds = plugin.getJSONArray("mcpServerIds");
- if(mcpServerIds != null || !mcpServerIds.isEmpty()){
+ if (mcpServerIds != null || !mcpServerIds.isEmpty()) {
JSONArray mcpServerUrls = plugin.getJSONArray("mcpServerUrls");
for (Object mcpServerId : mcpServerIds) {
String server = (String) mcpServerId;
@@ -2284,7 +2284,7 @@ public class WorkflowService extends ServiceImpl {
return vo;
}
- private JSONObject getIoTrans(List nodes) {
+ public JSONObject getIoTrans(List nodes) {
if (nodes.isEmpty()) {
return null;
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
index 1de38576..be64f5bf 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/tool/spark/SparkApiTool.java
@@ -4,7 +4,6 @@ import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSON;
import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.exception.BusinessException;
-import com.iflytek.astron.console.toolkit.config.properties.CommonConfig;
import com.iflytek.astron.console.toolkit.entity.spark.SparkApiProtocol;
import com.iflytek.astron.console.toolkit.entity.spark.Text;
import com.iflytek.astron.console.toolkit.entity.spark.chat.ChatResponse;
@@ -16,7 +15,6 @@ import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;
import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
diff --git a/console/backend/toolkit/src/main/resources/application-toolkit.yml b/console/backend/toolkit/src/main/resources/application-toolkit.yml
index 61eba9f8..5e87c244 100644
--- a/console/backend/toolkit/src/main/resources/application-toolkit.yml
+++ b/console/backend/toolkit/src/main/resources/application-toolkit.yml
@@ -34,6 +34,7 @@ api:
deleteXinghuoDatasetFileUrl: http://127.0.0.1:8080/dataset/deleteXinghuoDatasetFile
deleteXinghuoDatasetUrl: http://127.0.0.1:8080/dataset/deleteXinghuoDataset
rpaUrl: https://newapi.iflyrpa.com
+ appIdQryUrl: http://10.1.87.65:5052
# Business-specific configuration
biz:
--
Gitee
From e838bacb18e1324e000cc6f38e469871e8aa7208 Mon Sep 17 00:00:00 2001
From: zyzy0116 <727517347@qq.com>
Date: Thu, 16 Oct 2025 19:01:48 +0800
Subject: [PATCH 52/90] refactor: change api name
---
.../astron/console/hub/service/OpenApiServiceTest.java | 2 +-
.../console/toolkit/controller/open/OpenApiController.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
index 337968cf..a042fd76 100644
--- a/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
+++ b/console/backend/hub/src/test/java/com/iflytek/astron/console/hub/service/OpenApiServiceTest.java
@@ -24,7 +24,7 @@ public class OpenApiServiceTest {
@Test
void getOpenApi() {
- ApiResult> result = openApiController.getWorkflowIoTransformations("Bearer 17e9c079c3a70c42e45526c2b9ba176f:ZGM3ZWFlZmI4YWU0Y2NlOGViMGY0ZDJi");
+ ApiResult> result = openApiController.getWorkflowIoInfoList("Bearer 17e9c079c3a70c42e45526c2b9ba176f:ZGM3ZWFlZmI4YWU0Y2NlOGViMGY0ZDJi");
List workflowIoTransformations = result.data();
for (JSONObject workflowIoTransformation : workflowIoTransformations) {
log.info("workflowIoTransformation:{}", workflowIoTransformation.toString());
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
index 71600a2a..7d54f8d3 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/open/OpenApiController.java
@@ -36,10 +36,10 @@ public class OpenApiController {
* @param authorization Authorization header in format "Bearer apiKey:apiSecret"
* @return List of IO transformation data
*/
- @GetMapping("/workflow-io-transformations")
+ @GetMapping("/workflow-io-info-list")
@Operation(summary = "Get workflow IO transformations",
description = "Retrieve workflow IO transformation data using API key authentication")
- public ApiResult> getWorkflowIoTransformations(
+ public ApiResult> getWorkflowIoInfoList(
@RequestHeader("authorization") String authorization) {
// Parse authorization header
--
Gitee
From 4e0964f2837fe85b94d08099f191326311c63edf Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Mon, 20 Oct 2025 14:03:04 +0800
Subject: [PATCH 53/90] feat: rpa debug api add
---
.../console/commons/util/S3ClientUtil.java | 26 +-
.../controller/tool/RpaController.java | 79 +++++-
.../entity/dto/rpa/ExecuteAsyncResponse.java | 26 ++
.../dto/rpa/ExecutionStatusResponse.java | 167 +++++++++++++
.../toolkit/entity/dto/rpa/StartReq.java | 16 ++
.../toolkit/entity/enumVo/DebugStatus.java | 23 ++
.../toolkit/entity/vo/rpa/DebugSession.java | 140 +++++++++++
.../console/toolkit/handler/RpaHandler.java | 64 +++++
.../service/external/ExternalApiService.java | 6 +-
.../toolkit/service/model/ModelService.java | 4 +-
.../toolkit/service/tool/RpaDebugService.java | 141 +++++++++++
.../toolkit/task/scheduler/PollScheduler.java | 41 +++
.../knowledge/FileControllerTest.java | 233 ++++++++----------
.../knowledge/KnowledgeControllerTest.java | 100 ++++----
.../knowledge/RepoControllerTest.java | 158 ++++++------
15 files changed, 950 insertions(+), 274 deletions(-)
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/enumVo/DebugStatus.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/rpa/DebugSession.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
create mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
index d7b411cb..40c96b01 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
@@ -74,11 +74,10 @@ public class S3ClientUtil {
if (enablePublicRead) {
String publicReadPolicy = buildPublicReadPolicy(defaultBucket);
minioClient.setBucketPolicy(
- SetBucketPolicyArgs.builder()
- .bucket(defaultBucket)
- .config(publicReadPolicy)
- .build()
- );
+ SetBucketPolicyArgs.builder()
+ .bucket(defaultBucket)
+ .config(publicReadPolicy)
+ .build());
log.info("Set public read policy for bucket: {}", defaultBucket);
}
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
@@ -88,8 +87,8 @@ public class S3ClientUtil {
}
/**
- * Build public read policy JSON for a bucket using fastjson2.
- * This allows anonymous users to read/download objects from the bucket.
+ * Build public read policy JSON for a bucket using fastjson2. This allows anonymous users to
+ * read/download objects from the bucket.
*
* @param bucketName bucket name
* @return JSON policy string
@@ -264,13 +263,12 @@ public class S3ClientUtil {
public String generatePresignedGetUrl(String bucketName, String objectKey, int expirySeconds) {
try {
return minioClient.getPresignedObjectUrl(
- GetPresignedObjectUrlArgs.builder()
- .method(Method.GET)
- .bucket(bucketName)
- .object(objectKey)
- .expiry(expirySeconds)
- .build()
- );
+ GetPresignedObjectUrlArgs.builder()
+ .method(Method.GET)
+ .bucket(bucketName)
+ .object(objectKey)
+ .expiry(expirySeconds)
+ .build());
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException | ServerException e) {
log.error("S3 error on presign GET for bucket '{}', object '{}': {}", bucketName, objectKey, e.getMessage(), e);
throw new BusinessException(ResponseEnum.S3_PRESIGN_ERROR);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
index b17687c9..6c2451cd 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
@@ -2,21 +2,26 @@ package com.iflytek.astron.console.toolkit.controller.tool;
import com.iflytek.astron.console.commons.response.ApiResult;
import com.iflytek.astron.console.toolkit.common.anno.ResponseResultBody;
+import com.iflytek.astron.console.toolkit.entity.dto.rpa.StartReq;
import com.iflytek.astron.console.toolkit.entity.table.tool.RpaInfo;
import com.iflytek.astron.console.toolkit.entity.table.tool.RpaUserAssistant;
import com.iflytek.astron.console.toolkit.entity.tool.CreateRpaAssistantReq;
import com.iflytek.astron.console.toolkit.entity.tool.RpaAssistantResp;
import com.iflytek.astron.console.toolkit.entity.tool.UpdateRpaAssistantReq;
+import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
import com.iflytek.astron.console.toolkit.handler.UserInfoManagerHandler;
-import com.iflytek.astron.console.toolkit.service.tool.RpaAssistantService;
-import com.iflytek.astron.console.toolkit.service.tool.RpaInfoService;
+import com.iflytek.astron.console.toolkit.service.tool.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import java.io.IOException;
import java.util.List;
+import java.util.Map;
/**
* REST controller for RPA resources.
@@ -41,6 +46,9 @@ public class RpaController {
@Autowired
private RpaAssistantService rpaAssistantService;
+ @Autowired
+ private RpaDebugService rpaDebugService;
+
/**
* Get all available RPA platforms/sources.
*
@@ -124,4 +132,71 @@ public class RpaController {
String userId = UserInfoManagerHandler.getUserId();
rpaAssistantService.delete(userId, id);
}
+
+ @PostMapping("/debug/start")
+ public Map start(@RequestBody StartReq req,
+ @RequestHeader(value = "X-RPA-Token") String tokenOverride) {
+ DebugSession s = rpaDebugService.start(req.projectId, req.version, req.execPosition, req.params, tokenOverride);
+ return Map.of(
+ "debugId", s.getDebugId(),
+ "executionId", s.getExecutionId(),
+ "status", s.getStatus().name(),
+ "message", s.getMessage(),
+ "updatedAt", s.getUpdatedAt().toString());
+ }
+
+ /**
+ * SSE 事件流 事件名:status(过程更新)、final(终态)
+ */
+ @GetMapping(value = "/debug/{debugId}/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ public SseEmitter stream(@PathVariable String debugId) {
+ DebugSession s = rpaDebugService.get(debugId);
+ // 不超时,由服务器控制完成
+ SseEmitter emitter = new SseEmitter(0L);
+ if (s == null) {
+ try {
+ emitter.send(SseEmitter.event().name("final").data(Map.of("status", "FAILED", "message", "debug not found")));
+ } catch (IOException ignored) {
+ }
+ emitter.complete();
+ return emitter;
+ }
+
+ // 立即推一次当前状态
+ try {
+ emitter.send(SseEmitter.event().name("status").data(rpaDebugService.toView(s)));
+ } catch (IOException ignored) {
+ }
+
+ rpaDebugService.onUpdate(debugId, updated -> {
+ String eventName = switch (updated.getStatus()) {
+ case SUCCEEDED, FAILED, CANCELED, TIMEOUT -> "final";
+ default -> "status";
+ };
+ try {
+ emitter.send(SseEmitter.event().name(eventName).data(rpaDebugService.toView(updated)));
+ if ("final".equals(eventName)) {
+ emitter.complete();
+ rpaDebugService.offAll(debugId);
+ }
+ } catch (IOException e) {
+ emitter.completeWithError(e);
+ rpaDebugService.offAll(debugId);
+ }
+ });
+
+ return emitter;
+ }
+
+ /**
+ * 轮询兜底接口(前端断线或不支持 SSE 时使用)
+ */
+ @GetMapping("/debug/{debugId}/status")
+ public Object status(@PathVariable String debugId) {
+ DebugSession s = rpaDebugService.get(debugId);
+ if (s == null) {
+ return Map.of("code", "B0404", "message", "debug not found");
+ }
+ return rpaDebugService.toView(s);
+ }
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
new file mode 100644
index 00000000..9e7872ca
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
@@ -0,0 +1,26 @@
+package com.iflytek.astron.console.toolkit.entity.dto.rpa;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+
+@Data
+public class ExecuteAsyncResponse {
+ private String code;
+ private String msg;
+ private Data data;
+
+
+ public static class Data {
+ @JsonProperty("executionId")
+ private String executionId;
+
+ public String getExecutionId() {
+ return executionId;
+ }
+
+ public void setExecutionId(String executionId) {
+ this.executionId = executionId;
+ }
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
new file mode 100644
index 00000000..c31264b4
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
@@ -0,0 +1,167 @@
+package com.iflytek.astron.console.toolkit.entity.dto.rpa;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+public class ExecutionStatusResponse {
+ private String code;
+ private String msg;
+ private Data data;
+
+ public static class Data {
+ private Execution execution;
+
+ public Execution getExecution() {
+ return execution;
+ }
+
+ public void setExecution(Execution execution) {
+ this.execution = execution;
+ }
+ }
+
+ public static class Execution {
+ private String id;
+
+ @JsonProperty("project_id")
+ private String projectId;
+
+ // PENDING / COMPLETED / FAILED
+ private String status;
+
+ private JSONObject parameters;
+
+ private Map result;
+ private Object error;
+
+ @JsonProperty("exec_position")
+ private String execPosition;
+
+ private Integer version;
+
+ @JsonProperty("user_id")
+ private String userId;
+
+ @JsonProperty("start_time")
+ private String startTime;
+
+ @JsonProperty("end_time")
+ private String endTime;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProjectId() {
+ return projectId;
+ }
+
+ public void setProjectId(String projectId) {
+ this.projectId = projectId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public JSONObject getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(JSONObject parameters) {
+ this.parameters = parameters;
+ }
+
+ public Map getResult() {
+ return result;
+ }
+
+ public void setResult(Map result) {
+ this.result = result;
+ }
+
+ public Object getError() {
+ return error;
+ }
+
+ public void setError(Object error) {
+ this.error = error;
+ }
+
+ public String getExecPosition() {
+ return execPosition;
+ }
+
+ public void setExecPosition(String execPosition) {
+ this.execPosition = execPosition;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public void setVersion(Integer version) {
+ this.version = version;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(String startTime) {
+ this.startTime = startTime;
+ }
+
+ public String getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(String endTime) {
+ this.endTime = endTime;
+ }
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Data getData() {
+ return data;
+ }
+
+ public void setData(Data data) {
+ this.data = data;
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
new file mode 100644
index 00000000..4fb93132
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
@@ -0,0 +1,16 @@
+package com.iflytek.astron.console.toolkit.entity.dto.rpa;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+
+import java.util.Map;
+
+@Data
+public class StartReq {
+ @NotBlank
+ public String projectId;
+ public String execPosition = "EXECUTOR";
+ // 可空,默认 RPA 当前启用版本
+ public Integer version;
+ public Map params = Map.of();
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/enumVo/DebugStatus.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/enumVo/DebugStatus.java
new file mode 100644
index 00000000..9c7cdc6c
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/enumVo/DebugStatus.java
@@ -0,0 +1,23 @@
+package com.iflytek.astron.console.toolkit.entity.enumVo;
+
+/**
+ * RPA调试任务状态
+ */
+public enum DebugStatus {
+ // 本地创建
+ CREATED,
+ // 已拿到 executionId
+ SUBMITTED,
+ // RPA PENDING(运行中)
+ RUNNING,
+ // RPA COMPLETED
+ SUCCEEDED,
+ // RPA FAILED 或本地失败
+ FAILED,
+ // (预留,如后续支持取消)
+ CANCELED,
+ // 查询失败后重试
+ RETRYING,
+ // 超时
+ TIMEOUT
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/rpa/DebugSession.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/rpa/DebugSession.java
new file mode 100644
index 00000000..8edb41e6
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/vo/rpa/DebugSession.java
@@ -0,0 +1,140 @@
+package com.iflytek.astron.console.toolkit.entity.vo.rpa;
+
+import com.iflytek.astron.console.toolkit.entity.enumVo.DebugStatus;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DebugSession {
+ private final String debugId = UUID.randomUUID().toString();
+
+ private final String projectId;
+ private final Integer version;
+ private final String execPosition;
+ private final Map params;
+
+
+ private volatile String apiToken;
+ // RPA 返回
+ private volatile String executionId;
+ private volatile DebugStatus status = DebugStatus.CREATED;
+ // 错误或提示信息
+ private volatile String message;
+ // 若第三方不提供,保持 0
+ private volatile int progress = 0;
+ private volatile Instant createdAt = Instant.now();
+ private volatile Instant updatedAt = Instant.now();
+ // 生命周期(ms)
+ private final long expireAtEpochMilli;
+ private final AtomicInteger retries = new AtomicInteger(0);
+ // 当前轮询间隔(ms)
+ private volatile long nextPollMs;
+
+ public DebugSession(String projectId, Integer version, String execPosition, Map params, String apiToken, long timeoutSeconds, long initialPollMs) {
+ this.projectId = projectId;
+ this.version = version;
+ this.execPosition = (execPosition == null || execPosition.isBlank()) ? "EXECUTOR" : execPosition;
+ this.apiToken = apiToken;
+ this.params = params;
+ this.expireAtEpochMilli = System.currentTimeMillis() + timeoutSeconds * 1000;
+ this.nextPollMs = initialPollMs;
+ }
+
+ public String getDebugId() {
+ return debugId;
+ }
+
+ public String getProjectId() {
+ return projectId;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public String getExecPosition() {
+ return execPosition;
+ }
+
+ public Map getParams() {
+ return params;
+ }
+
+ public String getExecutionId() {
+ return executionId;
+ }
+
+ public void setExecutionId(String executionId) {
+ this.executionId = executionId;
+ touch();
+ }
+
+ public DebugStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(DebugStatus status) {
+ this.status = status;
+ touch();
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ touch();
+ }
+
+ public int getProgress() {
+ return progress;
+ }
+
+ public void setProgress(int progress) {
+ this.progress = progress;
+ touch();
+ }
+
+ public Instant getCreatedAt() {
+ return createdAt;
+ }
+
+ public Instant getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public void touch() {
+ this.updatedAt = Instant.now();
+ }
+
+ public boolean isExpired() {
+ return System.currentTimeMillis() > expireAtEpochMilli;
+ }
+
+ public int incRetries() {
+ return retries.incrementAndGet();
+ }
+
+ public int getRetries() {
+ return retries.get();
+ }
+
+ public long getNextPollMs() {
+ return nextPollMs;
+ }
+
+ public void setNextPollMs(long nextPollMs) {
+ this.nextPollMs = nextPollMs;
+ }
+
+ public String getApiToken() {
+ return apiToken;
+ }
+
+ public void setApiToken(String apiToken) {
+ this.apiToken = apiToken;
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
index 56418970..19b44c39 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
@@ -7,10 +7,12 @@ import java.util.*;
import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.exception.BusinessException;
import com.iflytek.astron.console.toolkit.config.properties.ApiUrl;
+import com.iflytek.astron.console.toolkit.entity.dto.rpa.ExecutionStatusResponse;
import com.iflytek.astron.console.toolkit.entity.enumVo.VarType;
import com.iflytek.astron.console.toolkit.util.OkHttpUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
@@ -150,4 +152,66 @@ public class RpaHandler {
String cut = new String(bytes, 0, max, StandardCharsets.UTF_8);
return cut + "...(" + bytes.length + "B)";
}
+
+ public String executeAsync(String projectId, String execPosition, Map params, Integer version, String bearerToken) {
+ Map body = new HashMap<>();
+ body.put("project_id", projectId);
+ body.put("exec_position", execPosition == null ? "EXECUTOR" : execPosition);
+ body.put("params", params == null ? Map.of() : params);
+ if (version != null)
+ body.put("version", version);
+
+ Map headerMap = new HashMap<>();
+ headerMap.put("Authorization", "Bearer " + bearerToken);
+ String url = apiUrl.getRpaUrl() + "/api/rpa-openapi/workflows/execute-async";
+ JSONObject data = null;
+ try {
+ log.info("executeAsync rpa uri: {}, body: {},token: {}", url, body, headerMap);
+ String post = OkHttpUtil.post(url, headerMap, JSON.toJSONString(body));
+
+ if (StringUtils.isBlank(post)) {
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "Empty response from execute-async");
+ }
+ log.info("executeAsync rpa response {}", post);
+ JSONObject resp = JSONObject.parseObject(post);
+ String code = resp.getString("code");
+ if (!"0000".equals(code)) {
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async failed: " + code + " " + resp.getString("msg"));
+ }
+ data = resp.getJSONObject("data");
+ if (data == null || data.getString("executionId") == null) {
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async missing executionId");
+ }
+ } catch (BusinessException e) {
+ throw new RuntimeException(e);
+ } catch (Exception e) {
+ log.error("execute-async failed", e);
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async failed");
+ }
+ return data.getString("executionId");
+ }
+
+ public ExecutionStatusResponse.Execution getExecution(String executionId, String bearerToken) {
+ String url = apiUrl.getRpaUrl() + "/api/rpa-openapi/executions/" + executionId;
+ Map headerMap = new HashMap<>();
+ headerMap.put("Authorization", "Bearer " + bearerToken);
+ log.info("getExecution rpa uri: {}, executionId: {},token: {}", url, executionId, headerMap);
+
+ String responseStr = OkHttpUtil.get(url, headerMap);
+ log.info("getExecution response: {}", responseStr);
+
+ if (StringUtils.isBlank(responseStr))
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "Empty response from executions");
+ JSONObject resp = JSONObject.parseObject(responseStr);
+ String code = resp.getString("code");
+ if (!"0000".equals(code)) {
+ throw new RuntimeException("getExecution failed: " + code + " " + resp.getString("msg"));
+ }
+ JSONObject data = resp.getJSONObject("data");
+ if (data == null || data.getJSONObject("execution") == null) {
+ throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "getExecution missing execution payload");
+ }
+ ExecutionStatusResponse.Execution execution = JSON.parseObject(String.valueOf(data.getJSONObject("execution")), ExecutionStatusResponse.Execution.class);
+ return execution;
+ }
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
index dfdb404f..b920694c 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/external/ExternalApiService.java
@@ -39,7 +39,7 @@ public class ExternalApiService {
// Check for common error responses
if (response.contains("404") || response.contains("not found") ||
- response.contains("error") || !response.trim().startsWith("{")) {
+ response.contains("error") || !response.trim().startsWith("{")) {
log.warn("External API not available (response: {}), using mock data for apiKey: {}",
response.trim(), apiKey);
return createMockResponse(apiKey);
@@ -54,8 +54,8 @@ public class ExternalApiService {
}
/**
- * Create mock response when external API is not available
- * TODO: Remove this when external API is fixed
+ * Create mock response when external API is not available TODO: Remove this when external API is
+ * fixed
*/
private AppInfoResponse createMockResponse(String apiKey) {
AppInfoResponse response = new AppInfoResponse();
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
index 0aa722e2..4ddec2c7 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java
@@ -298,9 +298,9 @@ public class ModelService extends ServiceImpl {
LambdaQueryWrapper lqw = new LambdaQueryWrapper()
.eq(Model::getName, request.getModelName())
.eq(Model::getIsDeleted, 0);
- if(spaceId != null){
+ if (spaceId != null) {
lqw.eq(Model::getSpaceId, spaceId);
- }else{
+ } else {
lqw.eq(Model::getUid, request.getUid()).isNull(Model::getSpaceId);
}
Model exist = this.getOne(lqw);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
new file mode 100644
index 00000000..bdadbc41
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
@@ -0,0 +1,141 @@
+package com.iflytek.astron.console.toolkit.service.tool;
+
+import com.iflytek.astron.console.toolkit.entity.dto.rpa.ExecutionStatusResponse;
+import com.iflytek.astron.console.toolkit.entity.enumVo.DebugStatus;
+import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
+import com.iflytek.astron.console.toolkit.handler.RpaHandler;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * @Author clliu19
+ * @Date: 2025/10/17 14:04
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class RpaDebugService {
+ private final RpaHandler rpaHandler;
+ private final Map sessions = new ConcurrentHashMap<>();
+ private final Map>> listeners = new ConcurrentHashMap<>();
+
+ public DebugSession start(String projectId, Integer version, String execPosition, Map params, String tokenOverride) {
+ DebugSession s = new DebugSession(projectId, version, execPosition, params, tokenOverride,
+ 3000, 1000);
+ sessions.put(s.getDebugId(), s);
+
+ try {
+ String executionId = rpaHandler.executeAsync(projectId, execPosition, params, version, tokenOverride);
+ s.setExecutionId(executionId);
+ s.setStatus(DebugStatus.SUBMITTED);
+ s.setMessage("submitted");
+ emit(s);
+ } catch (Exception e) {
+ s.setStatus(DebugStatus.FAILED);
+ s.setMessage("execute-async failed: " + e.getMessage());
+ emit(s);
+ }
+ return s;
+ }
+
+ public DebugSession get(String debugId) {
+ return sessions.get(debugId);
+ }
+
+ public Collection all() {
+ return sessions.values();
+ }
+
+ public void onUpdate(String debugId, Consumer cb) {
+ listeners.computeIfAbsent(debugId, k -> Collections.synchronizedList(new ArrayList<>())).add(cb);
+ }
+
+ public void offAll(String debugId) {
+ listeners.remove(debugId);
+ }
+
+ private void emit(DebugSession s) {
+ List> ls = listeners.get(s.getDebugId());
+ if (ls != null) {
+ for (Consumer cb : ls)
+ cb.accept(s);
+ }
+ }
+
+ /**
+ * 被 PollScheduler 调用:检查状态并更新
+ */
+ public void pollOnce(DebugSession s, String tokenOverride) {
+ if (s == null)
+ return;
+ if (isFinal(s.getStatus()))
+ return;
+
+ // 生命周期到期
+ if (s.isExpired()) {
+ s.setStatus(DebugStatus.TIMEOUT);
+ s.setMessage("Timeout after " + 3000 + "s");
+ emit(s);
+ return;
+ }
+
+ // 还没拿到 executionId,跳过
+ if (s.getExecutionId() == null)
+ return;
+
+ try {
+ ExecutionStatusResponse.Execution ex = rpaHandler.getExecution(s.getExecutionId(), tokenOverride);
+ String rpaStatus = ex.getStatus();
+ // RPA 状态映射
+ if ("PENDING".equalsIgnoreCase(rpaStatus)) {
+ s.setStatus(DebugStatus.RUNNING);
+ s.setMessage("RPA PENDING");
+ // 运行中逐步增大间隔(上限)
+ long next = Math.min((long) (s.getNextPollMs() * 1.4), 15000);
+ s.setNextPollMs(next);
+ } else if ("COMPLETED".equalsIgnoreCase(rpaStatus)) {
+ s.setStatus(DebugStatus.SUCCEEDED);
+ s.setMessage("RPA COMPLETED");
+ } else if ("FAILED".equalsIgnoreCase(rpaStatus)) {
+ s.setStatus(DebugStatus.FAILED);
+ String msg = (ex.getError() != null)
+ ? String.valueOf(ex.getError())
+ : "RPA FAILED";
+ s.setMessage(msg);
+ } else {
+ s.setStatus(DebugStatus.RETRYING);
+ s.setMessage("Unknown status: " + rpaStatus);
+ }
+ emit(s);
+ } catch (Exception e) {
+ log.error("poll once error e = ", e);
+ s.setStatus(DebugStatus.RETRYING);
+ s.setMessage("query failed: " + e.getMessage());
+ int r = s.incRetries();
+ long backoff = Math.min((long) (1000 * Math.pow(1.8, r)), 15000);
+ s.setNextPollMs(backoff);
+ emit(s);
+ }
+ }
+
+ private boolean isFinal(DebugStatus st) {
+ return st == DebugStatus.SUCCEEDED || st == DebugStatus.FAILED || st == DebugStatus.CANCELED || st == DebugStatus.TIMEOUT;
+ }
+
+ // 用于 SSE 输出的轻量 Map
+ public Map toView(DebugSession s) {
+ Map m = new LinkedHashMap<>();
+ m.put("debugId", s.getDebugId());
+ m.put("executionId", s.getExecutionId());
+ m.put("status", s.getStatus().name());
+ m.put("message", s.getMessage());
+ m.put("progress", s.getProgress());
+ m.put("updatedAt", s.getUpdatedAt().toString());
+ return m;
+ }
+}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
new file mode 100644
index 00000000..09050089
--- /dev/null
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
@@ -0,0 +1,41 @@
+package com.iflytek.astron.console.toolkit.task.scheduler;
+
+import com.iflytek.astron.console.toolkit.entity.enumVo.DebugStatus;
+import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
+import com.iflytek.astron.console.toolkit.service.tool.RpaDebugService;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class PollScheduler {
+
+ private final RpaDebugService debugService;
+ private final Map lastPollAt = new ConcurrentHashMap<>();
+
+ public PollScheduler(RpaDebugService debugService) {
+ this.debugService = debugService;
+ }
+
+ @Scheduled(fixedDelay = 500)
+ public void tick() {
+ long now = System.currentTimeMillis();
+ for (DebugSession s : debugService.all()) {
+ if (isFinal(s.getStatus()))
+ continue;
+ String key = s.getDebugId();
+ long last = lastPollAt.getOrDefault(key, 0L);
+ if (now - last >= s.getNextPollMs()) {
+ lastPollAt.put(key, now);
+ // 可选:支持不同会话携带不同 Token,这里简化为不覆盖;如需覆盖,可把 token 缓存在 session 上
+ debugService.pollOnce(s, s.getApiToken());
+ }
+ }
+ }
+
+ private boolean isFinal(DebugStatus st) {
+ return st == DebugStatus.SUCCEEDED || st == DebugStatus.FAILED || st == DebugStatus.CANCELED || st == DebugStatus.TIMEOUT;
+ }
+}
diff --git a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/FileControllerTest.java b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/FileControllerTest.java
index a5b60fb0..6dde5c51 100644
--- a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/FileControllerTest.java
+++ b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/FileControllerTest.java
@@ -41,13 +41,10 @@ import static org.mockito.Mockito.*;
/**
* Unit tests for FileController
- *
- * Technology Stack: JUnit5 + Mockito + AssertJ
- * Coverage Requirements:
- * - JaCoCo Statement Coverage >= 80%
- * - JaCoCo Branch Coverage >= 90%
- * - High PIT Mutation Test Score
- * - Covers normal flows, edge cases, and exceptions
+ *
+ * Technology Stack: JUnit5 + Mockito + AssertJ Coverage Requirements: - JaCoCo Statement Coverage
+ * >= 80% - JaCoCo Branch Coverage >= 90% - High PIT Mutation Test Score - Covers normal flows, edge
+ * cases, and exceptions
*
* @author AI Assistant
*/
@@ -77,8 +74,8 @@ class FileControllerTest {
private KnowledgeQueryVO knowledgeQueryVO;
/**
- * Set up test fixtures before each test
- * Initializes common test data including mock file info, VO objects, and query parameters
+ * Set up test fixtures before each test Initializes common test data including mock file info, VO
+ * objects, and query parameters
*/
@BeforeEach
void setUp() {
@@ -120,8 +117,8 @@ class FileControllerTest {
class FileUploadTests {
/**
- * Test successful file upload
- * Verifies that a file can be uploaded successfully and returns correct result
+ * Test successful file upload Verifies that a file can be uploaded successfully and returns correct
+ * result
*/
@Test
@DisplayName("Upload file successfully")
@@ -144,8 +141,8 @@ class FileControllerTest {
}
/**
- * Test file upload with empty file name
- * Verifies that uploading a file with empty name throws BusinessException
+ * Test file upload with empty file name Verifies that uploading a file with empty name throws
+ * BusinessException
*/
@Test
@DisplayName("Upload file - Empty file name")
@@ -164,8 +161,7 @@ class FileControllerTest {
}
/**
- * Test successful HTML file creation
- * Verifies that HTML files can be created from URL addresses
+ * Test successful HTML file creation Verifies that HTML files can be created from URL addresses
*/
@Test
@DisplayName("Create HTML file successfully")
@@ -186,8 +182,8 @@ class FileControllerTest {
}
/**
- * Test HTML file creation with empty address list
- * Verifies that creating HTML files with empty address list returns empty result
+ * Test HTML file creation with empty address list Verifies that creating HTML files with empty
+ * address list returns empty result
*/
@Test
@DisplayName("Create HTML file - Empty address list")
@@ -215,9 +211,9 @@ class FileControllerTest {
class FileSliceTests {
/**
- * Test successful file slicing with normal separator
- * Verifies that files can be sliced successfully with provided separator
- *
+ * Test successful file slicing with normal separator Verifies that files can be sliced successfully
+ * with provided separator
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -242,9 +238,9 @@ class FileControllerTest {
}
/**
- * Test successful file slicing with empty separator defaults to newline
- * Verifies that empty separator is automatically replaced with default newline separator
- *
+ * Test successful file slicing with empty separator defaults to newline Verifies that empty
+ * separator is automatically replaced with default newline separator
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -271,9 +267,9 @@ class FileControllerTest {
}
/**
- * Test successful file slicing with null separator defaults to newline
- * Verifies that null separator is automatically replaced with default newline separator
- *
+ * Test successful file slicing with null separator defaults to newline Verifies that null separator
+ * is automatically replaced with default newline separator
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -299,9 +295,9 @@ class FileControllerTest {
}
/**
- * Test file slicing failure with error message returned
- * Verifies that slicing failure returns appropriate error code and message
- *
+ * Test file slicing failure with error message returned Verifies that slicing failure returns
+ * appropriate error code and message
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -329,7 +325,7 @@ class FileControllerTest {
/**
* Test file slicing throws InterruptedException
* Verifies that InterruptedException is properly propagated when thread is interrupted
- *
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -355,9 +351,8 @@ class FileControllerTest {
class FileEmbeddingTests {
/**
- * Test successful file embedding
- * Verifies that files can be embedded successfully without errors
- *
+ * Test successful file embedding Verifies that files can be embedded successfully without errors
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -377,9 +372,9 @@ class FileControllerTest {
}
/**
- * Test file embedding throws RuntimeException
- * Verifies that RuntimeException is properly thrown when embedding fails
- *
+ * Test file embedding throws RuntimeException Verifies that RuntimeException is properly thrown
+ * when embedding fails
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -388,7 +383,8 @@ class FileControllerTest {
void embeddingFiles_ThrowsRuntimeException() throws ExecutionException, InterruptedException {
// Given
doThrow(new RuntimeException("Embedding failed"))
- .when(fileInfoV2Service).embeddingFiles(dealFileVO, request);
+ .when(fileInfoV2Service)
+ .embeddingFiles(dealFileVO, request);
// When & Then
assertThatThrownBy(() -> fileController.embeddingFiles(dealFileVO, request))
@@ -398,9 +394,9 @@ class FileControllerTest {
}
/**
- * Test successful background file embedding
- * Verifies that files can be embedded in background successfully
- *
+ * Test successful background file embedding Verifies that files can be embedded in background
+ * successfully
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -420,9 +416,8 @@ class FileControllerTest {
}
/**
- * Test retry failed files
- * Verifies that failed files can be retried successfully
- *
+ * Test retry failed files Verifies that failed files can be retried successfully
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -450,8 +445,7 @@ class FileControllerTest {
class FileStatusTests {
/**
- * Test get file indexing status
- * Verifies that file indexing status can be retrieved successfully
+ * Test get file indexing status Verifies that file indexing status can be retrieved successfully
*/
@Test
@DisplayName("Get file indexing status")
@@ -471,8 +465,8 @@ class FileControllerTest {
}
/**
- * Test get file summary information
- * Verifies that file summary information can be retrieved successfully
+ * Test get file summary information Verifies that file summary information can be retrieved
+ * successfully
*/
@Test
@DisplayName("Get file summary information")
@@ -492,8 +486,8 @@ class FileControllerTest {
}
/**
- * Test get file info by sourceId
- * Verifies that file information can be retrieved by sourceId successfully
+ * Test get file info by sourceId Verifies that file information can be retrieved by sourceId
+ * successfully
*/
@Test
@DisplayName("Get file info by sourceId")
@@ -521,8 +515,8 @@ class FileControllerTest {
class KnowledgeTests {
/**
- * Test list preview knowledge by page
- * Verifies that preview knowledge can be queried with pagination successfully
+ * Test list preview knowledge by page Verifies that preview knowledge can be queried with
+ * pagination successfully
*/
@Test
@DisplayName("List preview knowledge by page")
@@ -541,8 +535,7 @@ class FileControllerTest {
}
/**
- * Test list knowledge by page
- * Verifies that knowledge can be queried with pagination successfully
+ * Test list knowledge by page Verifies that knowledge can be queried with pagination successfully
*/
@Test
@DisplayName("List knowledge by page")
@@ -563,8 +556,8 @@ class FileControllerTest {
}
/**
- * Test download knowledge by violation
- * Verifies that knowledge marked as violation can be downloaded successfully
+ * Test download knowledge by violation Verifies that knowledge marked as violation can be
+ * downloaded successfully
*/
@Test
@DisplayName("Download knowledge by violation")
@@ -588,8 +581,8 @@ class FileControllerTest {
class FileQueryTests {
/**
- * Test query file list with default parameters
- * Verifies that file list can be queried with default pagination parameters
+ * Test query file list with default parameters Verifies that file list can be queried with default
+ * pagination parameters
*/
@Test
@DisplayName("Query file list - With defaults")
@@ -610,8 +603,8 @@ class FileControllerTest {
}
/**
- * Test query file list with custom parameters
- * Verifies that file list can be queried with custom pagination and filter parameters
+ * Test query file list with custom parameters Verifies that file list can be queried with custom
+ * pagination and filter parameters
*/
@Test
@DisplayName("Query file list - With custom params")
@@ -636,8 +629,8 @@ class FileControllerTest {
}
/**
- * Test search file
- * Verifies that files can be searched with specified criteria and returns SSE emitter
+ * Test search file Verifies that files can be searched with specified criteria and returns SSE
+ * emitter
*/
@Test
@DisplayName("Search file")
@@ -664,8 +657,8 @@ class FileControllerTest {
}
/**
- * Test search file with null parameters
- * Verifies that file search can handle null parameters gracefully
+ * Test search file with null parameters Verifies that file search can handle null parameters
+ * gracefully
*/
@Test
@DisplayName("Search file - With null params")
@@ -694,8 +687,8 @@ class FileControllerTest {
class FolderOperationsTests {
/**
- * Test create folder successfully without tags
- * Verifies that a folder can be created without any tags
+ * Test create folder successfully without tags Verifies that a folder can be created without any
+ * tags
*/
@Test
@DisplayName("Create folder successfully - No tags")
@@ -714,8 +707,8 @@ class FileControllerTest {
}
/**
- * Test create folder successfully with valid tags
- * Verifies that a folder can be created with tags of normal length
+ * Test create folder successfully with valid tags Verifies that a folder can be created with tags
+ * of normal length
*/
@Test
@DisplayName("Create folder successfully - With valid tags")
@@ -734,8 +727,8 @@ class FileControllerTest {
}
/**
- * Test create folder successfully with max length tag
- * Verifies that a folder can be created with tag exactly 30 characters long
+ * Test create folder successfully with max length tag Verifies that a folder can be created with
+ * tag exactly 30 characters long
*/
@Test
@DisplayName("Create folder successfully - With max length tag")
@@ -755,8 +748,8 @@ class FileControllerTest {
}
/**
- * Test create folder failure when tag is too long
- * Verifies that folder creation fails when tag exceeds 30 characters
+ * Test create folder failure when tag is too long Verifies that folder creation fails when tag
+ * exceeds 30 characters
*/
@Test
@DisplayName("Create folder failure - Tag too long")
@@ -773,8 +766,8 @@ class FileControllerTest {
}
/**
- * Test create folder failure when one of many tags is too long
- * Verifies that folder creation fails when at least one tag exceeds length limit
+ * Test create folder failure when one of many tags is too long Verifies that folder creation fails
+ * when at least one tag exceeds length limit
*/
@Test
@DisplayName("Create folder failure - One of many tags too long")
@@ -789,8 +782,8 @@ class FileControllerTest {
}
/**
- * Test create folder successfully with empty tag list
- * Verifies that a folder can be created with an empty tag list
+ * Test create folder successfully with empty tag list Verifies that a folder can be created with an
+ * empty tag list
*/
@Test
@DisplayName("Create folder successfully - Empty tag list")
@@ -809,8 +802,7 @@ class FileControllerTest {
}
/**
- * Test update folder successfully
- * Verifies that a folder can be updated successfully
+ * Test update folder successfully Verifies that a folder can be updated successfully
*/
@Test
@DisplayName("Update folder successfully")
@@ -828,8 +820,7 @@ class FileControllerTest {
}
/**
- * Test delete folder successfully
- * Verifies that a folder can be deleted successfully
+ * Test delete folder successfully Verifies that a folder can be deleted successfully
*/
@Test
@DisplayName("Delete folder successfully")
@@ -856,8 +847,7 @@ class FileControllerTest {
class FileOperationsTests {
/**
- * Test update file successfully
- * Verifies that a file can be updated successfully
+ * Test update file successfully Verifies that a file can be updated successfully
*/
@Test
@DisplayName("Update file successfully")
@@ -875,8 +865,7 @@ class FileControllerTest {
}
/**
- * Test delete file successfully
- * Verifies that a file can be deleted successfully
+ * Test delete file successfully Verifies that a file can be deleted successfully
*/
@Test
@DisplayName("Delete file successfully")
@@ -897,8 +886,7 @@ class FileControllerTest {
}
/**
- * Test enable file
- * Verifies that a file can be enabled successfully
+ * Test enable file Verifies that a file can be enabled successfully
*/
@Test
@DisplayName("Enable file")
@@ -918,8 +906,7 @@ class FileControllerTest {
}
/**
- * Test disable file
- * Verifies that a file can be disabled successfully
+ * Test disable file Verifies that a file can be disabled successfully
*/
@Test
@DisplayName("Disable file")
@@ -939,8 +926,7 @@ class FileControllerTest {
}
/**
- * Test get file directory tree
- * Verifies that file directory tree can be retrieved successfully
+ * Test get file directory tree Verifies that file directory tree can be retrieved successfully
*/
@Test
@DisplayName("Get file directory tree")
@@ -949,8 +935,7 @@ class FileControllerTest {
Long fileId = 123L;
List expectedTree = Arrays.asList(
new FileDirectoryTree(),
- new FileDirectoryTree()
- );
+ new FileDirectoryTree());
when(fileInfoV2Service.listFileDirectoryTree(fileId)).thenReturn(expectedTree);
// When
@@ -964,8 +949,8 @@ class FileControllerTest {
}
/**
- * Test get file directory tree with empty result
- * Verifies that empty result is handled correctly when file has no directory tree
+ * Test get file directory tree with empty result Verifies that empty result is handled correctly
+ * when file has no directory tree
*/
@Test
@DisplayName("Get file directory tree - Empty result")
@@ -993,9 +978,9 @@ class FileControllerTest {
class EdgeCaseTests {
/**
- * Test sliceFiles with empty separator list
- * Verifies that empty separator list causes IndexOutOfBoundsException
- *
+ * Test sliceFiles with empty separator list Verifies that empty separator list causes
+ * IndexOutOfBoundsException
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -1011,8 +996,8 @@ class FileControllerTest {
}
/**
- * Test queryFileList with large page number
- * Verifies that query works correctly with maximum integer page number
+ * Test queryFileList with large page number Verifies that query works correctly with maximum
+ * integer page number
*/
@Test
@DisplayName("queryFileList - Large page number")
@@ -1033,8 +1018,8 @@ class FileControllerTest {
}
/**
- * Test queryFileList with large page size
- * Verifies that query works correctly with large page size value
+ * Test queryFileList with large page size Verifies that query works correctly with large page size
+ * value
*/
@Test
@DisplayName("queryFileList - Large page size")
@@ -1055,8 +1040,7 @@ class FileControllerTest {
}
/**
- * Test deleteFile with empty string ID
- * Verifies that file deletion works with empty string ID
+ * Test deleteFile with empty string ID Verifies that file deletion works with empty string ID
*/
@Test
@DisplayName("deleteFile - Empty string ID")
@@ -1077,8 +1061,8 @@ class FileControllerTest {
}
/**
- * Test createFolder with tag at boundary value
- * Verifies that folder creation works correctly with tags at length boundary (29, 30 characters)
+ * Test createFolder with tag at boundary value Verifies that folder creation works correctly with
+ * tags at length boundary (29, 30 characters)
*/
@Test
@DisplayName("createFolder - Tag at boundary")
@@ -1086,19 +1070,19 @@ class FileControllerTest {
// Given - Test with 29, 30, 31 characters
String tag29 = "a".repeat(29);
String tag30 = "a".repeat(30);
-
+
// 29 characters should succeed
createFolderVO.setTags(Collections.singletonList(tag29));
doNothing().when(fileInfoV2Service).createFolder(createFolderVO);
-
+
ApiResult result1 = fileController.createFolder(createFolderVO);
assertThat(result1.code()).isEqualTo(0);
-
+
// 30 characters should succeed
createFolderVO.setTags(Collections.singletonList(tag30));
ApiResult result2 = fileController.createFolder(createFolderVO);
assertThat(result2.code()).isEqualTo(0);
-
+
verify(fileInfoV2Service, times(2)).createFolder(createFolderVO);
}
}
@@ -1127,9 +1111,9 @@ class FileControllerTest {
}
/**
- * Test embeddingFiles throws BusinessException
- * Verifies that BusinessException is properly thrown when embedding fails
- *
+ * Test embeddingFiles throws BusinessException Verifies that BusinessException is properly thrown
+ * when embedding fails
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -1138,7 +1122,8 @@ class FileControllerTest {
void embeddingFiles_BusinessException() throws ExecutionException, InterruptedException {
// Given
doThrow(new BusinessException(ResponseEnum.REPO_FILE_EMBEDDING_FAILED))
- .when(fileInfoV2Service).embeddingFiles(dealFileVO, request);
+ .when(fileInfoV2Service)
+ .embeddingFiles(dealFileVO, request);
// When & Then
assertThatThrownBy(() -> fileController.embeddingFiles(dealFileVO, request))
@@ -1146,8 +1131,8 @@ class FileControllerTest {
}
/**
- * Test deleteFolder when service throws exception
- * Verifies that BusinessException is properly thrown when folder doesn't exist
+ * Test deleteFolder when service throws exception Verifies that BusinessException is properly
+ * thrown when folder doesn't exist
*/
@Test
@DisplayName("deleteFolder - Service throws exception")
@@ -1155,7 +1140,8 @@ class FileControllerTest {
// Given
Long folderId = 123L;
doThrow(new BusinessException(ResponseEnum.REPO_FOLDER_NOT_EXIST))
- .when(fileInfoV2Service).deleteFolder(folderId);
+ .when(fileInfoV2Service)
+ .deleteFolder(folderId);
// When & Then
assertThatThrownBy(() -> fileController.deleteFolder(folderId))
@@ -1163,8 +1149,8 @@ class FileControllerTest {
}
/**
- * Test getFileInfoV2BySourceId when file doesn't exist
- * Verifies that BusinessException is properly thrown when file is not found
+ * Test getFileInfoV2BySourceId when file doesn't exist Verifies that BusinessException is properly
+ * thrown when file is not found
*/
@Test
@DisplayName("getFileInfoV2BySourceId - File not found")
@@ -1182,7 +1168,7 @@ class FileControllerTest {
/**
* Test sliceFiles throws RuntimeException
* Verifies that RuntimeException is properly thrown when slice processing fails
- *
+ *
* @throws InterruptedException if the operation is interrupted
* @throws ExecutionException if the operation fails during execution
*/
@@ -1226,7 +1212,7 @@ class FileControllerTest {
/**
* Test complete file processing flow including upload, slice, and embedding
* Verifies that the entire file processing workflow works correctly from start to finish
- *
+ *
* @throws ExecutionException if the operation fails during execution
* @throws InterruptedException if the operation is interrupted
*/
@@ -1252,15 +1238,15 @@ class FileControllerTest {
assertThat(uploadResult.code()).isEqualTo(0);
assertThat(sliceResultApi.code()).isEqualTo(0);
assertThat(embeddingResult.code()).isEqualTo(0);
-
+
verify(fileInfoV2Service, times(1)).uploadFile(multipartFile, 0L, 100L, "tag", request);
verify(fileInfoV2Service, times(1)).sliceFiles(dealFileVO);
verify(fileInfoV2Service, times(1)).embeddingFiles(dealFileVO, request);
}
/**
- * Test folder operations flow including create, update, and delete
- * Verifies that folder operations can be performed sequentially without errors
+ * Test folder operations flow including create, update, and delete Verifies that folder operations
+ * can be performed sequentially without errors
*/
@Test
@DisplayName("Folder operations flow - Create, update, delete")
@@ -1279,11 +1265,10 @@ class FileControllerTest {
assertThat(createResult.code()).isEqualTo(0);
assertThat(updateResult.code()).isEqualTo(0);
assertThat(deleteResult.code()).isEqualTo(0);
-
+
verify(fileInfoV2Service, times(1)).createFolder(createFolderVO);
verify(fileInfoV2Service, times(1)).updateFolder(createFolderVO);
verify(fileInfoV2Service, times(1)).deleteFolder(1L);
}
}
}
-
diff --git a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/KnowledgeControllerTest.java b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/KnowledgeControllerTest.java
index 539527a4..2f12b56b 100644
--- a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/KnowledgeControllerTest.java
+++ b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/KnowledgeControllerTest.java
@@ -31,10 +31,14 @@ import static org.mockito.Mockito.*;
/**
* Unit tests for KnowledgeController
- *
- * Technology Stack: JUnit5 + Mockito + AssertJ
- *
- * Coverage Requirements:
+ *
+ *
+ * Technology Stack: JUnit5 + Mockito + AssertJ
+ *
+ *
+ *
+ * Coverage Requirements:
+ *
*
* - JaCoCo Statement Coverage >= 80%
* - JaCoCo Branch Coverage >= 90%
@@ -58,8 +62,8 @@ class KnowledgeControllerTest {
private KnowledgeVO knowledgeVO;
/**
- * Set up test fixtures before each test method.
- * Initializes common test data including mock Knowledge entity and KnowledgeVO.
+ * Set up test fixtures before each test method. Initializes common test data including mock
+ * Knowledge entity and KnowledgeVO.
*/
@BeforeEach
void setUp() {
@@ -82,8 +86,8 @@ class KnowledgeControllerTest {
}
/**
- * Test cases for the createKnowledge method.
- * Validates knowledge creation functionality including success scenarios and error handling.
+ * Test cases for the createKnowledge method. Validates knowledge creation functionality including
+ * success scenarios and error handling.
*/
@Nested
@DisplayName("createKnowledge Tests")
@@ -91,7 +95,7 @@ class KnowledgeControllerTest {
/**
* Test successful knowledge creation with valid input.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -117,7 +121,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge creation with empty VO object.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -179,7 +183,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge creation when service returns null.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -202,8 +206,8 @@ class KnowledgeControllerTest {
}
/**
- * Test cases for the updateKnowledge method.
- * Validates knowledge update functionality including tag validation and error handling.
+ * Test cases for the updateKnowledge method. Validates knowledge update functionality including tag
+ * validation and error handling.
*/
@Nested
@DisplayName("updateKnowledge Tests")
@@ -211,7 +215,7 @@ class KnowledgeControllerTest {
/**
* Test successful knowledge update without tags.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -236,7 +240,7 @@ class KnowledgeControllerTest {
/**
* Test successful knowledge update with empty tags list.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -260,7 +264,7 @@ class KnowledgeControllerTest {
/**
* Test successful knowledge update with valid tags.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -285,7 +289,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with tag length equal to 30 (boundary value).
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -310,7 +314,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with tag length equal to 29 (boundary value).
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -383,8 +387,7 @@ class KnowledgeControllerTest {
// Given
List tags = Arrays.asList(
"1234567890123456789012345678901", // 31 characters, too long
- "validTag"
- );
+ "validTag");
knowledgeVO.setTags(tags);
// When & Then
@@ -398,7 +401,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with tags containing empty string.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -422,7 +425,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with tags containing single character.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -503,7 +506,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with valid Chinese tags.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -547,7 +550,7 @@ class KnowledgeControllerTest {
/**
* Test knowledge update with many valid tags.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -574,8 +577,8 @@ class KnowledgeControllerTest {
}
/**
- * Test cases for the enableKnowledge method.
- * Validates knowledge enable/disable functionality and various input scenarios.
+ * Test cases for the enableKnowledge method. Validates knowledge enable/disable functionality and
+ * various input scenarios.
*/
@Nested
@DisplayName("enableKnowledge Tests")
@@ -583,7 +586,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge with enabled=1.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -609,7 +612,7 @@ class KnowledgeControllerTest {
/**
* Test disabling knowledge with enabled=0.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -635,7 +638,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge with empty string ID.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -659,7 +662,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge with non-standard enabled value.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -724,7 +727,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge when service returns null.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -749,7 +752,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge when service returns empty string.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -774,7 +777,7 @@ class KnowledgeControllerTest {
/**
* Test enabling knowledge with negative enabled value.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -798,8 +801,8 @@ class KnowledgeControllerTest {
}
/**
- * Test cases for the deleteKnowledge method.
- * Validates knowledge deletion functionality with various scenarios.
+ * Test cases for the deleteKnowledge method. Validates knowledge deletion functionality with
+ * various scenarios.
*/
@Nested
@DisplayName("deleteKnowledge Tests")
@@ -875,7 +878,8 @@ class KnowledgeControllerTest {
// Given
String knowledgeId = "knowledge-001";
doThrow(new RuntimeException("Delete failed"))
- .when(knowledgeService).deleteKnowledge(anyString());
+ .when(knowledgeService)
+ .deleteKnowledge(anyString());
// When & Then
assertThatThrownBy(() -> knowledgeController.deleteKnowledge(knowledgeId))
@@ -894,7 +898,8 @@ class KnowledgeControllerTest {
// Given
String knowledgeId = "knowledge-001";
doThrow(new BusinessException(ResponseEnum.REPO_KNOWLEDGE_NOT_EXIST))
- .when(knowledgeService).deleteKnowledge(anyString());
+ .when(knowledgeService)
+ .deleteKnowledge(anyString());
// When & Then
assertThatThrownBy(() -> knowledgeController.deleteKnowledge(knowledgeId))
@@ -970,8 +975,7 @@ class KnowledgeControllerTest {
}
/**
- * Integration scenario tests.
- * Tests complete workflows combining multiple operations.
+ * Integration scenario tests. Tests complete workflows combining multiple operations.
*/
@Nested
@DisplayName("Integration Scenario Tests")
@@ -979,7 +983,7 @@ class KnowledgeControllerTest {
/**
* Test full lifecycle: create, update, enable, and delete knowledge.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -990,7 +994,7 @@ class KnowledgeControllerTest {
String knowledgeId = "knowledge-full-test";
KnowledgeVO createVO = new KnowledgeVO();
createVO.setContent("Initial content");
-
+
Knowledge createdKnowledge = Knowledge.builder()
.id(knowledgeId)
.enabled(0)
@@ -1036,7 +1040,7 @@ class KnowledgeControllerTest {
/**
* Test boundary scenario where all tags have exactly 30 characters.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -1047,7 +1051,7 @@ class KnowledgeControllerTest {
List tags = Arrays.asList(
"123456789012345678901234567890", // 30 chars
"abcdefghijklmnopqrstuvwxyz1234", // 30 chars
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234" // 30 chars
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234" // 30 chars
);
knowledgeVO.setTags(tags);
when(knowledgeService.updateKnowledge(any(KnowledgeVO.class))).thenReturn(mockKnowledge);
@@ -1079,8 +1083,7 @@ class KnowledgeControllerTest {
}
/**
- * Parameter validation tests.
- * Tests handling of null and edge-case parameter values.
+ * Parameter validation tests. Tests handling of null and edge-case parameter values.
*/
@Nested
@DisplayName("Parameter Validation Tests")
@@ -1088,7 +1091,7 @@ class KnowledgeControllerTest {
/**
* Test createKnowledge with null parameter.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -1108,7 +1111,7 @@ class KnowledgeControllerTest {
/**
* Test updateKnowledge with all VO fields being null.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -1129,7 +1132,7 @@ class KnowledgeControllerTest {
/**
* Test enableKnowledge with null parameters.
- *
+ *
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted
*/
@@ -1149,4 +1152,3 @@ class KnowledgeControllerTest {
}
}
}
-
diff --git a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/RepoControllerTest.java b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/RepoControllerTest.java
index 4ac0a28c..7ec37187 100644
--- a/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/RepoControllerTest.java
+++ b/console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/controller/knowledge/RepoControllerTest.java
@@ -267,8 +267,8 @@ class RepoControllerTest {
}
/**
- * Tests repository creation with an empty VO object.
- * Verifies that the controller can handle minimal input.
+ * Tests repository creation with an empty VO object. Verifies that the controller can handle
+ * minimal input.
*/
@Test
@DisplayName("Create repository - with empty VO")
@@ -309,8 +309,8 @@ class RepoControllerTest {
}
/**
- * Tests repository creation with all optional fields populated.
- * Verifies that all optional parameters are correctly processed.
+ * Tests repository creation with all optional fields populated. Verifies that all optional
+ * parameters are correctly processed.
*/
@Test
@DisplayName("Create repository - with all optional fields")
@@ -341,8 +341,8 @@ class RepoControllerTest {
class UpdateRepoTests {
/**
- * Tests successful repository update with valid data.
- * Verifies that changes are properly applied and reflected in the response.
+ * Tests successful repository update with valid data. Verifies that changes are properly applied
+ * and reflected in the response.
*/
@Test
@DisplayName("Update repository - successful flow")
@@ -363,8 +363,8 @@ class RepoControllerTest {
}
/**
- * Tests partial update of repository fields.
- * Verifies that only specified fields are updated while others remain unchanged.
+ * Tests partial update of repository fields. Verifies that only specified fields are updated while
+ * others remain unchanged.
*/
@Test
@DisplayName("Update repository - partial update")
@@ -385,8 +385,8 @@ class RepoControllerTest {
}
/**
- * Tests update attempt with a non-existent repository ID.
- * Verifies that appropriate exception is thrown for invalid IDs.
+ * Tests update attempt with a non-existent repository ID. Verifies that appropriate exception is
+ * thrown for invalid IDs.
*/
@Test
@DisplayName("Update repository - non-existent ID")
@@ -465,8 +465,8 @@ class RepoControllerTest {
}
/**
- * Tests repository status update with different status values.
- * Verifies that all valid status values are properly handled.
+ * Tests repository status update with different status values. Verifies that all valid status
+ * values are properly handled.
*
* @param status the status value to test
*/
@@ -560,8 +560,8 @@ class RepoControllerTest {
}
/**
- * Tests listing repositories with various pagination parameters.
- * Verifies that different page numbers and sizes are correctly handled.
+ * Tests listing repositories with various pagination parameters. Verifies that different page
+ * numbers and sizes are correctly handled.
*
* @param pageNo the page number
* @param pageSize the page size
@@ -593,8 +593,8 @@ class RepoControllerTest {
}
/**
- * Tests listing repositories when no results are found.
- * Verifies that empty result sets are properly handled.
+ * Tests listing repositories when no results are found. Verifies that empty result sets are
+ * properly handled.
*/
@Test
@DisplayName("List repositories - empty result")
@@ -668,8 +668,7 @@ class RepoControllerTest {
}
/**
- * Tests simplified list with ordering parameter.
- * Verifies that orderBy field is properly handled.
+ * Tests simplified list with ordering parameter. Verifies that orderBy field is properly handled.
*/
@Test
@DisplayName("Simplified list - with order by field")
@@ -707,8 +706,8 @@ class RepoControllerTest {
}
/**
- * Tests simplified list with all available parameters.
- * Verifies that all parameters work together correctly.
+ * Tests simplified list with all available parameters. Verifies that all parameters work together
+ * correctly.
*/
@Test
@DisplayName("Simplified list - with all parameters")
@@ -840,8 +839,8 @@ class RepoControllerTest {
}
/**
- * Tests get detail with various repository ID values.
- * Verifies that different valid IDs are correctly processed.
+ * Tests get detail with various repository ID values. Verifies that different valid IDs are
+ * correctly processed.
*
* @param id the repository ID to test
*/
@@ -870,8 +869,8 @@ class RepoControllerTest {
class HitTestTests {
/**
- * Tests hit test functionality with default topN value.
- * Verifies that default parameters work correctly.
+ * Tests hit test functionality with default topN value. Verifies that default parameters work
+ * correctly.
*/
@Test
@DisplayName("Hit test - default topN")
@@ -890,8 +889,8 @@ class RepoControllerTest {
}
/**
- * Tests hit test with different topN parameter values.
- * Verifies that various topN values are correctly handled.
+ * Tests hit test with different topN parameter values. Verifies that various topN values are
+ * correctly handled.
*
* @param topN the number of top results to return
*/
@@ -997,8 +996,8 @@ class RepoControllerTest {
}
/**
- * Tests listing hit test history with various pagination parameters.
- * Verifies that different page numbers and sizes work correctly.
+ * Tests listing hit test history with various pagination parameters. Verifies that different page
+ * numbers and sizes work correctly.
*
* @param pageNo the page number
* @param pageSize the page size
@@ -1029,8 +1028,8 @@ class RepoControllerTest {
}
/**
- * Tests listing hit test history with empty results.
- * Verifies that empty history lists are properly handled.
+ * Tests listing hit test history with empty results. Verifies that empty history lists are properly
+ * handled.
*/
@Test
@DisplayName("History list - empty result")
@@ -1076,8 +1075,7 @@ class RepoControllerTest {
class EnableRepoTests {
/**
- * Tests enabling a repository.
- * Verifies that repositories can be successfully enabled.
+ * Tests enabling a repository. Verifies that repositories can be successfully enabled.
*/
@Test
@DisplayName("Enable repository")
@@ -1095,8 +1093,7 @@ class RepoControllerTest {
}
/**
- * Tests disabling a repository.
- * Verifies that repositories can be successfully disabled.
+ * Tests disabling a repository. Verifies that repositories can be successfully disabled.
*/
@Test
@DisplayName("Disable repository")
@@ -1117,8 +1114,8 @@ class RepoControllerTest {
}
/**
- * Tests enable/disable with different state values.
- * Verifies that both enabled and disabled states work correctly.
+ * Tests enable/disable with different state values. Verifies that both enabled and disabled states
+ * work correctly.
*
* @param enabled the enabled state (0 for disabled, 1 for enabled)
*/
@@ -1138,15 +1135,16 @@ class RepoControllerTest {
}
/**
- * Tests enabling repository with invalid ID.
- * Verifies that appropriate exception is thrown for invalid IDs.
+ * Tests enabling repository with invalid ID. Verifies that appropriate exception is thrown for
+ * invalid IDs.
*/
@Test
@DisplayName("Enable repository - invalid ID")
void enableRepo_InvalidId() {
// Given
doThrow(new IllegalArgumentException("Repository not found"))
- .when(repoService).enableRepo(eq(INVALID_REPO_ID), anyInt());
+ .when(repoService)
+ .enableRepo(eq(INVALID_REPO_ID), anyInt());
// When & Then
assertThatThrownBy(() -> controller.enableRepo(INVALID_REPO_ID, ENABLED))
@@ -1155,15 +1153,16 @@ class RepoControllerTest {
}
/**
- * Tests enable repository when service throws RuntimeException.
- * Verifies that system errors are properly propagated.
+ * Tests enable repository when service throws RuntimeException. Verifies that system errors are
+ * properly propagated.
*/
@Test
@DisplayName("Enable repository - service throws RuntimeException")
void enableRepo_ServiceThrowsRuntimeException() {
// Given
doThrow(new RuntimeException("System error"))
- .when(repoService).enableRepo(anyLong(), anyInt());
+ .when(repoService)
+ .enableRepo(anyLong(), anyInt());
// When & Then
assertThatThrownBy(() -> controller.enableRepo(VALID_REPO_ID, ENABLED))
@@ -1179,8 +1178,8 @@ class RepoControllerTest {
class DeleteRepoTests {
/**
- * Tests deleting repository without tag parameter.
- * Verifies that deletion works with minimal parameters.
+ * Tests deleting repository without tag parameter. Verifies that deletion works with minimal
+ * parameters.
*/
@Test
@DisplayName("Delete repository - without tag")
@@ -1199,8 +1198,8 @@ class RepoControllerTest {
}
/**
- * Tests deleting repository with tag parameter.
- * Verifies that tag-filtered deletion works correctly.
+ * Tests deleting repository with tag parameter. Verifies that tag-filtered deletion works
+ * correctly.
*/
@Test
@DisplayName("Delete repository - with tag")
@@ -1257,8 +1256,8 @@ class RepoControllerTest {
}
/**
- * Tests deleting repository with different tag values.
- * Verifies that various tag values including null and empty are handled correctly.
+ * Tests deleting repository with different tag values. Verifies that various tag values including
+ * null and empty are handled correctly.
*
* @param tag the tag parameter value
*/
@@ -1288,8 +1287,8 @@ class RepoControllerTest {
class SetTopTests {
/**
- * Tests successfully setting a repository as top/pinned.
- * Verifies that repositories can be pinned to the top of lists.
+ * Tests successfully setting a repository as top/pinned. Verifies that repositories can be pinned
+ * to the top of lists.
*/
@Test
@DisplayName("Set top - success")
@@ -1306,8 +1305,7 @@ class RepoControllerTest {
}
/**
- * Tests that setTop returns proper ApiResult.
- * Verifies the response structure and success code.
+ * Tests that setTop returns proper ApiResult. Verifies the response structure and success code.
*/
@Test
@DisplayName("Set top - verify ApiResult")
@@ -1325,8 +1323,8 @@ class RepoControllerTest {
}
/**
- * Tests setTop with different repository IDs.
- * Verifies that various valid IDs are correctly processed.
+ * Tests setTop with different repository IDs. Verifies that various valid IDs are correctly
+ * processed.
*
* @param id the repository ID to pin
*/
@@ -1346,15 +1344,16 @@ class RepoControllerTest {
}
/**
- * Tests setTop with invalid repository ID.
- * Verifies that appropriate exception is thrown for non-existent repositories.
+ * Tests setTop with invalid repository ID. Verifies that appropriate exception is thrown for
+ * non-existent repositories.
*/
@Test
@DisplayName("Set top - invalid ID")
void setTop_InvalidId() {
// Given
doThrow(new IllegalArgumentException("Repository not found"))
- .when(repoService).setTop(eq(INVALID_REPO_ID));
+ .when(repoService)
+ .setTop(eq(INVALID_REPO_ID));
// When & Then
assertThatThrownBy(() -> controller.setTop(INVALID_REPO_ID))
@@ -1362,15 +1361,16 @@ class RepoControllerTest {
}
/**
- * Tests setTop when service throws exception.
- * Verifies that errors during pinning are properly propagated.
+ * Tests setTop when service throws exception. Verifies that errors during pinning are properly
+ * propagated.
*/
@Test
@DisplayName("Set top - service throws exception")
void setTop_ServiceThrowsException() {
// Given
doThrow(new RuntimeException("Set top failed"))
- .when(repoService).setTop(anyLong());
+ .when(repoService)
+ .setTop(anyLong());
// When & Then
assertThatThrownBy(() -> controller.setTop(VALID_REPO_ID))
@@ -1386,8 +1386,8 @@ class RepoControllerTest {
class ListFilesTests {
/**
- * Tests successful file listing for a repository.
- * Verifies that repository files are correctly retrieved.
+ * Tests successful file listing for a repository. Verifies that repository files are correctly
+ * retrieved.
*/
@Test
@DisplayName("List files - success")
@@ -1502,8 +1502,8 @@ class RepoControllerTest {
class GetRepoUseStatusTests {
/**
- * Tests successful retrieval of repository usage status.
- * Verifies that usage statistics are correctly returned.
+ * Tests successful retrieval of repository usage status. Verifies that usage statistics are
+ * correctly returned.
*/
@Test
@DisplayName("Get use status - success")
@@ -1643,8 +1643,7 @@ class RepoControllerTest {
class EdgeCasesAndExceptionsTests {
/**
- * Tests handling of large ID values.
- * Verifies that maximum long values are correctly processed.
+ * Tests handling of large ID values. Verifies that maximum long values are correctly processed.
*/
@Test
@DisplayName("Large ID values test")
@@ -1679,7 +1678,7 @@ class RepoControllerTest {
int threadCount = 10;
List threads = new ArrayList<>();
for (int i = 0; i < threadCount; i++) {
- Thread thread = new Thread(() ->
+ Thread thread = new Thread(() ->
controller.getDetail(VALID_REPO_ID, "", request));
threads.add(thread);
thread.start();
@@ -1695,8 +1694,8 @@ class RepoControllerTest {
}
/**
- * Tests handling of special characters in input.
- * Verifies that special characters are properly escaped and processed.
+ * Tests handling of special characters in input. Verifies that special characters are properly
+ * escaped and processed.
*/
@Test
@DisplayName("Special characters handling test")
@@ -1716,8 +1715,8 @@ class RepoControllerTest {
}
/**
- * Tests handling of very long strings.
- * Verifies that extremely long input strings are properly processed.
+ * Tests handling of very long strings. Verifies that extremely long input strings are properly
+ * processed.
*/
@Test
@DisplayName("Very long string test")
@@ -1776,8 +1775,8 @@ class RepoControllerTest {
}
/**
- * Tests handling of Unicode characters including emojis.
- * Verifies that Unicode strings are properly processed.
+ * Tests handling of Unicode characters including emojis. Verifies that Unicode strings are properly
+ * processed.
*/
@Test
@DisplayName("Unicode characters test")
@@ -1861,8 +1860,8 @@ class RepoControllerTest {
}
/**
- * Tests hit test and history query scenario.
- * Verifies that hit testing and history retrieval work together correctly.
+ * Tests hit test and history query scenario. Verifies that hit testing and history retrieval work
+ * together correctly.
*/
@Test
@DisplayName("Hit test and history query scenario")
@@ -1876,7 +1875,7 @@ class RepoControllerTest {
// When
Object testResult = controller.hitTest(VALID_REPO_ID, "test query", 5);
- ApiResult> historyResult =
+ ApiResult> historyResult =
controller.listHitTestHistoryByPage(VALID_REPO_ID, 1, 10);
// Then
@@ -1959,17 +1958,16 @@ class RepoControllerTest {
}
/**
- * Tests verification that no interactions occurred.
- * Verifies that service methods were not called when controller methods are not invoked.
+ * Tests verification that no interactions occurred. Verifies that service methods were not called
+ * when controller methods are not invoked.
*/
@Test
@DisplayName("Verify no interactions")
void verifyNoInteractionsTest() {
// When - no methods called
-
+
// Then
verifyNoMoreInteractions(repoService);
}
}
}
-
--
Gitee
From db236c4b964b437da03bf4f997d56218d06ce4ff Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Mon, 20 Oct 2025 16:49:01 +0800
Subject: [PATCH 54/90] feat: rpa debug api add
---
.../com/iflytek/astron/console/toolkit/handler/RpaHandler.java | 2 +-
.../astron/console/toolkit/task/scheduler/PollScheduler.java | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
index 19b44c39..18d46f1f 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
@@ -183,7 +183,7 @@ public class RpaHandler {
throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async missing executionId");
}
} catch (BusinessException e) {
- throw new RuntimeException(e);
+ throw e;
} catch (Exception e) {
log.error("execute-async failed", e);
throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async failed");
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
index 09050089..c210a3ae 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
@@ -29,7 +29,6 @@ public class PollScheduler {
long last = lastPollAt.getOrDefault(key, 0L);
if (now - last >= s.getNextPollMs()) {
lastPollAt.put(key, now);
- // 可选:支持不同会话携带不同 Token,这里简化为不覆盖;如需覆盖,可把 token 缓存在 session 上
debugService.pollOnce(s, s.getApiToken());
}
}
--
Gitee
From 271d7db202630cfa8ecac52e1c925992622425d7 Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Wed, 22 Oct 2025 16:08:54 +0800
Subject: [PATCH 55/90] feat: rpa debug api fixed
---
.../toolkit/config/properties/ApiUrl.java | 1 +
.../controller/tool/RpaController.java | 71 +-------
.../entity/dto/rpa/ExecuteAsyncResponse.java | 26 ---
.../dto/rpa/ExecutionStatusResponse.java | 167 ------------------
.../toolkit/entity/dto/rpa/StartReq.java | 1 +
.../console/toolkit/handler/RpaHandler.java | 64 -------
.../service/tool/RpaAssistantService.java | 81 +++++++++
.../toolkit/service/tool/RpaDebugService.java | 141 ---------------
.../toolkit/task/scheduler/PollScheduler.java | 40 -----
.../console/toolkit/util/OkHttpUtil.java | 23 ++-
.../main/resources/application-toolkit.yml | 1 +
11 files changed, 111 insertions(+), 505 deletions(-)
delete mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
delete mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
delete mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
delete mode 100644 console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/config/properties/ApiUrl.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/config/properties/ApiUrl.java
index 8db381b5..7c02b366 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/config/properties/ApiUrl.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/config/properties/ApiUrl.java
@@ -18,6 +18,7 @@ public class ApiUrl {
String knowledgeUrl;
String streamChatUrl;
String toolUrl;
+ String toolRpaUrl;
String appUrl;
String apiKey;
String apiSecret;
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
index 6c2451cd..53ea60eb 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
@@ -46,9 +46,6 @@ public class RpaController {
@Autowired
private RpaAssistantService rpaAssistantService;
- @Autowired
- private RpaDebugService rpaDebugService;
-
/**
* Get all available RPA platforms/sources.
*
@@ -133,70 +130,12 @@ public class RpaController {
rpaAssistantService.delete(userId, id);
}
- @PostMapping("/debug/start")
- public Map start(@RequestBody StartReq req,
- @RequestHeader(value = "X-RPA-Token") String tokenOverride) {
- DebugSession s = rpaDebugService.start(req.projectId, req.version, req.execPosition, req.params, tokenOverride);
- return Map.of(
- "debugId", s.getDebugId(),
- "executionId", s.getExecutionId(),
- "status", s.getStatus().name(),
- "message", s.getMessage(),
- "updatedAt", s.getUpdatedAt().toString());
- }
-
- /**
- * SSE 事件流 事件名:status(过程更新)、final(终态)
- */
- @GetMapping(value = "/debug/{debugId}/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public SseEmitter stream(@PathVariable String debugId) {
- DebugSession s = rpaDebugService.get(debugId);
- // 不超时,由服务器控制完成
- SseEmitter emitter = new SseEmitter(0L);
- if (s == null) {
- try {
- emitter.send(SseEmitter.event().name("final").data(Map.of("status", "FAILED", "message", "debug not found")));
- } catch (IOException ignored) {
- }
- emitter.complete();
- return emitter;
- }
-
- // 立即推一次当前状态
- try {
- emitter.send(SseEmitter.event().name("status").data(rpaDebugService.toView(s)));
- } catch (IOException ignored) {
- }
-
- rpaDebugService.onUpdate(debugId, updated -> {
- String eventName = switch (updated.getStatus()) {
- case SUCCEEDED, FAILED, CANCELED, TIMEOUT -> "final";
- default -> "status";
- };
- try {
- emitter.send(SseEmitter.event().name(eventName).data(rpaDebugService.toView(updated)));
- if ("final".equals(eventName)) {
- emitter.complete();
- rpaDebugService.offAll(debugId);
- }
- } catch (IOException e) {
- emitter.completeWithError(e);
- rpaDebugService.offAll(debugId);
- }
- });
-
- return emitter;
- }
-
/**
- * 轮询兜底接口(前端断线或不支持 SSE 时使用)
+ * 调试RPA机器人
*/
- @GetMapping("/debug/{debugId}/status")
- public Object status(@PathVariable String debugId) {
- DebugSession s = rpaDebugService.get(debugId);
- if (s == null) {
- return Map.of("code", "B0404", "message", "debug not found");
- }
- return rpaDebugService.toView(s);
+ @PostMapping(value = "/debug", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ public SseEmitter stream(@RequestBody StartReq req,
+ @RequestHeader(value = "X-RPA-Token") String apiToken) {
+ return rpaAssistantService.debug(req, apiToken);
}
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
deleted file mode 100644
index 9e7872ca..00000000
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecuteAsyncResponse.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.iflytek.astron.console.toolkit.entity.dto.rpa;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
-
-@Data
-public class ExecuteAsyncResponse {
- private String code;
- private String msg;
- private Data data;
-
-
- public static class Data {
- @JsonProperty("executionId")
- private String executionId;
-
- public String getExecutionId() {
- return executionId;
- }
-
- public void setExecutionId(String executionId) {
- this.executionId = executionId;
- }
- }
-}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
deleted file mode 100644
index c31264b4..00000000
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/ExecutionStatusResponse.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package com.iflytek.astron.console.toolkit.entity.dto.rpa;
-
-import com.alibaba.fastjson2.JSONObject;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Data;
-
-import java.util.Map;
-
-@Data
-public class ExecutionStatusResponse {
- private String code;
- private String msg;
- private Data data;
-
- public static class Data {
- private Execution execution;
-
- public Execution getExecution() {
- return execution;
- }
-
- public void setExecution(Execution execution) {
- this.execution = execution;
- }
- }
-
- public static class Execution {
- private String id;
-
- @JsonProperty("project_id")
- private String projectId;
-
- // PENDING / COMPLETED / FAILED
- private String status;
-
- private JSONObject parameters;
-
- private Map result;
- private Object error;
-
- @JsonProperty("exec_position")
- private String execPosition;
-
- private Integer version;
-
- @JsonProperty("user_id")
- private String userId;
-
- @JsonProperty("start_time")
- private String startTime;
-
- @JsonProperty("end_time")
- private String endTime;
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getProjectId() {
- return projectId;
- }
-
- public void setProjectId(String projectId) {
- this.projectId = projectId;
- }
-
- public String getStatus() {
- return status;
- }
-
- public void setStatus(String status) {
- this.status = status;
- }
-
- public JSONObject getParameters() {
- return parameters;
- }
-
- public void setParameters(JSONObject parameters) {
- this.parameters = parameters;
- }
-
- public Map getResult() {
- return result;
- }
-
- public void setResult(Map result) {
- this.result = result;
- }
-
- public Object getError() {
- return error;
- }
-
- public void setError(Object error) {
- this.error = error;
- }
-
- public String getExecPosition() {
- return execPosition;
- }
-
- public void setExecPosition(String execPosition) {
- this.execPosition = execPosition;
- }
-
- public Integer getVersion() {
- return version;
- }
-
- public void setVersion(Integer version) {
- this.version = version;
- }
-
- public String getUserId() {
- return userId;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public String getStartTime() {
- return startTime;
- }
-
- public void setStartTime(String startTime) {
- this.startTime = startTime;
- }
-
- public String getEndTime() {
- return endTime;
- }
-
- public void setEndTime(String endTime) {
- this.endTime = endTime;
- }
- }
-
- public String getCode() {
- return code;
- }
-
- public void setCode(String code) {
- this.code = code;
- }
-
- public String getMsg() {
- return msg;
- }
-
- public void setMsg(String msg) {
- this.msg = msg;
- }
-
- public Data getData() {
- return data;
- }
-
- public void setData(Data data) {
- this.data = data;
- }
-}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
index 4fb93132..068642c3 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
@@ -1,5 +1,6 @@
package com.iflytek.astron.console.toolkit.entity.dto.rpa;
+import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
index 18d46f1f..56418970 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/handler/RpaHandler.java
@@ -7,12 +7,10 @@ import java.util.*;
import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.exception.BusinessException;
import com.iflytek.astron.console.toolkit.config.properties.ApiUrl;
-import com.iflytek.astron.console.toolkit.entity.dto.rpa.ExecutionStatusResponse;
import com.iflytek.astron.console.toolkit.entity.enumVo.VarType;
import com.iflytek.astron.console.toolkit.util.OkHttpUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
@@ -152,66 +150,4 @@ public class RpaHandler {
String cut = new String(bytes, 0, max, StandardCharsets.UTF_8);
return cut + "...(" + bytes.length + "B)";
}
-
- public String executeAsync(String projectId, String execPosition, Map params, Integer version, String bearerToken) {
- Map body = new HashMap<>();
- body.put("project_id", projectId);
- body.put("exec_position", execPosition == null ? "EXECUTOR" : execPosition);
- body.put("params", params == null ? Map.of() : params);
- if (version != null)
- body.put("version", version);
-
- Map headerMap = new HashMap<>();
- headerMap.put("Authorization", "Bearer " + bearerToken);
- String url = apiUrl.getRpaUrl() + "/api/rpa-openapi/workflows/execute-async";
- JSONObject data = null;
- try {
- log.info("executeAsync rpa uri: {}, body: {},token: {}", url, body, headerMap);
- String post = OkHttpUtil.post(url, headerMap, JSON.toJSONString(body));
-
- if (StringUtils.isBlank(post)) {
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "Empty response from execute-async");
- }
- log.info("executeAsync rpa response {}", post);
- JSONObject resp = JSONObject.parseObject(post);
- String code = resp.getString("code");
- if (!"0000".equals(code)) {
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async failed: " + code + " " + resp.getString("msg"));
- }
- data = resp.getJSONObject("data");
- if (data == null || data.getString("executionId") == null) {
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async missing executionId");
- }
- } catch (BusinessException e) {
- throw e;
- } catch (Exception e) {
- log.error("execute-async failed", e);
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "execute-async failed");
- }
- return data.getString("executionId");
- }
-
- public ExecutionStatusResponse.Execution getExecution(String executionId, String bearerToken) {
- String url = apiUrl.getRpaUrl() + "/api/rpa-openapi/executions/" + executionId;
- Map headerMap = new HashMap<>();
- headerMap.put("Authorization", "Bearer " + bearerToken);
- log.info("getExecution rpa uri: {}, executionId: {},token: {}", url, executionId, headerMap);
-
- String responseStr = OkHttpUtil.get(url, headerMap);
- log.info("getExecution response: {}", responseStr);
-
- if (StringUtils.isBlank(responseStr))
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "Empty response from executions");
- JSONObject resp = JSONObject.parseObject(responseStr);
- String code = resp.getString("code");
- if (!"0000".equals(code)) {
- throw new RuntimeException("getExecution failed: " + code + " " + resp.getString("msg"));
- }
- JSONObject data = resp.getJSONObject("data");
- if (data == null || data.getJSONObject("execution") == null) {
- throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "getExecution missing execution payload");
- }
- ExecutionStatusResponse.Execution execution = JSON.parseObject(String.valueOf(data.getJSONObject("execution")), ExecutionStatusResponse.Execution.class);
- return execution;
- }
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
index e51bfb97..070e1702 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
@@ -10,9 +10,13 @@ import com.iflytek.astron.console.commons.constant.ResponseEnum;
import com.iflytek.astron.console.commons.entity.user.UserInfo;
import com.iflytek.astron.console.commons.entity.workflow.Workflow;
import com.iflytek.astron.console.commons.exception.BusinessException;
+import com.iflytek.astron.console.commons.util.SseEmitterUtil;
import com.iflytek.astron.console.commons.util.space.SpaceInfoUtil;
+import com.iflytek.astron.console.toolkit.config.properties.ApiUrl;
import com.iflytek.astron.console.toolkit.entity.biz.workflow.BizWorkflowData;
import com.iflytek.astron.console.toolkit.entity.biz.workflow.BizWorkflowNode;
+import com.iflytek.astron.console.toolkit.entity.core.workflow.sse.ChatResponse;
+import com.iflytek.astron.console.toolkit.entity.dto.rpa.StartReq;
import com.iflytek.astron.console.toolkit.entity.table.ConfigInfo;
import com.iflytek.astron.console.toolkit.entity.table.tool.*;
import com.iflytek.astron.console.toolkit.entity.tool.*;
@@ -21,11 +25,18 @@ import com.iflytek.astron.console.toolkit.handler.UserInfoManagerHandler;
import com.iflytek.astron.console.toolkit.mapper.ConfigInfoMapper;
import com.iflytek.astron.console.toolkit.mapper.tool.*;
import com.iflytek.astron.console.toolkit.service.workflow.WorkflowService;
+import com.iflytek.astron.console.toolkit.sse.WorkflowSseEventSourceListener;
+import com.iflytek.astron.console.toolkit.util.JacksonUtil;
+import com.iflytek.astron.console.toolkit.util.OkHttpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import okhttp3.sse.EventSource;
+import okhttp3.sse.EventSourceListener;
import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.time.LocalDateTime;
import java.util.*;
@@ -49,6 +60,7 @@ public class RpaAssistantService {
private final WorkflowService workflowService;
private final ObjectMapper objectMapper = new ObjectMapper();
private final ConfigInfoMapper configInfoMapper;
+ private final ApiUrl apiUrl;
/**
* Create an RPA assistant with plaintext credentials.
@@ -496,4 +508,73 @@ public class RpaAssistantService {
.like(StringUtils.isNoneBlank(name), RpaUserAssistant::getAssistantName, name)
.orderByDesc(RpaUserAssistant::getUpdateTime));
}
+
+ public SseEmitter debug(StartReq startReq, String apiToken) {
+ try {
+ String url = apiUrl.getToolRpaUrl() + "/rpa/v1/exec";
+ Map headerMap = new HashMap<>();
+ headerMap.put(HttpHeaders.AUTHORIZATION, apiToken);
+
+ String sseId = UserInfoManagerHandler.getUserId();
+ if (StringUtils.isBlank(startReq.getProjectId())) {
+ return SseEmitterUtil.newSseAndSendMessageClose("project_id is required");
+ }
+
+ SseEmitter emitter = SseEmitterUtil.create(sseId, 1_800_000L);
+ Map body = new HashMap<>();
+ body.put("project_id", startReq.getProjectId());
+ body.put("exec_position",
+ (startReq.getExecPosition()==null||startReq.getExecPosition().isBlank()) ? "EXECUTOR" : startReq.getExecPosition());
+ body.put("params", startReq.getParams()==null ? Map.of() : startReq.getParams());
+ String reqBody = JacksonUtil.toJSONString(body, JacksonUtil.NON_NULL_OBJECT_MAPPER);
+ log.info("[SSE] rpa debug url={}, headers={}, reqBody={}", url, headerMap, reqBody);
+
+ EventSourceListener listener = new EventSourceListener() {
+ @Override public void onOpen(EventSource es, okhttp3.Response response) {
+ log.info("[SSE][{}] open, code={}", sseId, response.code());
+ SseEmitterUtil.EVENTSOURCE_MAP.put(sseId, es);
+ try { emitter.send(SseEmitter.event().name("open").data("ok")); } catch (Exception ignore) {}
+ }
+
+ @Override public void onEvent(EventSource es, String id, String type, String data) {
+ try {
+ String event = (type == null || type.isBlank()) ? "data" : type;
+ if ("data".equals(event)) {
+ emitter.send(SseEmitter.event().name("data").data(data));
+ } else if ("ping".equals(event)) {
+ emitter.send(SseEmitter.event().name("ping").data(data));
+ } else if ("finish".equals(event)) {
+ emitter.send(SseEmitter.event().name("finish").data(data));
+ SseEmitterUtil.sendEndAndComplete(emitter);
+ } else {
+ emitter.send(SseEmitter.event().name("data").data(data));
+ }
+ } catch (Exception e) {
+ log.warn("[SSE][{}] forward event failed: {}", sseId, e.getMessage(), e);
+ }
+ }
+
+ @Override public void onClosed(EventSource es) {
+ log.info("[SSE][{}] downstream closed", sseId);
+ SseEmitterUtil.sendEndAndComplete(emitter);
+ SseEmitterUtil.EVENTSOURCE_MAP.remove(sseId);
+ }
+
+ @Override public void onFailure(EventSource es, Throwable t, okhttp3.Response resp) {
+ String msg = (t != null) ? t.getMessage() : (resp != null ? ("http " + resp.code()) : "unknown");
+ log.error("[SSE][{}] downstream failure: {}", sseId, msg, t);
+ SseEmitterUtil.completeWithError(emitter, msg);
+ if (es != null) es.cancel();
+ SseEmitterUtil.EVENTSOURCE_MAP.remove(sseId);
+ }
+ };
+
+ EventSource es = OkHttpUtil.connectRealEventSourceReturn(url, headerMap, reqBody, listener);
+ SseEmitterUtil.EVENTSOURCE_MAP.put(sseId, es);
+ return emitter;
+ } catch (Exception e) {
+ log.error("SSE debug error: {}", e.getMessage(), e);
+ return SseEmitterUtil.newSseAndSendMessageClose(e.getMessage());
+ }
+ }
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
deleted file mode 100644
index bdadbc41..00000000
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaDebugService.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.iflytek.astron.console.toolkit.service.tool;
-
-import com.iflytek.astron.console.toolkit.entity.dto.rpa.ExecutionStatusResponse;
-import com.iflytek.astron.console.toolkit.entity.enumVo.DebugStatus;
-import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
-import com.iflytek.astron.console.toolkit.handler.RpaHandler;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-
-/**
- * @Author clliu19
- * @Date: 2025/10/17 14:04
- */
-@Service
-@RequiredArgsConstructor
-@Slf4j
-public class RpaDebugService {
- private final RpaHandler rpaHandler;
- private final Map sessions = new ConcurrentHashMap<>();
- private final Map>> listeners = new ConcurrentHashMap<>();
-
- public DebugSession start(String projectId, Integer version, String execPosition, Map params, String tokenOverride) {
- DebugSession s = new DebugSession(projectId, version, execPosition, params, tokenOverride,
- 3000, 1000);
- sessions.put(s.getDebugId(), s);
-
- try {
- String executionId = rpaHandler.executeAsync(projectId, execPosition, params, version, tokenOverride);
- s.setExecutionId(executionId);
- s.setStatus(DebugStatus.SUBMITTED);
- s.setMessage("submitted");
- emit(s);
- } catch (Exception e) {
- s.setStatus(DebugStatus.FAILED);
- s.setMessage("execute-async failed: " + e.getMessage());
- emit(s);
- }
- return s;
- }
-
- public DebugSession get(String debugId) {
- return sessions.get(debugId);
- }
-
- public Collection all() {
- return sessions.values();
- }
-
- public void onUpdate(String debugId, Consumer cb) {
- listeners.computeIfAbsent(debugId, k -> Collections.synchronizedList(new ArrayList<>())).add(cb);
- }
-
- public void offAll(String debugId) {
- listeners.remove(debugId);
- }
-
- private void emit(DebugSession s) {
- List> ls = listeners.get(s.getDebugId());
- if (ls != null) {
- for (Consumer cb : ls)
- cb.accept(s);
- }
- }
-
- /**
- * 被 PollScheduler 调用:检查状态并更新
- */
- public void pollOnce(DebugSession s, String tokenOverride) {
- if (s == null)
- return;
- if (isFinal(s.getStatus()))
- return;
-
- // 生命周期到期
- if (s.isExpired()) {
- s.setStatus(DebugStatus.TIMEOUT);
- s.setMessage("Timeout after " + 3000 + "s");
- emit(s);
- return;
- }
-
- // 还没拿到 executionId,跳过
- if (s.getExecutionId() == null)
- return;
-
- try {
- ExecutionStatusResponse.Execution ex = rpaHandler.getExecution(s.getExecutionId(), tokenOverride);
- String rpaStatus = ex.getStatus();
- // RPA 状态映射
- if ("PENDING".equalsIgnoreCase(rpaStatus)) {
- s.setStatus(DebugStatus.RUNNING);
- s.setMessage("RPA PENDING");
- // 运行中逐步增大间隔(上限)
- long next = Math.min((long) (s.getNextPollMs() * 1.4), 15000);
- s.setNextPollMs(next);
- } else if ("COMPLETED".equalsIgnoreCase(rpaStatus)) {
- s.setStatus(DebugStatus.SUCCEEDED);
- s.setMessage("RPA COMPLETED");
- } else if ("FAILED".equalsIgnoreCase(rpaStatus)) {
- s.setStatus(DebugStatus.FAILED);
- String msg = (ex.getError() != null)
- ? String.valueOf(ex.getError())
- : "RPA FAILED";
- s.setMessage(msg);
- } else {
- s.setStatus(DebugStatus.RETRYING);
- s.setMessage("Unknown status: " + rpaStatus);
- }
- emit(s);
- } catch (Exception e) {
- log.error("poll once error e = ", e);
- s.setStatus(DebugStatus.RETRYING);
- s.setMessage("query failed: " + e.getMessage());
- int r = s.incRetries();
- long backoff = Math.min((long) (1000 * Math.pow(1.8, r)), 15000);
- s.setNextPollMs(backoff);
- emit(s);
- }
- }
-
- private boolean isFinal(DebugStatus st) {
- return st == DebugStatus.SUCCEEDED || st == DebugStatus.FAILED || st == DebugStatus.CANCELED || st == DebugStatus.TIMEOUT;
- }
-
- // 用于 SSE 输出的轻量 Map
- public Map toView(DebugSession s) {
- Map m = new LinkedHashMap<>();
- m.put("debugId", s.getDebugId());
- m.put("executionId", s.getExecutionId());
- m.put("status", s.getStatus().name());
- m.put("message", s.getMessage());
- m.put("progress", s.getProgress());
- m.put("updatedAt", s.getUpdatedAt().toString());
- return m;
- }
-}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
deleted file mode 100644
index c210a3ae..00000000
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/task/scheduler/PollScheduler.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.iflytek.astron.console.toolkit.task.scheduler;
-
-import com.iflytek.astron.console.toolkit.entity.enumVo.DebugStatus;
-import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
-import com.iflytek.astron.console.toolkit.service.tool.RpaDebugService;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Component
-public class PollScheduler {
-
- private final RpaDebugService debugService;
- private final Map lastPollAt = new ConcurrentHashMap<>();
-
- public PollScheduler(RpaDebugService debugService) {
- this.debugService = debugService;
- }
-
- @Scheduled(fixedDelay = 500)
- public void tick() {
- long now = System.currentTimeMillis();
- for (DebugSession s : debugService.all()) {
- if (isFinal(s.getStatus()))
- continue;
- String key = s.getDebugId();
- long last = lastPollAt.getOrDefault(key, 0L);
- if (now - last >= s.getNextPollMs()) {
- lastPollAt.put(key, now);
- debugService.pollOnce(s, s.getApiToken());
- }
- }
- }
-
- private boolean isFinal(DebugStatus st) {
- return st == DebugStatus.SUCCEEDED || st == DebugStatus.FAILED || st == DebugStatus.CANCELED || st == DebugStatus.TIMEOUT;
- }
-}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
index f48e4a26..83538f82 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
@@ -5,7 +5,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Cookie;
import okhttp3.*;
import okhttp3.internal.sse.RealEventSource;
-import okhttp3.sse.EventSourceListener;
+import okhttp3.sse.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
@@ -877,6 +877,27 @@ public class OkHttpUtil {
RealEventSource realEventSource = new RealEventSource(request, listener);
realEventSource.connect(HTTP_CLIENT); // The actual start of the request
}
+ public static EventSource connectRealEventSourceReturn(
+ String url,
+ Map headers,
+ String jsonBody,
+ EventSourceListener listener) {
+
+ RequestBody body = RequestBody.create(jsonBody == null ? "{}" : jsonBody, MediaType.get("application/json; charset=utf-8"));
+ Request.Builder rb = new Request.Builder()
+ .url(url)
+ .addHeader("Accept", "text/event-stream")
+ .addHeader("Content-Type", "application/json");
+
+ if (headers != null) headers.forEach((k, v) -> {
+ if (v != null) rb.addHeader(k, v);
+ });
+
+ Request req = rb.post(body).build();
+ EventSource.Factory factory = EventSources.createFactory(HTTP_CLIENT);
+ return factory.newEventSource(req, listener);
+ }
+
/**
* Establish an SSE (Server-Sent Events) connection using a prepared {@link RequestBody}.
diff --git a/console/backend/toolkit/src/main/resources/application-toolkit.yml b/console/backend/toolkit/src/main/resources/application-toolkit.yml
index abb396c2..0019ac3f 100644
--- a/console/backend/toolkit/src/main/resources/application-toolkit.yml
+++ b/console/backend/toolkit/src/main/resources/application-toolkit.yml
@@ -23,6 +23,7 @@ api:
tenantKey: ${TENANT_KEY:tenantKey}
tenantSecret: ${TENANT_SECRET:tenantSecret}
toolUrl: ${TOOL_URL:http://127.0.0.1:18888}
+ toolRpaUrl: ${TOOL_RPA_URL:http://127.0.0.1:17198}
workflow: ${WORKFLOW_URL:http://127.0.0.1:7880}
sparkDB: ${SPARK_DB_URL:http://127.0.0.1:7990}
sparkDocUrl: https://chatdoc.xfyun.cn
--
Gitee
From e0e610a02c63433a8eff794eb385266233195263 Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Wed, 22 Oct 2025 16:10:42 +0800
Subject: [PATCH 56/90] feat: rpa debug api fixed
---
.../controller/tool/RpaController.java | 7 ++---
.../toolkit/entity/dto/rpa/StartReq.java | 1 -
.../service/tool/RpaAssistantService.java | 28 +++++++++++--------
.../console/toolkit/util/OkHttpUtil.java | 9 ++++--
4 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
index 53ea60eb..57484f64 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/controller/tool/RpaController.java
@@ -8,7 +8,6 @@ import com.iflytek.astron.console.toolkit.entity.table.tool.RpaUserAssistant;
import com.iflytek.astron.console.toolkit.entity.tool.CreateRpaAssistantReq;
import com.iflytek.astron.console.toolkit.entity.tool.RpaAssistantResp;
import com.iflytek.astron.console.toolkit.entity.tool.UpdateRpaAssistantReq;
-import com.iflytek.astron.console.toolkit.entity.vo.rpa.DebugSession;
import com.iflytek.astron.console.toolkit.handler.UserInfoManagerHandler;
import com.iflytek.astron.console.toolkit.service.tool.*;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -19,9 +18,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
-import java.io.IOException;
import java.util.List;
-import java.util.Map;
/**
* REST controller for RPA resources.
@@ -135,7 +132,7 @@ public class RpaController {
*/
@PostMapping(value = "/debug", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter stream(@RequestBody StartReq req,
- @RequestHeader(value = "X-RPA-Token") String apiToken) {
- return rpaAssistantService.debug(req, apiToken);
+ @RequestHeader(value = "X-RPA-Token") String apiToken) {
+ return rpaAssistantService.debug(req, apiToken);
}
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
index 068642c3..4fb93132 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
@@ -1,6 +1,5 @@
package com.iflytek.astron.console.toolkit.entity.dto.rpa;
-import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
index 070e1702..0ab80c61 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
@@ -15,7 +15,6 @@ import com.iflytek.astron.console.commons.util.space.SpaceInfoUtil;
import com.iflytek.astron.console.toolkit.config.properties.ApiUrl;
import com.iflytek.astron.console.toolkit.entity.biz.workflow.BizWorkflowData;
import com.iflytek.astron.console.toolkit.entity.biz.workflow.BizWorkflowNode;
-import com.iflytek.astron.console.toolkit.entity.core.workflow.sse.ChatResponse;
import com.iflytek.astron.console.toolkit.entity.dto.rpa.StartReq;
import com.iflytek.astron.console.toolkit.entity.table.ConfigInfo;
import com.iflytek.astron.console.toolkit.entity.table.tool.*;
@@ -25,7 +24,6 @@ import com.iflytek.astron.console.toolkit.handler.UserInfoManagerHandler;
import com.iflytek.astron.console.toolkit.mapper.ConfigInfoMapper;
import com.iflytek.astron.console.toolkit.mapper.tool.*;
import com.iflytek.astron.console.toolkit.service.workflow.WorkflowService;
-import com.iflytek.astron.console.toolkit.sse.WorkflowSseEventSourceListener;
import com.iflytek.astron.console.toolkit.util.JacksonUtil;
import com.iflytek.astron.console.toolkit.util.OkHttpUtil;
import lombok.RequiredArgsConstructor;
@@ -522,21 +520,26 @@ public class RpaAssistantService {
SseEmitter emitter = SseEmitterUtil.create(sseId, 1_800_000L);
Map body = new HashMap<>();
- body.put("project_id", startReq.getProjectId());
+ body.put("project_id", startReq.getProjectId());
body.put("exec_position",
- (startReq.getExecPosition()==null||startReq.getExecPosition().isBlank()) ? "EXECUTOR" : startReq.getExecPosition());
- body.put("params", startReq.getParams()==null ? Map.of() : startReq.getParams());
+ (startReq.getExecPosition() == null || startReq.getExecPosition().isBlank()) ? "EXECUTOR" : startReq.getExecPosition());
+ body.put("params", startReq.getParams() == null ? Map.of() : startReq.getParams());
String reqBody = JacksonUtil.toJSONString(body, JacksonUtil.NON_NULL_OBJECT_MAPPER);
log.info("[SSE] rpa debug url={}, headers={}, reqBody={}", url, headerMap, reqBody);
EventSourceListener listener = new EventSourceListener() {
- @Override public void onOpen(EventSource es, okhttp3.Response response) {
+ @Override
+ public void onOpen(EventSource es, okhttp3.Response response) {
log.info("[SSE][{}] open, code={}", sseId, response.code());
SseEmitterUtil.EVENTSOURCE_MAP.put(sseId, es);
- try { emitter.send(SseEmitter.event().name("open").data("ok")); } catch (Exception ignore) {}
+ try {
+ emitter.send(SseEmitter.event().name("open").data("ok"));
+ } catch (Exception ignore) {
+ }
}
- @Override public void onEvent(EventSource es, String id, String type, String data) {
+ @Override
+ public void onEvent(EventSource es, String id, String type, String data) {
try {
String event = (type == null || type.isBlank()) ? "data" : type;
if ("data".equals(event)) {
@@ -554,17 +557,20 @@ public class RpaAssistantService {
}
}
- @Override public void onClosed(EventSource es) {
+ @Override
+ public void onClosed(EventSource es) {
log.info("[SSE][{}] downstream closed", sseId);
SseEmitterUtil.sendEndAndComplete(emitter);
SseEmitterUtil.EVENTSOURCE_MAP.remove(sseId);
}
- @Override public void onFailure(EventSource es, Throwable t, okhttp3.Response resp) {
+ @Override
+ public void onFailure(EventSource es, Throwable t, okhttp3.Response resp) {
String msg = (t != null) ? t.getMessage() : (resp != null ? ("http " + resp.code()) : "unknown");
log.error("[SSE][{}] downstream failure: {}", sseId, msg, t);
SseEmitterUtil.completeWithError(emitter, msg);
- if (es != null) es.cancel();
+ if (es != null)
+ es.cancel();
SseEmitterUtil.EVENTSOURCE_MAP.remove(sseId);
}
};
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
index 83538f82..12e347dc 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/OkHttpUtil.java
@@ -877,6 +877,7 @@ public class OkHttpUtil {
RealEventSource realEventSource = new RealEventSource(request, listener);
realEventSource.connect(HTTP_CLIENT); // The actual start of the request
}
+
public static EventSource connectRealEventSourceReturn(
String url,
Map headers,
@@ -889,9 +890,11 @@ public class OkHttpUtil {
.addHeader("Accept", "text/event-stream")
.addHeader("Content-Type", "application/json");
- if (headers != null) headers.forEach((k, v) -> {
- if (v != null) rb.addHeader(k, v);
- });
+ if (headers != null)
+ headers.forEach((k, v) -> {
+ if (v != null)
+ rb.addHeader(k, v);
+ });
Request req = rb.post(body).build();
EventSource.Factory factory = EventSources.createFactory(HTTP_CLIENT);
--
Gitee
From 96d1c384ef461e1928a7c03ed8591a8b857a1d21 Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 17:07:47 +0800
Subject: [PATCH 57/90] docs:Optimize deployment
---
.../casdoor/conf/init_data.json.template | 33 ++++++++++++++++++
docker/astronAgent/casdoor/entrypoint.sh | 34 +++++++------------
.../astronAgent/docker-compose-with-auth.yml | 5 ++-
3 files changed, 48 insertions(+), 24 deletions(-)
create mode 100644 docker/astronAgent/casdoor/conf/init_data.json.template
diff --git a/docker/astronAgent/casdoor/conf/init_data.json.template b/docker/astronAgent/casdoor/conf/init_data.json.template
new file mode 100644
index 00000000..06c8bd74
--- /dev/null
+++ b/docker/astronAgent/casdoor/conf/init_data.json.template
@@ -0,0 +1,33 @@
+{
+ "applications": [
+ {
+ "owner": "admin",
+ "name": "astron-agent-app",
+ "displayName": "Astron Agent Application",
+ "logo": "https://raw.githubusercontent.com/iflytek/astron-agent/bf285fd637e0920f38fbfd293f22e950b7534484/docs/logo.svg",
+ "organization": "built-in",
+ "cert": "cert-built-in",
+ "enablePassword": true,
+ "enableSignUp": true,
+ "clientId": "astron-agent-client",
+ "redirectUris": [
+ "${CONSOLE_DOMAIN}/callback"
+ ],
+ "tokenFormat": "JWT",
+ "tokenFields": [],
+ "expireInHours": 168,
+ "refreshExpireInHours": 168,
+ "grantTypes": ["authorization_code","refresh_token"],
+ "signinMethods": [
+ {"name": "Password", "displayName": "Password", "rule": "All"}
+ ],
+ "signupItems": [
+ {"name": "Username", "visible": true, "required": true, "prompted": false, "rule": "None"},
+ {"name": "Password", "visible": true, "required": true, "prompted": false, "rule": "None"},
+ {"name": "Email", "visible": true, "required": true, "prompted": false, "rule": "None"}
+ ],
+ "tags": [],
+ "formOffset": 2
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docker/astronAgent/casdoor/entrypoint.sh b/docker/astronAgent/casdoor/entrypoint.sh
index aea43b7b..137a1c74 100644
--- a/docker/astronAgent/casdoor/entrypoint.sh
+++ b/docker/astronAgent/casdoor/entrypoint.sh
@@ -1,29 +1,21 @@
#!/bin/sh
-
-# Casdoor configuration script
-# This script dynamically replaces redirectUris in init_data.json
-
set -e
-CONFIG_FILE="/conf/init_data.json"
+echo "===== Initializing Casdoor Configuration ====="
+echo "CONSOLE_DOMAIN: ${CONSOLE_DOMAIN:-http://localhost}"
-# Check if CONSOLE_DOMAIN is set
-if [ -z "$CONSOLE_DOMAIN" ]; then
- echo "Warning: CONSOLE_DOMAIN environment variable is not set. Using default value."
- CONSOLE_DOMAIN="http://localhost"
+# Install envsubst if not available
+if ! command -v envsubst >/dev/null 2>&1; then
+ echo "Installing gettext-base for envsubst..."
+ apk add --no-cache gettext
fi
-# Construct the redirect URI
-REDIRECT_URI="${CONSOLE_DOMAIN}/callback"
-
-echo "===== Casdoor Configuration ====="
-echo "CONSOLE_DOMAIN: $CONSOLE_DOMAIN"
-echo "REDIRECT_URI: $REDIRECT_URI"
-echo "================================="
+# Generate config from template using envsubst
+echo "Generating init_data.json from template..."
+envsubst < /conf/init_data.json.template > /conf/init_data.json
-# Use sed to replace the redirectUris value
-echo "Updating redirectUris in init_data.json..."
-sed -i "s|\"redirectUris\": \[[^]]*\]|\"redirectUris\": [\"${REDIRECT_URI}\"]|g" "$CONFIG_FILE"
+echo "Configuration updated: redirectUris set to [${CONSOLE_DOMAIN}/callback]"
+echo "=========================================="
-echo "Configuration updated successfully!"
-echo "RedirectUris set to: [\"${REDIRECT_URI}\"]"
\ No newline at end of file
+# Start Casdoor
+exec /server --createDatabase=true
\ No newline at end of file
diff --git a/docker/astronAgent/docker-compose-with-auth.yml b/docker/astronAgent/docker-compose-with-auth.yml
index c7da5c78..11ccc65b 100644
--- a/docker/astronAgent/docker-compose-with-auth.yml
+++ b/docker/astronAgent/docker-compose-with-auth.yml
@@ -10,14 +10,13 @@ services:
- CONSOLE_DOMAIN=${CONSOLE_DOMAIN:-http://localhost}
volumes:
- ./casdoor/conf:/conf
- - ./casdoor/entrypoint.sh:/custom-entrypoint.sh:ro
+ - ./casdoor/entrypoint.sh:/entrypoint.sh:ro
- casdoor-logs:/logs
networks:
- astron-agent-network
depends_on:
- casdoor-mysql
- entrypoint: ["/bin/sh", "-c"]
- command: ["sh /custom-entrypoint.sh && /server --createDatabase=true"]
+ entrypoint: ["/bin/sh", "/entrypoint.sh"]
casdoor-mysql:
image: mysql:8.4
--
Gitee
From 18d5da5980225891d94b7f645354a6e432f5e84b Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 17:20:32 +0800
Subject: [PATCH 58/90] docs:Optimize deployment
---
docker/astronAgent/casdoor/entrypoint.sh | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/docker/astronAgent/casdoor/entrypoint.sh b/docker/astronAgent/casdoor/entrypoint.sh
index 137a1c74..0f8314ed 100644
--- a/docker/astronAgent/casdoor/entrypoint.sh
+++ b/docker/astronAgent/casdoor/entrypoint.sh
@@ -4,15 +4,9 @@ set -e
echo "===== Initializing Casdoor Configuration ====="
echo "CONSOLE_DOMAIN: ${CONSOLE_DOMAIN:-http://localhost}"
-# Install envsubst if not available
-if ! command -v envsubst >/dev/null 2>&1; then
- echo "Installing gettext-base for envsubst..."
- apk add --no-cache gettext
-fi
-
-# Generate config from template using envsubst
+# Generate config from template using sed (no need to install extra packages)
echo "Generating init_data.json from template..."
-envsubst < /conf/init_data.json.template > /conf/init_data.json
+sed "s|\${CONSOLE_DOMAIN}|${CONSOLE_DOMAIN}|g" /conf/init_data.json.template > /conf/init_data.json
echo "Configuration updated: redirectUris set to [${CONSOLE_DOMAIN}/callback]"
echo "=========================================="
--
Gitee
From 1f8abb6ef07b17abbe7884207978b24758897d96 Mon Sep 17 00:00:00 2001
From: qzwang9
Date: Wed, 22 Oct 2025 17:20:38 +0800
Subject: [PATCH 59/90] fix: enhance security by using HttpUrl.Builder for safe
URL construction and validation
---
.../service/repo/FileInfoV2Service.java | 41 +++++++++++++------
1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
index 2660bce0..e423c9b5 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
@@ -60,6 +60,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
+import okhttp3.HttpUrl;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
@@ -1947,26 +1948,42 @@ public class FileInfoV2Service extends ServiceImpl
/* ======================== Spark Branch ======================== */
private void streamSparkSearch(SseEmitter emitter, Long repoId, String fileName, HttpServletRequest request) throws IOException {
- // Encode user input to prevent SSRF attacks
- String encodedFileName;
+ // Use HttpUrl.Builder to construct URL safely and prevent SSRF attacks
+ HttpUrl url;
try {
- encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
- } catch (Exception e) {
- log.error("Failed to encode fileName", e);
- encodedFileName = fileName;
+ HttpUrl base = HttpUrl.parse(apiUrl.getDatasetFileUrl());
+ if (base == null) {
+ log.error("Failed to parse base URL: {}", apiUrl.getDatasetFileUrl());
+ throw new IOException("Invalid base URL configuration");
+ }
+
+ url = base.newBuilder()
+ .addQueryParameter("datasetId", repoId.toString())
+ .addQueryParameter("searchValue", fileName)
+ .build();
+
+ // Validate that the constructed URL has the same host as the base URL
+ String expectedHost = base.host();
+ if (!url.host().equals(expectedHost)) {
+ log.error("Refusing to send request to unexpected host: {}", url.host());
+ throw new IOException("Refusing to send request to untrusted host");
+ }
+
+ log.info("searchFile request url: {}", url);
+ } catch (NullPointerException e) {
+ log.error("Null pointer exception while constructing URL", e);
+ throw new IOException("Failed to construct request URL", e);
+ } catch (IllegalArgumentException e) {
+ log.error("Invalid URL format", e);
+ throw new IOException("Invalid URL format", e);
}
- String url = apiUrl.getDatasetFileUrl() + "?datasetId="
- .concat(repoId.toString())
- .concat("&searchValue=")
- .concat(encodedFileName);
- log.info("searchFile request url: {}", url);
Map header = new HashMap<>();
String authorization = request.getHeader("Authorization");
if (StringUtils.isNotBlank(authorization)) {
header.put("Authorization", authorization);
}
- String resp = OkHttpUtil.get(url, header);
+ String resp = OkHttpUtil.get(url.toString(), header);
JSONObject obj = JSON.parseObject(resp);
log.info("searchFile response data: {}", resp);
--
Gitee
From 1a0f5076d2afbb21f52292811d9d1c23b651f4f1 Mon Sep 17 00:00:00 2001
From: Omuigix <33213087+Omuigix@users.noreply.github.com>
Date: Wed, 22 Oct 2025 17:54:28 +0800
Subject: [PATCH 60/90] fix: plat is 1 (#382)
Co-authored-by: xinxiong2
---
.../astron/console/commons/dto/workflow/MaasApi.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/dto/workflow/MaasApi.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/dto/workflow/MaasApi.java
index 8e0e0d2d..cb74ac18 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/dto/workflow/MaasApi.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/dto/workflow/MaasApi.java
@@ -30,7 +30,7 @@ public class MaasApi {
this.flow_id = flow_id;
this.app_id = app_id;
this.release_status = 1;
- this.plat = 2;
+ this.plat = 1;
}
public MaasApi(String flow_id, String app_id, String version) {
@@ -38,7 +38,7 @@ public class MaasApi {
this.app_id = app_id;
this.version = version;
this.release_status = 1;
- this.plat = 2;
+ this.plat = 1;
}
public MaasApi(String flow_id, String app_id, String version, JSONObject data) {
@@ -46,7 +46,7 @@ public class MaasApi {
this.app_id = app_id;
this.version = version;
this.release_status = 1;
- this.plat = 2;
+ this.plat = 1;
this.data = data;
}
}
--
Gitee
From 4c4ffaa22ba85da802606359a23911df281d651d Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 18:00:26 +0800
Subject: [PATCH 61/90] docs:Optimize deployment
---
.../astronAgent/casdoor/conf/init_data.json.template | 3 ++-
docker/astronAgent/casdoor/entrypoint.sh | 10 +++++++---
docker/astronAgent/docker-compose-with-auth.yml | 1 +
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/docker/astronAgent/casdoor/conf/init_data.json.template b/docker/astronAgent/casdoor/conf/init_data.json.template
index 06c8bd74..b4c73e33 100644
--- a/docker/astronAgent/casdoor/conf/init_data.json.template
+++ b/docker/astronAgent/casdoor/conf/init_data.json.template
@@ -11,7 +11,8 @@
"enableSignUp": true,
"clientId": "astron-agent-client",
"redirectUris": [
- "${CONSOLE_DOMAIN}/callback"
+ "${CONSOLE_DOMAIN}/callback",
+ "${HOST_BASE_ADDRESS}/callback"
],
"tokenFormat": "JWT",
"tokenFields": [],
diff --git a/docker/astronAgent/casdoor/entrypoint.sh b/docker/astronAgent/casdoor/entrypoint.sh
index 0f8314ed..2241ff7a 100644
--- a/docker/astronAgent/casdoor/entrypoint.sh
+++ b/docker/astronAgent/casdoor/entrypoint.sh
@@ -3,12 +3,16 @@ set -e
echo "===== Initializing Casdoor Configuration ====="
echo "CONSOLE_DOMAIN: ${CONSOLE_DOMAIN:-http://localhost}"
+echo "HOST_BASE_ADDRESS: ${HOST_BASE_ADDRESS:-http://localhost}"
-# Generate config from template using sed (no need to install extra packages)
+# Generate config from template using sed (replace all environment variables)
echo "Generating init_data.json from template..."
-sed "s|\${CONSOLE_DOMAIN}|${CONSOLE_DOMAIN}|g" /conf/init_data.json.template > /conf/init_data.json
+sed -e "s|\${CONSOLE_DOMAIN}|${CONSOLE_DOMAIN}|g" \
+ -e "s|\${HOST_BASE_ADDRESS}|${HOST_BASE_ADDRESS}|g" \
+ /conf/init_data.json.template > /conf/init_data.json
-echo "Configuration updated: redirectUris set to [${CONSOLE_DOMAIN}/callback]"
+echo "Configuration updated successfully!"
+echo "redirectUris: [${CONSOLE_DOMAIN}/callback, ${HOST_BASE_ADDRESS}/callback]"
echo "=========================================="
# Start Casdoor
diff --git a/docker/astronAgent/docker-compose-with-auth.yml b/docker/astronAgent/docker-compose-with-auth.yml
index 11ccc65b..c56ff11a 100644
--- a/docker/astronAgent/docker-compose-with-auth.yml
+++ b/docker/astronAgent/docker-compose-with-auth.yml
@@ -8,6 +8,7 @@ services:
environment:
- GIN_MODE=release
- CONSOLE_DOMAIN=${CONSOLE_DOMAIN:-http://localhost}
+ - HOST_BASE_ADDRESS=${HOST_BASE_ADDRESS:-http://localhost}
volumes:
- ./casdoor/conf:/conf
- ./casdoor/entrypoint.sh:/entrypoint.sh:ro
--
Gitee
From 028070d6faafead19b0e6087f27c4896a686b7a6 Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 18:57:51 +0800
Subject: [PATCH 62/90] docs:Optimize deployment
---
docker/astronAgent/casdoor/entrypoint.sh | 3 +++
1 file changed, 3 insertions(+)
diff --git a/docker/astronAgent/casdoor/entrypoint.sh b/docker/astronAgent/casdoor/entrypoint.sh
index 2241ff7a..fb757ebb 100644
--- a/docker/astronAgent/casdoor/entrypoint.sh
+++ b/docker/astronAgent/casdoor/entrypoint.sh
@@ -1,6 +1,9 @@
#!/bin/sh
set -e
+# 确保 /conf 目录有写权限
+chmod -R 777 /conf 2>/dev/null || true
+
echo "===== Initializing Casdoor Configuration ====="
echo "CONSOLE_DOMAIN: ${CONSOLE_DOMAIN:-http://localhost}"
echo "HOST_BASE_ADDRESS: ${HOST_BASE_ADDRESS:-http://localhost}"
--
Gitee
From db464afebc3aedc3f84c6c7903ad606a925374a6 Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 19:05:50 +0800
Subject: [PATCH 63/90] docs:Optimize deployment
---
docker/astronAgent/docker-compose-with-auth.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/docker/astronAgent/docker-compose-with-auth.yml b/docker/astronAgent/docker-compose-with-auth.yml
index c56ff11a..cf7886aa 100644
--- a/docker/astronAgent/docker-compose-with-auth.yml
+++ b/docker/astronAgent/docker-compose-with-auth.yml
@@ -3,6 +3,7 @@ services:
image: casbin/casdoor:latest
container_name: astron-agent-casdoor
restart: unless-stopped
+ user: root
ports:
- "8000:8000"
environment:
--
Gitee
From 7989b8eb0ce3804535965153898b8d9dab43807b Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 19:41:13 +0800
Subject: [PATCH 64/90] docs:Optimize deployment
---
docker/astronAgent/.env.example | 12 +-
docker/astronAgent/casdoor/entrypoint.sh | 1 -
.../astronAgent/docker-compose-with-auth.yml | 2 +-
docker/astronAgent/docker-compose.yaml | 148 +++++++-----------
docker/ragflow/docker-compose.yml | 4 +-
docs/DEPLOYMENT_GUIDE_zh.md | 8 +-
6 files changed, 74 insertions(+), 101 deletions(-)
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index 5ca4236a..e2940ceb 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -1,6 +1,13 @@
# Docker Compose environment variable configuration example
# Copy this file to .env and modify the configuration as needed
+# ============================================================================
+# Image Version Configuration
+# ============================================================================
+
+# astron-agent image version tag (default: latest)
+ASTRON_AGENT_VERSION=latest
+
# ============================================================================
# Middleware Configuration
# ============================================================================
@@ -80,6 +87,9 @@ HOST_BASE_ADDRESS=http://localhost
# astron-agent Application Port Configuration
# ============================================================================
+# Core Service Port Configuration
+CASDOOR_PORT=8000
+
# Core Service Port Configuration
CORE_TENANT_PORT=5052
CORE_DATABASE_PORT=7990
@@ -94,7 +104,7 @@ CORE_WORKFLOW_PORT=7880
# These variables are prioritized over VITE_CASDOOR_* equivalents in frontend builds
# Note: The CONSOLE_DOMAIN variable is used to dynamically set the Casdoor redirectUris
# The entrypoint.sh script in casdoor container will replace redirectUris with ${CONSOLE_DOMAIN}/callback
-CONSOLE_CASDOOR_URL=${HOST_BASE_ADDRESS}:8000
+CONSOLE_CASDOOR_URL=${HOST_BASE_ADDRESS}:${CASDOOR_PORT}
CONSOLE_CASDOOR_ID=astron-agent-client
CONSOLE_CASDOOR_APP=astron-agent-app
CONSOLE_CASDOOR_ORG=built-in
diff --git a/docker/astronAgent/casdoor/entrypoint.sh b/docker/astronAgent/casdoor/entrypoint.sh
index fb757ebb..d4c041a3 100644
--- a/docker/astronAgent/casdoor/entrypoint.sh
+++ b/docker/astronAgent/casdoor/entrypoint.sh
@@ -1,7 +1,6 @@
#!/bin/sh
set -e
-# 确保 /conf 目录有写权限
chmod -R 777 /conf 2>/dev/null || true
echo "===== Initializing Casdoor Configuration ====="
diff --git a/docker/astronAgent/docker-compose-with-auth.yml b/docker/astronAgent/docker-compose-with-auth.yml
index cf7886aa..aee70bc1 100644
--- a/docker/astronAgent/docker-compose-with-auth.yml
+++ b/docker/astronAgent/docker-compose-with-auth.yml
@@ -5,7 +5,7 @@ services:
restart: unless-stopped
user: root
ports:
- - "8000:8000"
+ - "${CASDOOR_PORT:-8000}:8000"
environment:
- GIN_MODE=release
- CONSOLE_DOMAIN=${CONSOLE_DOMAIN:-http://localhost}
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index c322b62d..8c428ce2 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -61,53 +61,53 @@ services:
timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
retries: ${HEALTH_CHECK_RETRIES:-60}
- # Elasticsearch Search Engine
- elasticsearch:
- image: elasticsearch:7.16.2
- container_name: astron-agent-elasticsearch
- environment:
- - discovery.type=single-node
- - "ES_JAVA_OPTS=${ES_JAVA_OPTS:--Xms512m -Xmx512m}"
- - xpack.security.enabled=${ELASTICSEARCH_SECURITY_ENABLED:-false}
- - cluster.name=astron-agent-cluster
- volumes:
- - elasticsearch_data:/usr/share/elasticsearch/data
- networks:
- - astron-agent-network
- restart: always
- healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
- interval: ${HEALTH_CHECK_INTERVAL:-30s}
- timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
- retries: ${HEALTH_CHECK_RETRIES:-60}
+ # Elasticsearch Search Engine (Disabled by default, uncomment to enable)
+ # elasticsearch:
+ # image: elasticsearch:7.16.2
+ # container_name: astron-agent-elasticsearch
+ # environment:
+ # - discovery.type=single-node
+ # - "ES_JAVA_OPTS=${ES_JAVA_OPTS:--Xms512m -Xmx512m}"
+ # - xpack.security.enabled=${ELASTICSEARCH_SECURITY_ENABLED:-false}
+ # - cluster.name=astron-agent-cluster
+ # volumes:
+ # - elasticsearch_data:/usr/share/elasticsearch/data
+ # networks:
+ # - astron-agent-network
+ # restart: always
+ # healthcheck:
+ # test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
+ # interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ # timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ # retries: ${HEALTH_CHECK_RETRIES:-60}
- # Kafka Message Queue
- kafka:
- image: apache/kafka:3.7.0
- container_name: astron-agent-kafka
- environment:
- KAFKA_NODE_ID: 1
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092
- KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:29093
- KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
- KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
- KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
- KAFKA_PROCESS_ROLES: broker,controller
- KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
- KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
- KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
- CLUSTER_ID: ${KAFKA_CLUSTER_ID:-MkU3OEVBNTcwNTJENDM2Qk}
- volumes:
- - kafka_data:/var/lib/kafka/data
- networks:
- - astron-agent-network
- restart: always
- healthcheck:
- test: ["CMD-SHELL", "netstat -tulpn | grep 29092 || exit 1"]
- interval: ${HEALTH_CHECK_INTERVAL:-30s}
- timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
- retries: ${HEALTH_CHECK_RETRIES:-60}
+ # Kafka Message Queue (Disabled by default, uncomment to enable)
+ # kafka:
+ # image: apache/kafka:3.7.0
+ # container_name: astron-agent-kafka
+ # environment:
+ # KAFKA_NODE_ID: 1
+ # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
+ # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092
+ # KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:29093
+ # KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ # KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
+ # KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
+ # KAFKA_PROCESS_ROLES: broker,controller
+ # KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
+ # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
+ # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
+ # CLUSTER_ID: ${KAFKA_CLUSTER_ID:-MkU3OEVBNTcwNTJENDM2Qk}
+ # volumes:
+ # - kafka_data:/var/lib/kafka/data
+ # networks:
+ # - astron-agent-network
+ # restart: always
+ # healthcheck:
+ # test: ["CMD-SHELL", "netstat -tulpn | grep 29092 || exit 1"]
+ # interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ # timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ # retries: ${HEALTH_CHECK_RETRIES:-60}
# MinIO Object Storage
minio:
@@ -137,7 +137,7 @@ services:
# Tenant Service
core-tenant:
- image: ghcr.io/iflytek/astron-agent/core-tenant:latest
+ image: ghcr.io/iflytek/astron-agent/core-tenant:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-tenant
environment:
SERVICE_PORT: "${CORE_TENANT_PORT:-5052}"
@@ -156,10 +156,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -173,7 +169,7 @@ services:
# Memory Database Service
core-database:
- image: ghcr.io/iflytek/astron-agent/core-database:latest
+ image: ghcr.io/iflytek/astron-agent/core-database:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-database
environment:
SERVICE_PORT: "${CORE_DATABASE_PORT:-7990}"
@@ -191,10 +187,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -206,7 +198,7 @@ services:
# RPA Plugin Service
core-rpa:
- image: ghcr.io/iflytek/astron-agent/core-rpa:latest
+ image: ghcr.io/iflytek/astron-agent/core-rpa:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-rpa
environment:
SERVICE_PORT: "${CORE_RPA_PORT:-17198}"
@@ -222,10 +214,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -237,7 +225,7 @@ services:
# Link Plugin Service
core-link:
- image: ghcr.io/iflytek/astron-agent/core-link:latest
+ image: ghcr.io/iflytek/astron-agent/core-link:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-link
environment:
SERVICE_PORT: "${CORE_LINK_PORT:-18888}"
@@ -260,10 +248,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -275,7 +259,7 @@ services:
# AI Tools Plugin Service
core-aitools:
- image: ghcr.io/iflytek/astron-agent/core-aitools:latest
+ image: ghcr.io/iflytek/astron-agent/core-aitools:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-aitools
environment:
SERVICE_PORT: "${CORE_AITOOLS_PORT:-18668}"
@@ -297,10 +281,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -312,7 +292,7 @@ services:
# Agent Service
core-agent:
- image: ghcr.io/iflytek/astron-agent/core-agent:latest
+ image: ghcr.io/iflytek/astron-agent/core-agent:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-agent
environment:
SERVICE_LOCATION: "${SERVICE_LOCATION:-hf}"
@@ -364,10 +344,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -378,7 +354,7 @@ services:
# Knowledge Base Service
core-knowledge:
- image: ghcr.io/iflytek/astron-agent/core-knowledge:latest
+ image: ghcr.io/iflytek/astron-agent/core-knowledge:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-knowledge
environment:
SERVICE_PORT: "${CORE_KNOWLEDGE_PORT:-20010}"
@@ -397,10 +373,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -412,7 +384,7 @@ services:
# Workflow Service
core-workflow:
- image: ghcr.io/iflytek/astron-agent/core-workflow:latest
+ image: ghcr.io/iflytek/astron-agent/core-workflow:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-core-workflow
environment:
RUNTIME_ENV: "${RUNTIME_ENV:-dev}"
@@ -461,10 +433,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
volumes:
@@ -501,7 +469,7 @@ services:
# Console Frontend
console-frontend:
- image: ghcr.io/iflytek/astron-agent/console-frontend:latest
+ image: ghcr.io/iflytek/astron-agent/console-frontend:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-console-frontend
environment:
CONSOLE_CASDOOR_URL: "${CONSOLE_CASDOOR_URL:-}"
@@ -518,7 +486,7 @@ services:
# Console Hub Service
console-hub:
- image: ghcr.io/iflytek/astron-agent/console-hub:latest
+ image: ghcr.io/iflytek/astron-agent/console-hub:${ASTRON_AGENT_VERSION:-latest}
container_name: astron-agent-console-hub
environment:
CONSOLE_CASDOOR_URL: "${CONSOLE_CASDOOR_URL:-}"
@@ -606,10 +574,6 @@ services:
condition: service_healthy
redis:
condition: service_healthy
- elasticsearch:
- condition: service_healthy
- kafka:
- condition: service_healthy
minio:
condition: service_healthy
networks:
diff --git a/docker/ragflow/docker-compose.yml b/docker/ragflow/docker-compose.yml
index ace761e8..9459d1e1 100644
--- a/docker/ragflow/docker-compose.yml
+++ b/docker/ragflow/docker-compose.yml
@@ -25,8 +25,8 @@ services:
container_name: ragflow-server
ports:
- ${SVR_HTTP_PORT}:9380
- - 10080:80
- - 10443:443
+ - 18080:80
+ - 18443:443
- 5678:5678
- 5679:5679
- 9382:9382 # entry for MCP (host_port:docker_port). The docker_port must match the value you set for `mcp-port` above.
diff --git a/docs/DEPLOYMENT_GUIDE_zh.md b/docs/DEPLOYMENT_GUIDE_zh.md
index 7ae770af..723fee0a 100644
--- a/docs/DEPLOYMENT_GUIDE_zh.md
+++ b/docs/DEPLOYMENT_GUIDE_zh.md
@@ -48,7 +48,7 @@ docker compose logs -f ragflow
```
**访问地址:**
-- RagFlow Web界面:http://localhost:10080
+- RagFlow Web界面:http://localhost:18080
**模型配置步骤:**
1. 点击头像进入 **Model Providers(模型提供商)** 页面,选择 **Add Model(添加模型)**,填写对应的 **API 地址** 和 **API Key**,分别添加 **Chat 模型** 和 **Embedding 模型**。
@@ -87,14 +87,14 @@ vim .env
```env
# RAGFlow配置
-RAGFLOW_BASE_URL=http://localhost:10080
+RAGFLOW_BASE_URL=http://localhost:18080
RAGFLOW_API_TOKEN=ragflow-your-api-token-here
RAGFLOW_TIMEOUT=60
RAGFLOW_DEFAULT_GROUP=星辰知识库
```
**获取 RagFlow API Token:**
-1. 访问 RagFlow Web界面:http://localhost:10080
+1. 访问 RagFlow Web界面:http://localhost:18080
2. 登录并点击头像进入用户设置
3. 点击API生成 API KEY
4. 将生成的 API KEY 更新到.env文件中的RAGFLOW_API_TOKEN
@@ -272,7 +272,7 @@ docker compose restart console-frontend console-hub
- **Casdoor 管理界面**:http://localhost:8000
### 知识库服务
-- **RagFlow Web界面**:http://localhost:10080
+- **RagFlow Web界面**:http://localhost:18080
### AstronAgent 核心服务
- **控制台前端(nginx代理)**:http://localhost/
--
Gitee
From d97be03712f4f05eae037128e3086504f422b50d Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Wed, 22 Oct 2025 19:57:48 +0800
Subject: [PATCH 65/90] docs:Optimize deployment
---
docs/DEPLOYMENT_GUIDE_zh.md | 37 ++++---------------------------------
1 file changed, 4 insertions(+), 33 deletions(-)
diff --git a/docs/DEPLOYMENT_GUIDE_zh.md b/docs/DEPLOYMENT_GUIDE_zh.md
index 723fee0a..49fe3d56 100644
--- a/docs/DEPLOYMENT_GUIDE_zh.md
+++ b/docs/DEPLOYMENT_GUIDE_zh.md
@@ -164,41 +164,9 @@ HOST_BASE_ADDRESS=http://localhost
- 如果您使用域名访问,请将 `localhost` 替换为您的域名
- 确保 nginx 和 minio 的端口已正确开放
-#### 2.5 配置 Casdoor 认证服务
-
-编辑 docker/astronAgent/.env 文件,配置 Casdoor 连接信息:
-
-```env
-# Casdoor配置
-CONSOLE_CASDOOR_URL=http://localhost:8000
-CONSOLE_CASDOOR_ID=astron-agent-client
-CONSOLE_CASDOOR_APP=astron-agent-app
-CONSOLE_CASDOOR_ORG=built-in
-```
-
-**说明:**
-- `CONSOLE_CASDOOR_URL`: Casdoor 服务地址
-- 默认使用内置的应用配置 (`astron-agent-app`) 和组织 (`built-in`)
-
-**如果修改了 Casdoor 服务地址或 Nginx 端口,需要同步修改回调地址:**
-
-编辑 `docker/astronAgent/casdoor/conf/init_data.json` 文件,修改 `redirectUris`:
-
-```json
-"redirectUris": [
- "http://your-domain/callback"
-]
-```
-
-**回调地址配置示例:**
-- 如果 Nginx 端口为 `80`: `http://your-domain/callback`
-- 如果 Nginx 端口为 `888`: `http://your-domain:888/callback`
-- 如果使用 localhost: `http://localhost/callback` (默认配置)
-
-
### 第三步:启动 AstronAgent 核心服务(包含 Casdoor 认证服务)
-启动 AstronAgent 服务请运行我们的 [docker-compose.yaml](/docker/astronAgent/docker-compose.yaml) 文件。**该文件已通过 `include` 机制集成了 Casdoor 认证服务**,会自动启动 Casdoor 及其 MySQL 数据库。
+启动 AstronAgent 服务请运行我们的 [docker-compose.yaml](/docker/astronAgent/docker-compose.yaml) 文件。**该文件已通过 `include` 机制集成了 Casdoor 认证服务**,会自动启动 Casdoor。
```bash
# 进入 astronAgent 目录
@@ -214,6 +182,9 @@ docker compose ps
docker compose logs -f
```
+**说明:**
+- Casdoor默认的登录账户名:`admin`,密码:`123`
+
### 第四步:修改 Casdoor 认证(可选)
您可以根据需要在 Casdoor 中创建新的应用和组织,并将配置信息更新到 `.env` 文件中(已存在默认组织和应用)。
--
Gitee
From 46074fae8fcc2d07210ce638e9fdbffae0155879 Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Thu, 23 Oct 2025 10:24:43 +0800
Subject: [PATCH 66/90] fix: improve hikari db connection pool config (#375)
---
.../hub/src/main/resources/application.yml | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/console/backend/hub/src/main/resources/application.yml b/console/backend/hub/src/main/resources/application.yml
index bfc6f4ac..db8998ad 100644
--- a/console/backend/hub/src/main/resources/application.yml
+++ b/console/backend/hub/src/main/resources/application.yml
@@ -15,6 +15,28 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${MYSQL_USER:astron}
password: ${MYSQL_PASSWORD:astron-dev-env-db}
+ # HikariCP connection pool configuration
+ hikari:
+ # Maximum pool size
+ maximum-pool-size: 30
+ # Minimum idle connections
+ minimum-idle: 5
+ # Connection timeout (ms)
+ connection-timeout: 30000
+ # Idle timeout (ms)
+ idle-timeout: 600000
+ # Max connection lifetime (ms)
+ max-lifetime: 1800000
+ # Validation timeout (ms)
+ validation-timeout: 5000
+ # Connection test query
+ connection-test-query: SELECT 1
+ # Pool name
+ pool-name: AstronHikariCP
+ # Auto commit
+ auto-commit: true
+ # Leak detection threshold (ms)
+ leak-detection-threshold: 60000
data:
redis:
host: ${REDIS_HOST:redis}
--
Gitee
From 106ba496ce7ee25f3ee2a55bc5629ab2389305f4 Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Thu, 23 Oct 2025 10:29:38 +0800
Subject: [PATCH 67/90] refactor: delete unused console/backend/docker
directory
Signed-off-by: Xudong Sun
---
console/backend/docker/schema.sql | 0
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 console/backend/docker/schema.sql
diff --git a/console/backend/docker/schema.sql b/console/backend/docker/schema.sql
deleted file mode 100644
index e69de29b..00000000
--
Gitee
From 5c644a0b49bd95d9590d63ce201afa103471bc9b Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Thu, 23 Oct 2025 10:31:39 +0800
Subject: [PATCH 68/90] fix:Remove Docker network configuration
---
docker/astronAgent/.env.example | 2 --
docker/astronAgent/docker-compose.yaml | 3 ---
2 files changed, 5 deletions(-)
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index e2940ceb..16e729e9 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -300,6 +300,4 @@ HEALTH_CHECK_INTERVAL=30s
HEALTH_CHECK_TIMEOUT=10s
HEALTH_CHECK_RETRIES=60
-# Network configuration
-NETWORK_SUBNET=172.20.0.0/16
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index 8c428ce2..4e8d3dae 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -586,9 +586,6 @@ services:
networks:
astron-agent-network:
driver: bridge
- ipam:
- config:
- - subnet: ${NETWORK_SUBNET:-172.40.0.0/16}
# ============================================================================
# Volume Configuration
--
Gitee
From 943724b7d7a97bc89523f8c5536d8aeac23c665f Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Thu, 23 Oct 2025 11:12:13 +0800
Subject: [PATCH 69/90] refactor: optimize hub service memory usage (#392)
---
console/backend/hub/Dockerfile | 16 +++++++++++++++-
.../hub/src/main/resources/application.yml | 11 +++++++++--
.../src/main/resources/application-toolkit.yml | 9 +++++----
3 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/console/backend/hub/Dockerfile b/console/backend/hub/Dockerfile
index 59fff43a..6b3cf707 100644
--- a/console/backend/hub/Dockerfile
+++ b/console/backend/hub/Dockerfile
@@ -29,4 +29,18 @@ ENV LC_ALL=zh_CN.UTF-8
COPY --from=build /backend/hub/target/hub-server.jar /app/app.jar
EXPOSE 8080
-ENTRYPOINT ["java","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=75.0","-jar","/app/app.jar"]
+
+# Optimized JVM parameters for reduced memory usage:
+# - MaxRAMPercentage: reduced from 75% to 70% for lower heap memory usage
+# - InitialRAMPercentage: set to 50% to avoid frequent heap expansion
+# - UseG1GC: use G1 garbage collector (default in Java 21, but explicit for clarity)
+# - UseStringDeduplication: eliminate duplicate strings to save memory
+# - Xss512k: reduce thread stack size from default 1MB to 512KB (safe for production)
+ENTRYPOINT ["java", \
+ "-XX:+UseContainerSupport", \
+ "-XX:MaxRAMPercentage=70.0", \
+ "-XX:InitialRAMPercentage=50.0", \
+ "-XX:+UseG1GC", \
+ "-XX:+UseStringDeduplication", \
+ "-Xss512k", \
+ "-jar", "/app/app.jar"]
diff --git a/console/backend/hub/src/main/resources/application.yml b/console/backend/hub/src/main/resources/application.yml
index db8998ad..68b0c20e 100644
--- a/console/backend/hub/src/main/resources/application.yml
+++ b/console/backend/hub/src/main/resources/application.yml
@@ -18,9 +18,9 @@ spring:
# HikariCP connection pool configuration
hikari:
# Maximum pool size
- maximum-pool-size: 30
+ maximum-pool-size: 10
# Minimum idle connections
- minimum-idle: 5
+ minimum-idle: 2
# Connection timeout (ms)
connection-timeout: 30000
# Idle timeout (ms)
@@ -42,6 +42,13 @@ spring:
host: ${REDIS_HOST:redis}
port: ${REDIS_PORT:6379}
database: ${REDIS_DATABASE_CONSOLE:0}
+ # Lettuce connection pool configuration for reduced memory usage
+ lettuce:
+ pool:
+ max-active: 8 # Maximum number of active connections
+ max-idle: 4 # Maximum number of idle connections
+ min-idle: 1 # Minimum number of idle connections
+ max-wait: 3000ms # Maximum wait time when pool is exhausted
security:
oauth2:
resourceserver:
diff --git a/console/backend/toolkit/src/main/resources/application-toolkit.yml b/console/backend/toolkit/src/main/resources/application-toolkit.yml
index 1d71da09..7e932d13 100644
--- a/console/backend/toolkit/src/main/resources/application-toolkit.yml
+++ b/console/backend/toolkit/src/main/resources/application-toolkit.yml
@@ -70,10 +70,11 @@ task:
wait-for-tasks-to-complete-on-shutdown: true
executor:
- core-pool-size: 8
- max-pool-size: 16
- queue-capacity: 2000
- keep-alive-seconds: 60
+ # Optimized thread pool configuration for reduced memory usage
+ core-pool-size: 4
+ max-pool-size: 10
+ queue-capacity: 1000
+ keep-alive-seconds: 30
allow-core-thread-timeout: false
thread-name-prefix: app-async-
await-termination-seconds: 20
--
Gitee
From 5c230f213969696091f1604379b70a9edcb7509d Mon Sep 17 00:00:00 2001
From: sharphu
Date: Thu, 23 Oct 2025 11:16:20 +0800
Subject: [PATCH 70/90] chore: database project adjustment of database
connection pool and main process number
---
core/memory/database/main.py | 2 +-
.../database/repository/middleware/database/db_manager.py | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/core/memory/database/main.py b/core/memory/database/main.py
index aa60ec8c..258eb638 100644
--- a/core/memory/database/main.py
+++ b/core/memory/database/main.py
@@ -178,7 +178,7 @@ if __name__ == "__main__":
workers=(
None
if sys.platform in ["win", "win32", "darwin"]
- else int(os.getenv("WORKERS", "20"))
+ else int(os.getenv("WORKERS", "1"))
),
reload=False,
log_level="error",
diff --git a/core/memory/database/repository/middleware/database/db_manager.py b/core/memory/database/repository/middleware/database/db_manager.py
index 7d3b2837..6f0f1faf 100644
--- a/core/memory/database/repository/middleware/database/db_manager.py
+++ b/core/memory/database/repository/middleware/database/db_manager.py
@@ -34,8 +34,8 @@ class DatabaseService(Service):
self,
database_url: str,
connect_timeout: int = 10,
- pool_size: int = 200,
- max_overflow: int = 800,
+ pool_size: int = 20,
+ max_overflow: int = 20,
pool_recycle: int = 3600,
):
"""Initialize database service with connection parameters.
@@ -60,8 +60,8 @@ class DatabaseService(Service):
cls,
database_url: str,
connect_timeout: int = 10,
- pool_size: int = 200,
- max_overflow: int = 800,
+ pool_size: int = 20,
+ max_overflow: int = 20,
pool_recycle: int = 3600,
) -> "DatabaseService":
"""Create and initialize database service instance.
--
Gitee
From ff3fc4b2f8ca56db88eaf3e5f9e68fdb44394fea Mon Sep 17 00:00:00 2001
From: hygao1024
Date: Thu, 23 Oct 2025 14:05:48 +0800
Subject: [PATCH 71/90] feat(workflow): optimize status evaluation and reduce
startup memory usage to 260MB
---
core/workflow/api/v1/flow/layout.py | 17 +----------------
core/workflow/consts/flow.py | 19 -------------------
core/workflow/domain/entities/flow.py | 2 --
core/workflow/domain/models/flow.py | 2 --
core/workflow/engine/dsl_engine.py | 4 +++-
core/workflow/engine/entities/file.py | 3 ++-
core/workflow/engine/node.py | 4 +++-
core/workflow/engine/nodes/base_node.py | 3 ++-
.../engine/nodes/plugin_tool/link_client.py | 8 ++++++--
.../extensions/fastapi/middleware/auth.py | 4 +++-
.../extensions/middleware/oss/manager.py | 4 +++-
.../providers/llm/openai/openai_chat_llm.py | 4 ++--
core/workflow/service/app_service.py | 7 ++++++-
core/workflow/service/chat_service.py | 6 ++++--
core/workflow/service/flow_service.py | 6 ------
core/workflow/service/publish_service.py | 1 -
16 files changed, 35 insertions(+), 59 deletions(-)
delete mode 100644 core/workflow/consts/flow.py
diff --git a/core/workflow/api/v1/flow/layout.py b/core/workflow/api/v1/flow/layout.py
index 5097f74c..b95b417d 100644
--- a/core/workflow/api/v1/flow/layout.py
+++ b/core/workflow/api/v1/flow/layout.py
@@ -20,7 +20,6 @@ from starlette.responses import JSONResponse, StreamingResponse
from workflow.cache.flow import del_flow_by_id
from workflow.consts.comparisons import Tag
-from workflow.consts.flow import FlowStatus
from workflow.domain.entities.compare_flow import DeleteComparisonVo, SaveComparisonVo
from workflow.domain.entities.flow import FlowRead, FlowUpdate
from workflow.domain.entities.response import Resp, Streaming
@@ -33,7 +32,7 @@ from workflow.extensions.middleware.cache.base import BaseCacheService
from workflow.extensions.middleware.getters import get_cache_service, get_session
from workflow.extensions.otlp.metric.meter import Meter
from workflow.extensions.otlp.trace.span import Span
-from workflow.service import app_service, flow_service, license_service
+from workflow.service import app_service, flow_service
router = APIRouter(tags=["Flows"])
@@ -56,12 +55,6 @@ def add(
) as current_span:
try:
current_span.add_info_event(f"add flow vo: {flow.json()}")
- if flow.status not in [FlowStatus.DRAFT.value, FlowStatus.PUBLISHED.value]:
- raise CustomException(
- err_code=CodeEnum.PROTOCOL_CREATE_ERROR,
- err_msg=f"status value can only be 0 or 1, "
- f"current value is {flow.status}",
- )
app_info = app_service.get_info(flow.app_id, session, current_span)
db_flow = flow_service.save(flow, app_info, session, current_span)
@@ -164,13 +157,6 @@ def update(
if not db_flow:
raise CustomException(CodeEnum.FLOW_NOT_FOUND_ERROR)
- # Register app_id to App table for published workflows
- # and bind in license table
- if db_flow.status > 0 or (flow.status is not None and flow.status > 0):
- if not db_flow.app_id:
- raise CustomException(CodeEnum.FLOW_NO_APP_ID_ERROR)
- db_app = app_service.get_info(db_flow.app_id, session, current_span)
- license_service.bind(session, db_app, db_flow.group_id)
flow_service.update(session, db_flow, flow, flow_id, current_span)
m.in_success_count()
return Resp.success(None, span.sid)
@@ -337,7 +323,6 @@ def save_comparisons(
name=db_flow.name,
data=chat_input.data,
description=db_flow.description,
- status=db_flow.status,
app_id=db_flow.app_id,
source=db_flow.source,
version=chat_input.version,
diff --git a/core/workflow/consts/flow.py b/core/workflow/consts/flow.py
deleted file mode 100644
index ab793dbe..00000000
--- a/core/workflow/consts/flow.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""
-Workflow and flow-related constants.
-
-This module defines constants and enumerations for workflow execution,
-node statuses, error handling, and flow states.
-"""
-
-from enum import Enum
-
-
-class FlowStatus(Enum):
- """
- Flow status enumeration.
-
- Defines the publication status of workflows.
- """
-
- DRAFT = 0
- PUBLISHED = 1
diff --git a/core/workflow/domain/entities/flow.py b/core/workflow/domain/entities/flow.py
index 8078b07e..0135485a 100644
--- a/core/workflow/domain/entities/flow.py
+++ b/core/workflow/domain/entities/flow.py
@@ -31,7 +31,6 @@ class FlowUpdate(BaseModel):
:param description: Workflow description
:param data: Workflow data dictionary
:param app_id: Application ID
- :param status: Workflow status
"""
id: Optional[str] = None
@@ -39,7 +38,6 @@ class FlowUpdate(BaseModel):
description: Optional[str] = None
data: Optional[Dict] = None
app_id: Optional[str] = None
- status: Optional[int] = None
class Edge(BaseModel):
diff --git a/core/workflow/domain/models/flow.py b/core/workflow/domain/models/flow.py
index 6401cf40..45ca8b96 100644
--- a/core/workflow/domain/models/flow.py
+++ b/core/workflow/domain/models/flow.py
@@ -29,7 +29,6 @@ class Flow(SQLModelSerializable, table=True): # type: ignore
:param release_data: Released workflow data (stored as JSON)
:param description: Workflow description
:param version: Workflow version string
- :param status: Workflow status (0=inactive, 1=active)
:param release_status: Release status (0=not released, 1=released)
:param app_id: Associated application identifier
:param source: Source identifier for the workflow
@@ -45,7 +44,6 @@ class Flow(SQLModelSerializable, table=True): # type: ignore
release_data: Dict = Field(default_factory=dict, sa_column=Column(JSON))
description: str = Field(default="", index=True)
version: str = Field(default="", index=True)
- status: int = Field(default=0)
release_status: int = Field(default=0)
app_id: str = Field(default="")
source: int = Field(default=0)
diff --git a/core/workflow/engine/dsl_engine.py b/core/workflow/engine/dsl_engine.py
index 27fdb0f8..c217cb2b 100644
--- a/core/workflow/engine/dsl_engine.py
+++ b/core/workflow/engine/dsl_engine.py
@@ -35,7 +35,6 @@ from workflow.engine.entities.variable_pool import VariablePool
from workflow.engine.entities.workflow_dsl import Edge, Node, NodeRef, WorkflowDSL
from workflow.engine.node import NodeFactory, SparkFlowEngineNode
from workflow.engine.nodes.base_node import BaseNode
-from workflow.engine.nodes.cache_node import tool_classes
from workflow.engine.nodes.entities.node_run_result import (
NodeRunResult,
WorkflowNodeExecutionStatus,
@@ -2026,6 +2025,9 @@ class WorkflowEngineBuilder:
:return: None
:raises CustomException: If node validation fails
"""
+
+ from workflow.engine.nodes.cache_node import tool_classes
+
node_type = node_id.split(":")[0]
node_class = tool_classes.get(node_type)
diff --git a/core/workflow/engine/entities/file.py b/core/workflow/engine/entities/file.py
index 366d82bf..b6d93694 100644
--- a/core/workflow/engine/entities/file.py
+++ b/core/workflow/engine/entities/file.py
@@ -1,7 +1,6 @@
import re
from typing import Tuple
-import requests # type: ignore
from pydantic import BaseModel
from workflow.configs import workflow_config
@@ -121,6 +120,8 @@ class File(BaseModel):
"""
try:
# Send HEAD request
+ import requests # type: ignore
+
response = requests.head(input_file_url)
# Get file metadata from response headers
content_length = response.headers.get(
diff --git a/core/workflow/engine/node.py b/core/workflow/engine/node.py
index 30540c3f..acf091ad 100644
--- a/core/workflow/engine/node.py
+++ b/core/workflow/engine/node.py
@@ -14,7 +14,6 @@ from workflow.engine.entities.retry_config import RetryConfig
from workflow.engine.entities.variable_pool import VariablePool
from workflow.engine.entities.workflow_dsl import InputItem, Node, OutputItem
from workflow.engine.nodes.base_node import BaseNode
-from workflow.engine.nodes.cache_node import tool_classes
from workflow.engine.nodes.entities.node_run_result import (
NodeRunResult,
WorkflowNodeExecutionStatus,
@@ -654,6 +653,9 @@ class NodeFactory:
:return: Created node instance
:raises CustomException: When node type is not supported
"""
+
+ from workflow.engine.nodes.cache_node import tool_classes
+
node_class = tool_classes.get(node.get_node_type())
if not node_class:
raise CustomException(
diff --git a/core/workflow/engine/nodes/base_node.py b/core/workflow/engine/nodes/base_node.py
index cc58004c..11d33be1 100644
--- a/core/workflow/engine/nodes/base_node.py
+++ b/core/workflow/engine/nodes/base_node.py
@@ -6,7 +6,6 @@ from abc import abstractmethod
from asyncio import Event
from typing import Any, AsyncIterator, Dict, List, Literal, Optional, Tuple
-import requests # type: ignore
from pydantic import BaseModel, Field, PrivateAttr
from workflow.consts.engine.chat_status import ChatStatus, SparkLLMStatus
@@ -1188,6 +1187,8 @@ class BaseLLMNode(BaseNode):
payload_comp_history.pop(0)
# If it's an image understanding model, reserve the first position in array for image
if image_url:
+ import requests # type: ignore
+
image_response = requests.get(image_url)
if image_response.status_code != 200:
raise Exception(f"Failed to download image from {image_url}")
diff --git a/core/workflow/engine/nodes/plugin_tool/link_client.py b/core/workflow/engine/nodes/plugin_tool/link_client.py
index b669ee20..ed344605 100644
--- a/core/workflow/engine/nodes/plugin_tool/link_client.py
+++ b/core/workflow/engine/nodes/plugin_tool/link_client.py
@@ -3,8 +3,6 @@ import time
from base64 import b64encode
from typing import Any, Dict, List, Set, Tuple
-import requests # type: ignore
-
from workflow.exception.e import CustomException
from workflow.exception.errors.code_convert import CodeConvert
from workflow.exception.errors.err_code import CodeEnum
@@ -263,6 +261,9 @@ class Tool:
)
# Execute HTTP request to Link system
+
+ import requests # type: ignore
+
try:
from aiohttp import ClientSession
@@ -371,6 +372,9 @@ class Link:
"versions": [self.version],
"app_id": self.app_id,
}
+
+ import requests # type: ignore
+
response_json = requests.get(
self.get_url, headers=self.const_headers, params=params
).json()
diff --git a/core/workflow/extensions/fastapi/middleware/auth.py b/core/workflow/extensions/fastapi/middleware/auth.py
index df453858..92493a8d 100644
--- a/core/workflow/extensions/fastapi/middleware/auth.py
+++ b/core/workflow/extensions/fastapi/middleware/auth.py
@@ -2,7 +2,6 @@ import json
import os
from typing import Any
-import requests # type: ignore
from common.utils.hmac_auth import HMACAuth
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
@@ -134,6 +133,9 @@ class AuthMiddleware(BaseHTTPMiddleware):
if app_id:
return app_id
url = f"{url}/{api_key}"
+
+ import requests # type: ignore
+
resp = requests.get(url, headers=self._gen_app_auth_header(url))
span.add_info_event(f"Application management platform response: {resp.text}")
if resp.status_code != 200:
diff --git a/core/workflow/extensions/middleware/oss/manager.py b/core/workflow/extensions/middleware/oss/manager.py
index 85b1229a..ac760247 100644
--- a/core/workflow/extensions/middleware/oss/manager.py
+++ b/core/workflow/extensions/middleware/oss/manager.py
@@ -10,7 +10,6 @@ from typing import Optional
from urllib.parse import urlencode
import boto3 # type: ignore
-import requests # type: ignore
from botocore.exceptions import ClientError
from common.utils.hmac_auth import HMACAuth
from loguru import logger
@@ -187,6 +186,9 @@ class IFlyGatewayStorageClient(BaseOSSService, Service):
headers["X-TTL"] = str(self.ttl)
headers["Content-Length"] = str(len(file_bytes))
try:
+
+ import requests # type: ignore
+
resp = requests.post(url, headers=headers, data=file_bytes)
except Exception as e:
logger.error(e)
diff --git a/core/workflow/infra/providers/llm/openai/openai_chat_llm.py b/core/workflow/infra/providers/llm/openai/openai_chat_llm.py
index 67977fc3..82b7283f 100644
--- a/core/workflow/infra/providers/llm/openai/openai_chat_llm.py
+++ b/core/workflow/infra/providers/llm/openai/openai_chat_llm.py
@@ -9,8 +9,6 @@ import asyncio
import json
from typing import Any, AsyncIterator, Dict, Tuple
-from openai import AsyncOpenAI # type: ignore
-
from workflow.consts.engine.chat_status import ChatStatus
from workflow.engine.nodes.entities.llm_response import LLMResponse
from workflow.exception.e import CustomException
@@ -110,6 +108,8 @@ class OpenAIChatAI(ChatAI):
:raises CustomException: If request times out or fails
"""
# Initialize OpenAI async client
+ from openai import AsyncOpenAI # type: ignore
+
aclient = AsyncOpenAI(
api_key=self.api_key,
base_url=url,
diff --git a/core/workflow/service/app_service.py b/core/workflow/service/app_service.py
index 7caf1d83..56bfab61 100644
--- a/core/workflow/service/app_service.py
+++ b/core/workflow/service/app_service.py
@@ -1,7 +1,6 @@
import json
import os
-import requests # type: ignore
from common.utils.hmac_auth import HMACAuth
from sqlmodel import Session # type: ignore
@@ -50,6 +49,9 @@ def get_app_source_id(app_id: str, span: Span) -> str:
url = f"{os.getenv('APP_MANAGE_PLAT_BASE_URL')}/v2/app/list"
# Make authenticated request to get application list
+
+ import requests # type: ignore
+
resp = requests.get(
url, headers=_gen_app_auth_header(url), params={"app_ids": app_id}
)
@@ -92,6 +94,9 @@ def get_app_source_detail(app_id: str, span: Span) -> tuple[str, str, str, str]:
url = f"{os.getenv('APP_MANAGE_PLAT_BASE_URL')}/v2/app/details"
# Make authenticated request to get application details
+
+ import requests # type: ignore
+
resp = requests.get(
url, headers=_gen_app_auth_header(url), params={"app_ids": app_id}
)
diff --git a/core/workflow/service/chat_service.py b/core/workflow/service/chat_service.py
index ca60e093..16f7681a 100644
--- a/core/workflow/service/chat_service.py
+++ b/core/workflow/service/chat_service.py
@@ -19,7 +19,6 @@ from typing import (
from loguru import logger
-from workflow.cache.engine import get_engine, set_engine
from workflow.cache.event_registry import Event, EventRegistry
from workflow.consts.app_audit import AppAuditPolicy
from workflow.consts.engine.chat_status import ChatStatus
@@ -38,7 +37,6 @@ from workflow.engine.callbacks.openai_types_sse import (
WorkflowStep,
)
from workflow.engine.dsl_engine import WorkflowEngine, WorkflowEngineFactory
-from workflow.engine.entities.file import File
from workflow.engine.entities.msg_or_end_dep_info import MsgOrEndDepInfo
from workflow.engine.entities.node_entities import NodeType
from workflow.engine.entities.variable_pool import ParamKey, VariablePool
@@ -176,6 +174,8 @@ async def _get_or_build_workflow_engine(
need_rebuild = True
# Attempt to retrieve engine from cache
+ from workflow.cache.engine import get_engine, set_engine
+
sparkflow_engine_cache_obj = get_engine(
is_release, chat_vo.flow_id, chat_vo.version, app_alias_id
)
@@ -311,6 +311,8 @@ async def _validate_file_inputs(
:raises CustomException: When file validation fails or
required parameters are missing
"""
+ from workflow.engine.entities.file import File
+
file_info_list, has_file = File.has_file_in_dsl(workflow_dsl, span_context)
if not has_file:
return
diff --git a/core/workflow/service/flow_service.py b/core/workflow/service/flow_service.py
index 887801f4..9937f1e5 100644
--- a/core/workflow/service/flow_service.py
+++ b/core/workflow/service/flow_service.py
@@ -57,7 +57,6 @@ def save(flow: Flow, app_info: App, session: Session, span: Span) -> Flow:
name=flow.name,
data=flow.data,
description=flow.description,
- status=flow.status,
app_id=flow.app_id,
source=app_info.actual_source,
version="-1", # Initial version for new flows
@@ -95,11 +94,6 @@ def update(
db_flow.app_id = flow.app_id
if flow.data:
db_flow.data = flow.data
- if flow.status:
- db_flow.status = flow.status
- # Set release data when status is published (status > 0)
- if flow.status > 0:
- db_flow.release_data = db_flow.data
session.add(db_flow)
session.commit()
diff --git a/core/workflow/service/publish_service.py b/core/workflow/service/publish_service.py
index f077b08f..8affa151 100644
--- a/core/workflow/service/publish_service.py
+++ b/core/workflow/service/publish_service.py
@@ -266,7 +266,6 @@ def _handle_version(
release_data=db_flow.release_data,
description=db_flow.description,
version=publish_input.version,
- status=db_flow.status,
release_status=db_flow.release_status,
app_id=db_flow.app_id,
source=db_flow.source,
--
Gitee
From c854ceb1859920b87014eea5f0dcd09b704f9fe4 Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Thu, 23 Oct 2025 14:35:28 +0800
Subject: [PATCH 72/90] feat: improve S3ClientUtil, adding remoteEndpoint
config. (#395)
---
.../console/commons/util/S3ClientUtil.java | 5 +-
.../commons/util/S3ClientUtilTest.java | 52 ++++++++++++-------
.../hub/src/main/resources/application.yml | 3 +-
.../astron/console/toolkit/util/S3Util.java | 9 ++--
4 files changed, 44 insertions(+), 25 deletions(-)
diff --git a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
index 40c96b01..638626b7 100644
--- a/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
+++ b/console/backend/commons/src/main/java/com/iflytek/astron/console/commons/util/S3ClientUtil.java
@@ -36,6 +36,9 @@ public class S3ClientUtil {
@Value("${s3.endpoint}")
private String endpoint;
+ @Value("${s3.remoteEndpoint}")
+ private String remoteEndpoint;
+
@Value("${s3.accessKey}")
private String accessKey;
@@ -151,7 +154,7 @@ public class S3ClientUtil {
* @return full object URL
*/
private String buildObjectUrl(String bucketName, String objectKey) {
- return String.format("%s/%s/%s", endpoint, bucketName, objectKey);
+ return String.format("%s/%s/%s", remoteEndpoint, bucketName, objectKey);
}
/**
diff --git a/console/backend/commons/src/test/java/com/iflytek/astron/console/commons/util/S3ClientUtilTest.java b/console/backend/commons/src/test/java/com/iflytek/astron/console/commons/util/S3ClientUtilTest.java
index c19835d8..9449ae55 100644
--- a/console/backend/commons/src/test/java/com/iflytek/astron/console/commons/util/S3ClientUtilTest.java
+++ b/console/backend/commons/src/test/java/com/iflytek/astron/console/commons/util/S3ClientUtilTest.java
@@ -25,7 +25,8 @@ import org.springframework.test.util.ReflectionTestUtils;
*
* Test configuration: - MinIO connection details configurable via environment variables Environment
* variables: - MINIO_TEST_ENDPOINT: MinIO server endpoint (default: http://localhost:9000) -
- * MINIO_TEST_ACCESS_KEY: Access key for authentication (default: minioadmin) -
+ * MINIO_TEST_REMOTE_ENDPOINT: Remote endpoint for external access (default: http://localhost:9000)
+ * - MINIO_TEST_ACCESS_KEY: Access key for authentication (default: minioadmin) -
* MINIO_TEST_SECRET_KEY: Secret key for authentication (default: minioadmin) - MINIO_TEST_BUCKET:
* Bucket name for testing (default: astron-project) - MINIO_INVALID_ACCESS_KEY: Invalid access key
* for negative testing (default: invalid-user) - MINIO_INVALID_SECRET_KEY: Invalid secret key for
@@ -39,6 +40,7 @@ class S3ClientUtilTest {
// MinIO test environment configuration - from environment variables
private static final String TEST_ENDPOINT = System.getenv().getOrDefault("MINIO_TEST_ENDPOINT", "http://localhost:9000");
+ private static final String TEST_REMOTE_ENDPOINT = System.getenv().getOrDefault("MINIO_TEST_REMOTE_ENDPOINT", "http://localhost:9000");
private static final String TEST_ACCESS_KEY = System.getenv().getOrDefault("MINIO_TEST_ACCESS_KEY", "minioadmin");
private static final String TEST_SECRET_KEY = System.getenv().getOrDefault("MINIO_TEST_SECRET_KEY", "minioadmin");
private static final String TEST_BUCKET = System.getenv().getOrDefault("MINIO_TEST_BUCKET", "astron-project");
@@ -77,6 +79,7 @@ class S3ClientUtilTest {
// Use real MinIO test environment configuration
ReflectionTestUtils.setField(s3ClientUtil, "endpoint", TEST_ENDPOINT);
+ ReflectionTestUtils.setField(s3ClientUtil, "remoteEndpoint", TEST_REMOTE_ENDPOINT);
ReflectionTestUtils.setField(s3ClientUtil, "accessKey", TEST_ACCESS_KEY);
ReflectionTestUtils.setField(s3ClientUtil, "secretKey", TEST_SECRET_KEY);
ReflectionTestUtils.setField(s3ClientUtil, "defaultBucket", TEST_BUCKET);
@@ -143,7 +146,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, contentType, inputStream, testContent.length, -1);
// Verify returned URL format is correct
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -159,7 +162,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, null, inputStream, testContent.length, -1);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -175,7 +178,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, "", inputStream, testContent.length, -1);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -185,6 +188,7 @@ class S3ClientUtilTest {
// Create an S3ClientUtil using invalid credentials
S3ClientUtil invalidS3ClientUtil = new S3ClientUtil();
ReflectionTestUtils.setField(invalidS3ClientUtil, "endpoint", TEST_ENDPOINT);
+ ReflectionTestUtils.setField(invalidS3ClientUtil, "remoteEndpoint", TEST_REMOTE_ENDPOINT);
ReflectionTestUtils.setField(invalidS3ClientUtil, "accessKey", INVALID_ACCESS_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "secretKey", INVALID_SECRET_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "defaultBucket", TEST_BUCKET);
@@ -233,6 +237,7 @@ class S3ClientUtilTest {
// Create an S3ClientUtil using invalid credentials
S3ClientUtil invalidS3ClientUtil = new S3ClientUtil();
ReflectionTestUtils.setField(invalidS3ClientUtil, "endpoint", TEST_ENDPOINT);
+ ReflectionTestUtils.setField(invalidS3ClientUtil, "remoteEndpoint", TEST_REMOTE_ENDPOINT);
ReflectionTestUtils.setField(invalidS3ClientUtil, "accessKey", INVALID_ACCESS_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "secretKey", INVALID_SECRET_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "defaultBucket", TEST_BUCKET);
@@ -289,7 +294,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(objectKey, contentType, inputStream, testContent.length, -1);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -322,7 +327,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, contentType, data);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -338,7 +343,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, contentType, inputStream);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -354,7 +359,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(objectKey, contentType, data);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -370,7 +375,7 @@ class S3ClientUtilTest {
String result = s3ClientUtil.uploadObject(objectKey, contentType, inputStream);
// Verify returned URL
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, result);
}
@@ -417,17 +422,20 @@ class S3ClientUtilTest {
// Execute upload
String generatedUrl = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, contentType, inputStream, testContentBytes.length, -1);
- // Verify URL format
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ // Verify URL format (should be remote endpoint)
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, generatedUrl);
+ // For testing actual access, use internal endpoint if remote endpoint is not accessible
+ String accessUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+
// Verify if URL is accessible
- Assertions.assertTrue(isUrlAccessible(generatedUrl),
- "Generated URL should be accessible: " + generatedUrl);
+ Assertions.assertTrue(isUrlAccessible(accessUrl),
+ "Generated URL should be accessible: " + accessUrl);
// Verify correct content can be read through URL
try {
- String downloadedContent = readFromUrl(generatedUrl);
+ String downloadedContent = readFromUrl(accessUrl);
Assertions.assertEquals(testContent, downloadedContent,
"Content downloaded via URL should match uploaded content");
} catch (IOException e) {
@@ -447,17 +455,20 @@ class S3ClientUtilTest {
// Execute test
String generatedUrl = s3ClientUtil.uploadObject(TEST_BUCKET, objectKey, contentType, data);
- // Verify URL format
- String expectedUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+ // Verify URL format (should be remote endpoint)
+ String expectedUrl = TEST_REMOTE_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
Assertions.assertEquals(expectedUrl, generatedUrl);
+ // For testing actual access, use internal endpoint if remote endpoint is not accessible
+ String accessUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/" + objectKey;
+
// Verify if URL is accessible
- Assertions.assertTrue(isUrlAccessible(generatedUrl),
- "Generated URL should be accessible: " + generatedUrl);
+ Assertions.assertTrue(isUrlAccessible(accessUrl),
+ "Generated URL should be accessible: " + accessUrl);
// Verify correct content can be read through URL
try {
- String downloadedContent = readFromUrl(generatedUrl);
+ String downloadedContent = readFromUrl(accessUrl);
Assertions.assertEquals(testContent, downloadedContent,
"Content downloaded via URL should match uploaded content");
} catch (IOException e) {
@@ -519,7 +530,7 @@ class S3ClientUtilTest {
@Test
@DisabledIf("isMinioUnavailable")
void uploadObject_invalidUrl_shouldNotBeAccessible() {
- // Construct a non-existent URL
+ // Construct a non-existent URL (using internal endpoint for actual access test)
String invalidUrl = TEST_ENDPOINT + "/" + TEST_BUCKET + "/nonexistent/file_" + System.currentTimeMillis() + ".txt";
// Verify non-existent URL is not accessible
@@ -596,6 +607,7 @@ class S3ClientUtilTest {
// Create an S3ClientUtil using invalid credentials
S3ClientUtil invalidS3ClientUtil = new S3ClientUtil();
ReflectionTestUtils.setField(invalidS3ClientUtil, "endpoint", TEST_ENDPOINT);
+ ReflectionTestUtils.setField(invalidS3ClientUtil, "remoteEndpoint", TEST_REMOTE_ENDPOINT);
ReflectionTestUtils.setField(invalidS3ClientUtil, "accessKey", INVALID_ACCESS_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "secretKey", INVALID_SECRET_KEY);
ReflectionTestUtils.setField(invalidS3ClientUtil, "defaultBucket", TEST_BUCKET);
diff --git a/console/backend/hub/src/main/resources/application.yml b/console/backend/hub/src/main/resources/application.yml
index 68b0c20e..87fd565d 100644
--- a/console/backend/hub/src/main/resources/application.yml
+++ b/console/backend/hub/src/main/resources/application.yml
@@ -96,7 +96,8 @@ mybatis-plus:
# S3(MinIO) basic configuration
s3:
- endpoint: ${OSS_REMOTE_ENDPOINT:http://minio:9000}
+ endpoint: ${OSS_ENDPOINT:http://minio:9000}
+ remoteEndpoint: ${OSS_REMOTE_ENDPOINT:http://your-host-domain:9000}
accessKey: ${OSS_ACCESS_KEY_ID:astron-uploader}
secretKey: ${OSS_ACCESS_KEY_SECRET:astron-uploader-secret}
bucket: ${OSS_BUCKET_CONSOLE:astron-agent}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/S3Util.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/S3Util.java
index 9e2a365d..da35d9e7 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/S3Util.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/S3Util.java
@@ -55,6 +55,9 @@ public class S3Util {
@Value("${s3.endpoint}")
private String endpoint;
+ @Value("${s3.remoteEndpoint}")
+ private String remoteEndpoint;
+
@Value("${s3.accessKey}")
private String accessKey;
@@ -281,7 +284,7 @@ public class S3Util {
* @return direct URL string
*/
public String getS3Url(String key) {
- String base = (hostname == null || hostname.isEmpty()) ? endpoint : ("https://" + hostname);
+ String base = (hostname == null || hostname.isEmpty()) ? remoteEndpoint : ("https://" + hostname);
String url = base + "/" + bucketName;
try {
for (String p : key.split("/")) {
@@ -300,7 +303,7 @@ public class S3Util {
* @return URL prefix ending with "/"
*/
public String getS3Prefix() {
- String base = (hostname == null || hostname.isEmpty()) ? endpoint : ("https://" + hostname);
+ String base = (hostname == null || hostname.isEmpty()) ? remoteEndpoint : ("https://" + hostname);
return base + "/" + bucketName + "/";
}
@@ -311,7 +314,7 @@ public class S3Util {
* @return direct URL string
*/
public String getS3UrlForKnowledge(String key) {
- String base = (hostname == null || hostname.isEmpty()) ? endpoint : ("http://" + hostname);
+ String base = (hostname == null || hostname.isEmpty()) ? remoteEndpoint : ("http://" + hostname);
return base + "/" + bucketName + "/" + key;
}
--
Gitee
From 6a1328f0ddc09a720d5ee261ffdda93c3c14bc77 Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Thu, 23 Oct 2025 14:46:35 +0800
Subject: [PATCH 73/90] feat: scope fixed and check fixed
---
.../toolkit/entity/dto/rpa/StartReq.java | 8 ++--
.../service/repo/FileInfoV2Service.java | 43 ++++++++++---------
.../service/tool/RpaAssistantService.java | 8 +++-
3 files changed, 33 insertions(+), 26 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
index 4fb93132..5e55b095 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/entity/dto/rpa/StartReq.java
@@ -8,9 +8,9 @@ import java.util.Map;
@Data
public class StartReq {
@NotBlank
- public String projectId;
- public String execPosition = "EXECUTOR";
+ private String projectId;
+ private String execPosition = "EXECUTOR";
// 可空,默认 RPA 当前启用版本
- public Integer version;
- public Map params = Map.of();
+ private Integer version;
+ private Map params = Map.of();
}
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
index 77538904..b6296ddd 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/repo/FileInfoV2Service.java
@@ -951,24 +951,7 @@ public class FileInfoV2Service extends ServiceImpl
FileInfoV2 fileInfoV2 = this.getOnly(new QueryWrapper().eq("uuid", fileId));
String source = fileInfoV2.getSource();
MysqlKnowledge knowledgeTemp = new MysqlKnowledge();
- if (ProjectContent.isCbgRagCompatible(source)) {
- BeanUtils.copyProperties(knowledge, knowledgeTemp);
- ChunkInfo chunkInfo = knowledgeTemp.getContent().toJavaObject(ChunkInfo.class);
- JSONObject references = chunkInfo.getReferences();
- Set referenceUnusedSet = new HashSet<>();
- if (!CollectionUtils.isEmpty(references)) {
- referenceUnusedSet = references.keySet();
- }
- if (!CollectionUtils.isEmpty(referenceUnusedSet)) {
- JSONObject newReference = new JSONObject();
- for (String referenceUnused : referenceUnusedSet) {
- buildNewMode(referenceUnused, references, newReference);
- }
- chunkInfo.setReferences(newReference);
- JSONObject updatedContent = (JSONObject) JSON.toJSON(chunkInfo);
- knowledgeTemp.setContent(updatedContent);
- }
- }
+ checkSourceFixed(knowledge, source, knowledgeTemp);
KnowledgeDto knowledgeDto = new KnowledgeDto();
knowledgeDtoList.add(knowledgeDto);
if (ProjectContent.isCbgRagCompatible(source)) {
@@ -991,6 +974,27 @@ public class FileInfoV2Service extends ServiceImpl
return pageData;
}
+ private static void checkSourceFixed(MysqlKnowledge knowledge, String source, MysqlKnowledge knowledgeTemp) {
+ if (ProjectContent.isCbgRagCompatible(source)) {
+ BeanUtils.copyProperties(knowledge, knowledgeTemp);
+ ChunkInfo chunkInfo = knowledgeTemp.getContent().toJavaObject(ChunkInfo.class);
+ JSONObject references = chunkInfo.getReferences();
+ Set referenceUnusedSet = new HashSet<>();
+ if (!CollectionUtils.isEmpty(references)) {
+ referenceUnusedSet = references.keySet();
+ }
+ if (!CollectionUtils.isEmpty(referenceUnusedSet)) {
+ JSONObject newReference = new JSONObject();
+ for (String referenceUnused : referenceUnusedSet) {
+ buildNewMode(referenceUnused, references, newReference);
+ }
+ chunkInfo.setReferences(newReference);
+ JSONObject updatedContent = (JSONObject) JSON.toJSON(chunkInfo);
+ knowledgeTemp.setContent(updatedContent);
+ }
+ }
+ }
+
private static void buildNewMode(String referenceUnused, JSONObject references, JSONObject newReference) {
String link = references.getString(referenceUnused);
JSONObject newReferenceV = new JSONObject();
@@ -1982,9 +1986,6 @@ public class FileInfoV2Service extends ServiceImpl
}
log.info("searchFile request url: {}", url);
- } catch (NullPointerException e) {
- log.error("Null pointer exception while constructing URL", e);
- throw new IOException("Failed to construct request URL", e);
} catch (IllegalArgumentException e) {
log.error("Invalid URL format", e);
throw new IOException("Invalid URL format", e);
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
index 0ab80c61..fa6f0006 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/tool/RpaAssistantService.java
@@ -534,7 +534,13 @@ public class RpaAssistantService {
SseEmitterUtil.EVENTSOURCE_MAP.put(sseId, es);
try {
emitter.send(SseEmitter.event().name("open").data("ok"));
- } catch (Exception ignore) {
+ } catch (Exception e) {
+ log.warn("[SSE][{}] send open event failed: {}", sseId, e.getMessage(), e);
+ SseEmitterUtil.completeWithError(emitter, "send open event failed: " + e.getMessage());
+ if (es != null) {
+ es.cancel();
+ }
+ SseEmitterUtil.EVENTSOURCE_MAP.remove(sseId);
}
}
--
Gitee
From 2f3d1e0f949452793b93a4baa22874d6ac445b66 Mon Sep 17 00:00:00 2001
From: BillorBear <2427406327@qq.com>
Date: Thu, 23 Oct 2025 15:11:29 +0800
Subject: [PATCH 74/90] feat: scope fixed and check fixed
---
.../console/toolkit/util/RedisUtil.java | 25 ++++++++++++-------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/RedisUtil.java b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/RedisUtil.java
index 9591b177..389231f4 100644
--- a/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/RedisUtil.java
+++ b/console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/util/RedisUtil.java
@@ -2,9 +2,7 @@ package com.iflytek.astron.console.toolkit.util;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.redis.core.Cursor;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
@@ -43,6 +41,8 @@ public class RedisUtil {
@Resource
private RedisTemplate redisTemplate;
+ @Resource
+ private StringRedisTemplate stringRedisTemplate;
/* ========================= Constants & Precompiled Scripts ========================= */
@@ -84,11 +84,13 @@ public class RedisUtil {
requireKey(key);
long ttl = Math.max(1, ttlSeconds);
String val = token != null ? token : UUID.randomUUID().toString();
- Boolean ok = redisTemplate.opsForValue().setIfAbsent(key, val, ttl, TimeUnit.SECONDS);
+ Boolean ok = stringRedisTemplate.opsForValue()
+ .setIfAbsent(key, val, ttl, TimeUnit.SECONDS);
log.debug("redis.tryLock key={}, ttl={}s, token={}, ok={}", key, ttl, safe(val), ok);
return Boolean.TRUE.equals(ok);
}
+
/**
* Acquire a distributed lock (with token).
*
@@ -116,11 +118,12 @@ public class RedisUtil {
public boolean renew(String key, long ttlSeconds, String token) {
requireKey(key);
Objects.requireNonNull(token, "token must not be null");
- long pttl = Math.max(1, ttlSeconds) * 1000L;
- Long ret = redisTemplate.execute(LUA_RENEW,
+ String pttl = String.valueOf(Math.max(1, ttlSeconds) * 1000L);
+ Long ret = stringRedisTemplate.execute(
+ LUA_RENEW,
Collections.singletonList(key),
- token,
- pttl);
+ token, pttl // ——全部是字符串
+ );
boolean ok = ret != null && ret > 0;
log.debug("redis.renew key={}, ttl={}s, token={}, ok={}", key, ttlSeconds, safe(token), ok);
return ok;
@@ -152,7 +155,11 @@ public class RedisUtil {
public boolean unlock(String key, String token) {
requireKey(key);
Objects.requireNonNull(token, "token must not be null");
- Long ret = redisTemplate.execute(LUA_UNLOCK, Collections.singletonList(key), token);
+ Long ret = stringRedisTemplate.execute(
+ LUA_UNLOCK,
+ Collections.singletonList(key),
+ token
+ );
boolean ok = ret != null && ret > 0;
log.debug("redis.unlock key={}, token={}, ok={}", key, safe(token), ok);
return ok;
--
Gitee
From 643267fef86f502c7ed298901437baa266c0289e Mon Sep 17 00:00:00 2001
From: BillorBear <128047331+BillorBear@users.noreply.github.com>
Date: Thu, 23 Oct 2025 15:19:10 +0800
Subject: [PATCH 75/90] Update .env.example
Signed-off-by: BillorBear <128047331+BillorBear@users.noreply.github.com>
---
docker/astronAgent/.env.example | 1 +
1 file changed, 1 insertion(+)
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index 16e729e9..b394c439 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -257,6 +257,7 @@ ADMIN_UID=9999
APP_URL=http://core-tenant:${CORE_TENANT_PORT:-5052}/v2/app
KNOWLEDGE_URL=http://core-knowledge:${CORE_KNOWLEDGE_PORT:-20010}/knowledge
TOOL_URL=http://core-link:18888
+TOOL_RPA_URL=http://rpa-server:17198
WORKFLOW_URL=http://core-workflow:${CORE_WORKFLOW_PORT:-7880}
SPARK_DB_URL=http://core-database:${CORE_DATABASE_PORT:-7990}
# Local model service address: The model service is open source,
--
Gitee
From 65d8d971a1c386a2759668be23f43c130aa95682 Mon Sep 17 00:00:00 2001
From: BillorBear <128047331+BillorBear@users.noreply.github.com>
Date: Thu, 23 Oct 2025 15:21:05 +0800
Subject: [PATCH 76/90] Update docker-compose.yaml
Signed-off-by: BillorBear <128047331+BillorBear@users.noreply.github.com>
---
docker/astronAgent/docker-compose.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index 4e8d3dae..76d63400 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -538,6 +538,7 @@ services:
APP_URL: "${APP_URL:-}"
KNOWLEDGE_URL: "${KNOWLEDGE_URL:-}"
TOOL_URL: "${TOOL_URL:-}"
+ TOOL_RPA_URL: "${TOOL_RPA_URL:-}"
WORKFLOW_URL: "${WORKFLOW_URL:-}"
SPARK_DB_URL: "${SPARK_DB_URL:-}"
LOCAL_MODEL_URL: "${LOCAL_MODEL_URL:-}"
--
Gitee
From 7c313af9787666d110864dff61ab8e5950f87cc1 Mon Sep 17 00:00:00 2001
From: BillorBear <128047331+BillorBear@users.noreply.github.com>
Date: Thu, 23 Oct 2025 15:21:59 +0800
Subject: [PATCH 77/90] Update .env.example
Signed-off-by: BillorBear <128047331+BillorBear@users.noreply.github.com>
---
docker/astronAgent/.env.example | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index b394c439..0db12409 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -257,7 +257,7 @@ ADMIN_UID=9999
APP_URL=http://core-tenant:${CORE_TENANT_PORT:-5052}/v2/app
KNOWLEDGE_URL=http://core-knowledge:${CORE_KNOWLEDGE_PORT:-20010}/knowledge
TOOL_URL=http://core-link:18888
-TOOL_RPA_URL=http://rpa-server:17198
+TOOL_RPA_URL=http://core-rpa:17198
WORKFLOW_URL=http://core-workflow:${CORE_WORKFLOW_PORT:-7880}
SPARK_DB_URL=http://core-database:${CORE_DATABASE_PORT:-7990}
# Local model service address: The model service is open source,
--
Gitee
From fb232e8d850f78bb1544389d86dfdb62e0ebb5f3 Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Thu, 23 Oct 2025 17:24:06 +0800
Subject: [PATCH 78/90] fix: let the backend use the internal network to access
casdoor to obtain jwt set by default (#399)
---
docker/astronAgent/.env.example | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/astronAgent/.env.example b/docker/astronAgent/.env.example
index 0db12409..d832a2da 100644
--- a/docker/astronAgent/.env.example
+++ b/docker/astronAgent/.env.example
@@ -202,7 +202,7 @@ REDIS_DATABASE_CONSOLE=1
# OAuth2 Configuration for Console Backend Api Server (as OAuth2 Resource Server)
OAUTH2_ISSUER_URI=${CONSOLE_CASDOOR_URL:-http://auth-server:8000}
-OAUTH2_JWK_SET_URI=${CONSOLE_CASDOOR_URL:-http://auth-server:8000}/.well-known/jwks
+OAUTH2_JWK_SET_URI=http://casdoor:8000/.well-known/jwks
OAUTH2_AUDIENCE=${CONSOLE_CASDOOR_ID:-your-oauth2-client-id}
# Open Platform API Configuration
--
Gitee
From b27991773cf94678959fb1c472ccfe7ef2071296 Mon Sep 17 00:00:00 2001
From: yjlu12
Date: Fri, 24 Oct 2025 10:37:13 +0800
Subject: [PATCH 79/90] fix:ADD RPA FUNCTION
---
docker/astronAgent/astronRPA/.env.example | 47 +
docker/astronAgent/astronRPA/QUICK_START.md | 64 +
.../astronAgent/astronRPA/docker-compose.yml | 289 ++
.../volumes/casdoor/init_data_dump.json | 2808 +++++++++++++++++
.../mysql/init_app_market_dict_data.sql | 37 +
.../volumes/mysql/init_c_atom_meta_data.sql | 1 +
.../volumes/mysql/init_his_data_enum_data.sql | 28 +
.../volumes/mysql/init_robot_example_data.sql | 14 +
.../astronRPA/volumes/mysql/my.cnf | 8 +
.../astronRPA/volumes/mysql/schema.sql | 1343 ++++++++
.../astronRPA/volumes/nginx/default.conf | 150 +
.../volumes/nginx/lua/auth_handler.lua | 165 +
.../volumes/nginx/lua/resty/http.lua | 1185 +++++++
.../volumes/nginx/lua/resty/http_connect.lua | 341 ++
.../volumes/nginx/lua/resty/http_headers.lua | 44 +
...-with-auth.yml => docker-compose-auth.yml} | 7 +
.../astronAgent/docker-compose-with-auth.yaml | 607 ++++
docker/astronAgent/docker-compose.yaml | 7 -
18 files changed, 7138 insertions(+), 7 deletions(-)
create mode 100644 docker/astronAgent/astronRPA/.env.example
create mode 100644 docker/astronAgent/astronRPA/QUICK_START.md
create mode 100644 docker/astronAgent/astronRPA/docker-compose.yml
create mode 100644 docker/astronAgent/astronRPA/volumes/casdoor/init_data_dump.json
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/init_app_market_dict_data.sql
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/init_c_atom_meta_data.sql
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/init_his_data_enum_data.sql
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/init_robot_example_data.sql
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/my.cnf
create mode 100644 docker/astronAgent/astronRPA/volumes/mysql/schema.sql
create mode 100644 docker/astronAgent/astronRPA/volumes/nginx/default.conf
create mode 100644 docker/astronAgent/astronRPA/volumes/nginx/lua/auth_handler.lua
create mode 100644 docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http.lua
create mode 100644 docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_connect.lua
create mode 100644 docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_headers.lua
rename docker/astronAgent/{docker-compose-with-auth.yml => docker-compose-auth.yml} (82%)
create mode 100644 docker/astronAgent/docker-compose-with-auth.yaml
diff --git a/docker/astronAgent/astronRPA/.env.example b/docker/astronAgent/astronRPA/.env.example
new file mode 100644
index 00000000..b77e72c1
--- /dev/null
+++ b/docker/astronAgent/astronRPA/.env.example
@@ -0,0 +1,47 @@
+# AI-Service
+LOG_LEVEL="INFO"
+LOG_DIR="/app/log"
+
+MONTHLY_GRANT_AMOUNT=100000
+AICHAT_POINTS_COST=100
+OCR_GENERAL_POINTS_COST=50
+JFBYM_POINTS_COST=10
+
+AICHAT_BASE_URL=""
+AICHAT_API_KEY=""
+
+XFYUN_APP_ID=""
+XFYUN_API_SECRET=""
+XFYUN_API_KEY=""
+
+JFBYM_ENDPOINT=""
+JFBYM_API_TOKEN=""
+
+# Mysql
+DATABASE_USERNAME="root"
+DATABASE_PASSWORD="rpa123456"
+DATABASE_HOST="rpa-opensource-mysql"
+DATABASE_PORT=3306
+DATABASE_NAME="rpa"
+ATLAS_URL=""
+
+# Redis
+REDIS_HOST="rpa-opensource-redis"
+REDIS_PORT=6379
+REDIS_DB=0
+REDIS_PASSWORD=""
+
+# Minio
+MINIO_URL="http://rpa-opensource-minio:9000"
+MINIO_BUCKET="rpa-resource"
+MINIO_AK="rpaminioadmin"
+MINIO_SK="rpaminioadmin123"
+
+# Casdoor
+CASDOOR_ENDPOINT="http://rpa-opensource-casdoor:8000"
+CASDOOR_CLIENT_ID="e3ba6fec42cfe996121f"
+CASDOOR_CLIENT-SECRET="0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd"
+CASDOOR_ORGANIZATION_NAME="example-org"
+CASDOOR_APPLICATION_NAME="example-app"
+CASDOOR_REDIRECT_URL="https://tauri.localhost/"
+CASDOOR_EXTERNAL_ENDPOINT="http://172.29.231.250:8000"
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/QUICK_START.md b/docker/astronAgent/astronRPA/QUICK_START.md
new file mode 100644
index 00000000..9e0f46de
--- /dev/null
+++ b/docker/astronAgent/astronRPA/QUICK_START.md
@@ -0,0 +1,64 @@
+# AstronRPA Quick Start Guide
+
+## 🚀 Quick Start
+
+1. **Copy environment file:**
+ ```bash
+ cp .env.example .env
+ ```
+
+2. **Start all services:**
+ ```bash
+ docker compose up -d
+ ```
+
+3. **Access services:**
+ - AI Service: http://localhost:8010
+ - OpenAPI Service: http://localhost:8020
+ - Resource Service: http://localhost:8030
+ - Robot Service: http://localhost:8040
+ - MinIO Console: http://localhost:9001
+
+## 🛑 Stop Services
+
+```bash
+docker compose stop
+```
+
+## 📋 Service Details
+
+| Service | Port | Description |
+|---------|------|-------------|
+| ai-service | 8010 | Python FastAPI AI service |
+| openapi-service | 8020 | Python FastAPI OpenAPI service |
+| resource-service | 8030 | Java Spring Boot resource service |
+| robot-service | 8040 | Java Spring Boot robot service |
+| mysql | 3306 | MySQL 8.4.6 database |
+| redis | 6379 | Redis 8.0 cache |
+| minio | 9000/9001 | MinIO object storage |
+
+## 🔧 Common Commands
+
+```bash
+# View logs
+docker compose logs -f [service-name]
+
+# Restart a service
+docker compose restart [service-name]
+
+# Rebuild and start
+docker compose up --build -d
+
+# Stop and remove volumes
+docker compose down -v
+
+# Check service status
+docker compose ps
+```
+
+## 🐛 Troubleshooting
+
+1. **Port conflicts:** Change ports in `.env` file
+2. **Permission issues:** Ensure Docker has proper permissions
+3. **Service won't start:** Check logs with `docker compose logs [service-name]`
+4. **Database issues:** Wait for MySQL to be healthy before starting other services
diff --git a/docker/astronAgent/astronRPA/docker-compose.yml b/docker/astronAgent/astronRPA/docker-compose.yml
new file mode 100644
index 00000000..e9839dbe
--- /dev/null
+++ b/docker/astronAgent/astronRPA/docker-compose.yml
@@ -0,0 +1,289 @@
+name: rpa-opensource-studio
+x-env-file: &env_file
+ - .env
+
+services:
+ mysql:
+ image: mysql:8.4.6
+ container_name: rpa-opensource-mysql
+ restart: always
+ environment:
+ MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
+ MYSQL_DATABASE: ${DATABASE_NAME}
+ env_file: *env_file
+ # ports:
+ # - "${DATABASE_PORT}:3306"
+ volumes:
+ - ./volumes/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
+ # - ./data/mysql:/var/lib/mysql
+ - ./volumes/mysql/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql
+ - ./volumes/mysql/init_app_market_dict_data.sql:/docker-entrypoint-initdb.d/02-init_data.sql
+ - ./volumes/mysql/init_c_atom_meta_data.sql:/docker-entrypoint-initdb.d/03-init_data.sql
+ - ./volumes/mysql/init_his_data_enum_data.sql:/docker-entrypoint-initdb.d/04-init_data.sql
+ healthcheck:
+ test:
+ [
+ 'CMD',
+ 'mysqladmin',
+ 'ping',
+ '-h',
+ 'localhost',
+ '-u${DATABASE_USERNAME}',
+ '-p${DATABASE_PASSWORD}',
+ ]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 30s
+ networks:
+ - rpa-opensource-network
+
+ redis:
+ image: bitnami/redis:latest
+ container_name: rpa-opensource-redis
+ restart: always
+ user: root
+ privileged: true
+ env_file: *env_file
+ environment:
+ - REDIS_AOF_ENABLED=no
+ - REDIS_PORT_NUMBER=${REDIS_PORT}
+ - REDIS_IO_THREADS=4
+ - ALLOW_EMPTY_PASSWORD=yes
+ # ports:
+ # - "${REDIS_PORT}:6379"
+ volumes:
+ - ./data/bitnami/redis:/bitnami/redis/data:rw,Z
+ command: >
+ bash -c "
+ /opt/bitnami/scripts/redis/setup.sh
+ # Set proper permissions for data directories
+ chown -R redis:redis /bitnami/redis/data
+ chmod g+s /bitnami/redis/data
+
+ exec /opt/bitnami/scripts/redis/entrypoint.sh /opt/bitnami/scripts/redis/run.sh
+ "
+ healthcheck:
+ test: ['CMD', 'redis-cli', 'ping']
+ interval: 5s
+ timeout: 10s
+ retries: 10
+ start_period: 10s
+ networks:
+ - rpa-opensource-network
+
+ minio:
+ image: minio/minio:RELEASE.2025-06-13T11-33-47Z-cpuv1
+ container_name: rpa-opensource-minio
+ user: root
+ privileged: true
+ restart: always
+ env_file: *env_file
+ # ports:
+ # - "9000:9000"
+ # - "9001:9001"
+ volumes:
+ - ./data/minio:/data
+ environment:
+ MINIO_ROOT_USER: ${MINIO_AK}
+ MINIO_ROOT_PASSWORD: ${MINIO_SK}
+ MINIO_DEFAULT_BUCKETS: ${MINIO_BUCKET}
+ entrypoint:
+ - /bin/sh
+ - -c
+ - |
+ # Run initialization in background
+ (
+ # Wait for MinIO to be ready
+ until (/usr/bin/mc alias set localminio http://localhost:9000 ${MINIO_AK} ${MINIO_SK}) do
+ echo "Waiting for MinIO to be ready..."
+ sleep 1
+ done
+
+ # Create bucket
+ /usr/bin/mc mb --ignore-existing localminio/${MINIO_BUCKET}
+
+ echo "MinIO initialization complete."
+ ) &
+
+ # Start minio server in foreground
+ exec minio server /data --console-address ":9001"
+ healthcheck:
+ test:
+ [
+ 'CMD-SHELL',
+ '/usr/bin/mc alias set health_check http://localhost:9000 ${MINIO_AK} ${MINIO_SK} && /usr/bin/mc ready health_check',
+ ]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 30s
+ networks:
+ - rpa-opensource-network
+
+ openresty-nginx:
+ image: openresty/openresty:1.27.1.1-alpine
+ container_name: rpa-opensource-openresty-nginx
+ restart: always
+ ports:
+ - "32742:80"
+ volumes:
+ - ./volumes/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
+ - ./volumes/nginx/lua:/usr/local/openresty/nginx/lua:ro
+ - ./logs/nginx:/usr/local/openresty/nginx/logs
+ depends_on:
+ - resource-service
+ - robot-service
+ - ai-service
+ - openapi-service
+ healthcheck:
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 10s
+ networks:
+ - rpa-opensource-network
+
+ casdoor:
+ image: casbin/casdoor:v2.67.0
+ container_name: rpa-opensource-casdoor
+ restart: always
+ env_file: *env_file
+ environment:
+ - driverName=mysql
+ - dataSourceName=${DATABASE_USERNAME}:${DATABASE_PASSWORD}@tcp(${DATABASE_HOST}:${DATABASE_PORT})/
+ ports:
+ - "8000:8000"
+ volumes:
+ - ./volumes/casdoor/init_data_dump.json:/init_data.json
+ depends_on:
+ mysql:
+ condition: service_healthy
+ networks:
+ - rpa-opensource-network
+
+ mysql-init:
+ image: mysql:8.4.6
+ container_name: rpa-opensource-mysql-init
+ depends_on:
+ mysql:
+ condition: service_healthy
+ resource-service:
+ condition: service_started
+ environment:
+ MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD}
+ MYSQL_DATABASE: ${DATABASE_NAME}
+ env_file: *env_file
+ volumes:
+ - ./volumes/mysql/init_robot_example_data.sql:/init_robot_example_data.sql
+ command: >
+ sh -c "
+ echo 'Waiting for robot-service to be ready...'
+ sleep 10
+ echo 'Executing robot example data initialization...'
+ mysql -h mysql -u root -p$$MYSQL_ROOT_PASSWORD --default-character-set=utf8mb4 --init-command='SET NAMES utf8mb4;' $$MYSQL_DATABASE < /init_robot_example_data.sql
+ echo 'Robot example data initialization completed!'
+ "
+ networks:
+ - rpa-opensource-network
+
+ ai-service:
+ build:
+ context: ..
+ dockerfile: backend/ai-service/Dockerfile
+ container_name: rpa-opensource-ai-service
+ restart: always
+ env_file: *env_file
+ environment:
+ - DATABASE_URL=mysql+aiomysql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@mysql:3306/${DATABASE_NAME}
+ - REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}/${REDIS_DB}
+ - AICHAT_BASE_URL=${AICHAT_BASE_URL}
+ - AICHAT_API_KEY=${AICHAT_API_KEY}
+ # ports:
+ # - "8010:8010"
+ volumes:
+ - ../backend/ai-service/app:/app/app
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ casdoor:
+ condition: service_started
+ networks:
+ - rpa-opensource-network
+
+ openapi-service:
+ build:
+ context: ..
+ dockerfile: backend/openapi-service/Dockerfile
+ container_name: rpa-opensource-openapi-service
+ restart: always
+ env_file: *env_file
+ environment:
+ - DATABASE_URL=mysql+aiomysql://${DATABASE_USERNAME}:${DATABASE_PASSWORD}@mysql:3306/${DATABASE_NAME}
+ - REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}/${REDIS_DB}
+ - AICHAT_BASE_URL=${AICHAT_BASE_URL}
+ - AICHAT_API_KEY=${AICHAT_API_KEY}
+ # ports:
+ # - "8020:8020"
+ volumes:
+ - ../backend/openapi-service/app:/app/app
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ casdoor:
+ condition: service_started
+ networks:
+ - rpa-opensource-network
+
+ resource-service:
+ build:
+ context: ..
+ dockerfile: backend/resource-service/Dockerfile
+ container_name: rpa-opensource-resource-service
+ restart: always
+ env_file: *env_file
+ # ports:
+ # - "8030:8030"
+ volumes:
+ - ./logs/resource-service:/app/logs
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ casdoor:
+ condition: service_started
+ networks:
+ - rpa-opensource-network
+
+ robot-service:
+ build:
+ context: ..
+ dockerfile: backend/robot-service/Dockerfile
+ container_name: rpa-opensource-robot-service
+ restart: always
+ env_file: *env_file
+ # ports:
+ # - "8040:8040"
+ volumes:
+ - ./logs/robot-service:/app/logs
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ casdoor:
+ condition: service_started
+ networks:
+ - rpa-opensource-network
+
+networks:
+ rpa-opensource-network:
+ driver: bridge
diff --git a/docker/astronAgent/astronRPA/volumes/casdoor/init_data_dump.json b/docker/astronAgent/astronRPA/volumes/casdoor/init_data_dump.json
new file mode 100644
index 00000000..a4ba69c9
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/casdoor/init_data_dump.json
@@ -0,0 +1,2808 @@
+{
+ "organizations": [
+ {
+ "owner": "admin",
+ "name": "example-org",
+ "createdTime": "2025-09-20T18:48:24+08:00",
+ "displayName": "示例组织",
+ "websiteUrl": "https://door.casdoor.com",
+ "logo": "",
+ "logoDark": "",
+ "favicon": "https://cdn.casbin.org/img/favicon.png",
+ "hasPrivilegeConsent": false,
+ "passwordType": "plain",
+ "passwordSalt": "",
+ "passwordOptions": [
+ "AtLeast6"
+ ],
+ "passwordObfuscatorType": "Plain",
+ "passwordObfuscatorKey": "",
+ "passwordExpireDays": 0,
+ "countryCodes": [
+ "CN"
+ ],
+ "defaultAvatar": "https://cdn.casbin.org/img/casbin.svg",
+ "defaultApplication": "example-app",
+ "userTypes": null,
+ "tags": [],
+ "languages": [
+ "en",
+ "es",
+ "fr",
+ "de",
+ "zh",
+ "id",
+ "ja",
+ "ko",
+ "ru",
+ "vi",
+ "pt",
+ "it",
+ "ms",
+ "tr",
+ "ar",
+ "he",
+ "nl",
+ "pl",
+ "fi",
+ "sv",
+ "uk",
+ "kk",
+ "fa",
+ "cs",
+ "sk",
+ "az"
+ ],
+ "themeData": null,
+ "masterPassword": "",
+ "defaultPassword": "",
+ "masterVerificationCode": "",
+ "ipWhitelist": "",
+ "initScore": 0,
+ "enableSoftDeletion": false,
+ "isProfilePublic": true,
+ "useEmailAsUsername": false,
+ "enableTour": true,
+ "disableSignin": false,
+ "ipRestriction": "",
+ "navItems": null,
+ "widgetItems": null,
+ "mfaItems": null,
+ "mfaRememberInHours": 12,
+ "accountItems": [
+ {
+ "name": "Organization",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "ID",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "Name",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Display name",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Avatar",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "User type",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Password",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Email",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Phone",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Country code",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Country/Region",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Location",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Address",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Affiliation",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Title",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "ID card type",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "ID card",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "ID card info",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Homepage",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Bio",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Tag",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Language",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Gender",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Birthday",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Education",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Score",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Karma",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Ranking",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Signup application",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "API key",
+ "visible": false,
+ "viewRule": "",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Groups",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Roles",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "Permissions",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "3rd-party logins",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Properties",
+ "visible": false,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is online",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is admin",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is forbidden",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is deleted",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Multi-factor authentication",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "WebAuthn credentials",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Managed accounts",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "MFA accounts",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ }
+ ]
+ },
+ {
+ "owner": "admin",
+ "name": "built-in",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "displayName": "Built-in Organization",
+ "websiteUrl": "https://example.com",
+ "logo": "",
+ "logoDark": "",
+ "favicon": "https://cdn.casbin.org/img/casbin/favicon.ico",
+ "hasPrivilegeConsent": false,
+ "passwordType": "plain",
+ "passwordSalt": "",
+ "passwordOptions": [
+ "AtLeast6"
+ ],
+ "passwordObfuscatorType": "",
+ "passwordObfuscatorKey": "",
+ "passwordExpireDays": 0,
+ "countryCodes": [
+ "US",
+ "ES",
+ "FR",
+ "DE",
+ "GB",
+ "CN",
+ "JP",
+ "KR",
+ "VN",
+ "ID",
+ "SG",
+ "IN"
+ ],
+ "defaultAvatar": "https://cdn.casbin.org/img/casbin.svg",
+ "defaultApplication": "",
+ "userTypes": [],
+ "tags": [],
+ "languages": [
+ "en",
+ "zh",
+ "es",
+ "fr",
+ "de",
+ "id",
+ "ja",
+ "ko",
+ "ru",
+ "vi",
+ "pt"
+ ],
+ "themeData": null,
+ "masterPassword": "",
+ "defaultPassword": "",
+ "masterVerificationCode": "",
+ "ipWhitelist": "",
+ "initScore": 2000,
+ "enableSoftDeletion": false,
+ "isProfilePublic": false,
+ "useEmailAsUsername": false,
+ "enableTour": true,
+ "disableSignin": false,
+ "ipRestriction": "",
+ "navItems": null,
+ "widgetItems": null,
+ "mfaItems": null,
+ "mfaRememberInHours": 0,
+ "accountItems": [
+ {
+ "name": "Organization",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "ID",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "Name",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Display name",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Avatar",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "User type",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Password",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Email",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Phone",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Country code",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Country/Region",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Location",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Affiliation",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Title",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Homepage",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Bio",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Tag",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Signup application",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Roles",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "Permissions",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Immutable",
+ "regex": ""
+ },
+ {
+ "name": "Groups",
+ "visible": true,
+ "viewRule": "Public",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "3rd-party logins",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Properties",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is admin",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is forbidden",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Is deleted",
+ "visible": true,
+ "viewRule": "Admin",
+ "modifyRule": "Admin",
+ "regex": ""
+ },
+ {
+ "name": "Multi-factor authentication",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "WebAuthn credentials",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "Managed accounts",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ },
+ {
+ "name": "MFA accounts",
+ "visible": true,
+ "viewRule": "Self",
+ "modifyRule": "Self",
+ "regex": ""
+ }
+ ]
+ }
+ ],
+ "applications": [
+ {
+ "owner": "admin",
+ "name": "example-app",
+ "createdTime": "2025-09-20T18:50:08+08:00",
+ "displayName": "示例应用",
+ "logo": "https://cdn.casbin.org/img/casdoor-logo_1185x256.png",
+ "order": 0,
+ "homepageUrl": "",
+ "description": "",
+ "organization": "example-org",
+ "cert": "cert-built-in",
+ "defaultGroup": "",
+ "headerHtml": "",
+ "enablePassword": true,
+ "enableSignUp": true,
+ "disableSignin": false,
+ "enableSigninSession": false,
+ "enableAutoSignin": false,
+ "enableCodeSignin": false,
+ "enableSamlCompress": false,
+ "enableSamlC14n10": false,
+ "enableSamlPostBinding": false,
+ "useEmailAsSamlNameId": false,
+ "enableWebAuthn": false,
+ "enableLinkWithEmail": false,
+ "orgChoiceMode": "",
+ "samlReplyUrl": "",
+ "providers": [
+ {
+ "owner": "",
+ "name": "provider_captcha_default",
+ "canSignUp": false,
+ "canSignIn": false,
+ "canUnlink": false,
+ "countryCodes": null,
+ "prompted": false,
+ "signupGroup": "",
+ "rule": "",
+ "provider": null
+ }
+ ],
+ "signinMethods": [
+ {
+ "name": "Password",
+ "displayName": "Password",
+ "rule": "All"
+ }
+ ],
+ "signupItems": [
+ {
+ "name": "ID",
+ "visible": false,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "Random"
+ },
+ {
+ "name": "Username",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Display name",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Password",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Confirm password",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Email",
+ "visible": false,
+ "required": false,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "No verification"
+ },
+ {
+ "name": "Phone",
+ "visible": true,
+ "required": false,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "No verification"
+ },
+ {
+ "name": "Agreement",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Signup button",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Providers",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": ".provider-img {\n width: 30px;\n margin: 5px;\n }\n .provider-big-img {\n margin-bottom: 10px;\n }\n ",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "small"
+ }
+ ],
+ "signinItems": [
+ {
+ "name": "Back button",
+ "visible": true,
+ "label": "",
+ "customCss": ".back-button {\n top: 65px;\n left: 15px;\n position: absolute;\n}\n.back-inner-button{}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Languages",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-languages {\n top: 55px;\n right: 5px;\n position: absolute;\n}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Logo",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-logo-box {}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Signin methods",
+ "visible": true,
+ "label": "",
+ "customCss": ".signin-methods {}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Username",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-username {}\n.login-username-input{}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Password",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-password {}\n.login-password-input{}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Agreement",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-agreement {}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Forgot password?",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Login button",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-button-box {\n margin-bottom: 5px;\n}\n.login-button {\n width: 100%;\n}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Signup link",
+ "visible": true,
+ "label": "",
+ "customCss": ".login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}",
+ "placeholder": "",
+ "rule": "None",
+ "isCustom": false
+ },
+ {
+ "name": "Providers",
+ "visible": true,
+ "label": "",
+ "customCss": ".provider-img {\n width: 30px;\n margin: 5px;\n}\n.provider-big-img {\n margin-bottom: 10px;\n}",
+ "placeholder": "",
+ "rule": "small",
+ "isCustom": false
+ }
+ ],
+ "grantTypes": [
+ "authorization_code",
+ "password",
+ "client_credentials",
+ "token",
+ "id_token",
+ "refresh_token"
+ ],
+ "organizationObj": null,
+ "certPublicKey": "",
+ "tags": [],
+ "samlAttributes": null,
+ "isShared": false,
+ "ipRestriction": "",
+ "clientId": "e3ba6fec42cfe996121f",
+ "clientSecret": "0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd",
+ "redirectUris": [
+ "https://tauri.localhost/","http://localhost:1420/"
+ ],
+ "forcedRedirectOrigin": "",
+ "tokenFormat": "JWT",
+ "tokenSigningMethod": "",
+ "tokenFields": [],
+ "tokenAttributes": null,
+ "expireInHours": 168,
+ "refreshExpireInHours": 168,
+ "signupUrl": "",
+ "signinUrl": "",
+ "forgetUrl": "",
+ "affiliationUrl": "",
+ "ipWhitelist": "",
+ "termsOfUse": "",
+ "signupHtml": "",
+ "signinHtml": "",
+ "themeData": null,
+ "footerHtml": "",
+ "formCss": "",
+ "formCssMobile": "",
+ "formOffset": 2,
+ "formSideHtml": "",
+ "formBackgroundUrl": "",
+ "formBackgroundUrlMobile": "",
+ "failedSigninLimit": 5,
+ "failedSigninFrozenTime": 15
+ },
+ {
+ "owner": "admin",
+ "name": "app-built-in",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "displayName": "Casdoor",
+ "logo": "https://cdn.casbin.org/img/casdoor-logo_1185x256.png",
+ "order": 0,
+ "homepageUrl": "https://casdoor.org",
+ "description": "",
+ "organization": "built-in",
+ "cert": "cert-built-in",
+ "defaultGroup": "",
+ "headerHtml": "",
+ "enablePassword": true,
+ "enableSignUp": true,
+ "disableSignin": false,
+ "enableSigninSession": false,
+ "enableAutoSignin": false,
+ "enableCodeSignin": false,
+ "enableSamlCompress": false,
+ "enableSamlC14n10": false,
+ "enableSamlPostBinding": false,
+ "useEmailAsSamlNameId": false,
+ "enableWebAuthn": false,
+ "enableLinkWithEmail": false,
+ "orgChoiceMode": "",
+ "samlReplyUrl": "",
+ "providers": [
+ {
+ "owner": "",
+ "name": "provider_captcha_default",
+ "canSignUp": false,
+ "canSignIn": false,
+ "canUnlink": false,
+ "countryCodes": null,
+ "prompted": false,
+ "signupGroup": "",
+ "rule": "None",
+ "provider": null
+ }
+ ],
+ "signinMethods": [
+ {
+ "name": "Password",
+ "displayName": "Password",
+ "rule": "All"
+ },
+ {
+ "name": "Verification code",
+ "displayName": "Verification code",
+ "rule": "All"
+ },
+ {
+ "name": "WebAuthn",
+ "displayName": "WebAuthn",
+ "rule": "None"
+ },
+ {
+ "name": "Face ID",
+ "displayName": "Face ID",
+ "rule": "None"
+ }
+ ],
+ "signupItems": [
+ {
+ "name": "ID",
+ "visible": false,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "Random"
+ },
+ {
+ "name": "Username",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Display name",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Password",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Confirm password",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Email",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "Normal"
+ },
+ {
+ "name": "Phone",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ },
+ {
+ "name": "Agreement",
+ "visible": true,
+ "required": true,
+ "prompted": false,
+ "type": "",
+ "customCss": "",
+ "label": "",
+ "placeholder": "",
+ "options": null,
+ "regex": "",
+ "rule": "None"
+ }
+ ],
+ "signinItems": null,
+ "grantTypes": null,
+ "organizationObj": null,
+ "certPublicKey": "",
+ "tags": [],
+ "samlAttributes": null,
+ "isShared": false,
+ "ipRestriction": "",
+ "clientId": "1d1eb3c891fa8faa7d63",
+ "clientSecret": "4d623f9beb97dd8263bbf3147e194ad3b0b1c9e2",
+ "redirectUris": [],
+ "forcedRedirectOrigin": "",
+ "tokenFormat": "JWT",
+ "tokenSigningMethod": "",
+ "tokenFields": [],
+ "tokenAttributes": null,
+ "expireInHours": 168,
+ "refreshExpireInHours": 0,
+ "signupUrl": "",
+ "signinUrl": "",
+ "forgetUrl": "",
+ "affiliationUrl": "",
+ "ipWhitelist": "",
+ "termsOfUse": "",
+ "signupHtml": "",
+ "signinHtml": "",
+ "themeData": null,
+ "footerHtml": "",
+ "formCss": "",
+ "formCssMobile": "",
+ "formOffset": 2,
+ "formSideHtml": "",
+ "formBackgroundUrl": "",
+ "formBackgroundUrlMobile": "",
+ "failedSigninLimit": 0,
+ "failedSigninFrozenTime": 0
+ }
+ ],
+ "users": [
+ {
+ "owner": "example-org",
+ "name": "example-user",
+ "createdTime": "2025-09-20T10:56:21Z",
+ "updatedTime": "2025-09-20T10:57:09Z",
+ "deletedTime": "",
+ "id": "3d7cca2b-8da9-41bf-b535-6c9f83d731db",
+ "externalId": "",
+ "type": "normal-user",
+ "password": "123456",
+ "passwordSalt": "",
+ "passwordType": "plain",
+ "displayName": "示例用户",
+ "firstName": "",
+ "lastName": "",
+ "avatar": "https://cdn.casbin.org/img/casbin.svg",
+ "avatarType": "",
+ "permanentAvatar": "",
+ "email": "",
+ "emailVerified": false,
+ "phone": "18888888888",
+ "countryCode": "CN",
+ "region": "",
+ "location": "",
+ "address": [],
+ "affiliation": "",
+ "title": "",
+ "idCardType": "",
+ "idCard": "",
+ "homepage": "",
+ "bio": "",
+ "tag": "",
+ "language": "",
+ "gender": "",
+ "birthday": "",
+ "education": "",
+ "score": 0,
+ "karma": 0,
+ "ranking": 1,
+ "balance": 0,
+ "currency": "",
+ "isDefaultAvatar": false,
+ "isOnline": false,
+ "isAdmin": false,
+ "isForbidden": false,
+ "isDeleted": false,
+ "signupApplication": "example-app",
+ "hash": "",
+ "preHash": "",
+ "accessKey": "",
+ "accessSecret": "",
+ "accessToken": "",
+ "createdIp": "",
+ "lastSigninTime": "",
+ "lastSigninIp": "",
+ "github": "",
+ "google": "",
+ "qq": "",
+ "wechat": "",
+ "facebook": "",
+ "dingtalk": "",
+ "weibo": "",
+ "gitee": "",
+ "linkedin": "",
+ "wecom": "",
+ "lark": "",
+ "gitlab": "",
+ "adfs": "",
+ "baidu": "",
+ "alipay": "",
+ "casdoor": "",
+ "infoflow": "",
+ "apple": "",
+ "azuread": "",
+ "azureadb2c": "",
+ "slack": "",
+ "steam": "",
+ "bilibili": "",
+ "okta": "",
+ "douyin": "",
+ "kwai": "",
+ "line": "",
+ "amazon": "",
+ "auth0": "",
+ "battlenet": "",
+ "bitbucket": "",
+ "box": "",
+ "cloudfoundry": "",
+ "dailymotion": "",
+ "deezer": "",
+ "digitalocean": "",
+ "discord": "",
+ "dropbox": "",
+ "eveonline": "",
+ "fitbit": "",
+ "gitea": "",
+ "heroku": "",
+ "influxcloud": "",
+ "instagram": "",
+ "intercom": "",
+ "kakao": "",
+ "lastfm": "",
+ "mailru": "",
+ "meetup": "",
+ "microsoftonline": "",
+ "naver": "",
+ "nextcloud": "",
+ "onedrive": "",
+ "oura": "",
+ "patreon": "",
+ "paypal": "",
+ "salesforce": "",
+ "shopify": "",
+ "soundcloud": "",
+ "spotify": "",
+ "strava": "",
+ "stripe": "",
+ "tiktok": "",
+ "tumblr": "",
+ "twitch": "",
+ "twitter": "",
+ "typetalk": "",
+ "uber": "",
+ "vk": "",
+ "wepay": "",
+ "xero": "",
+ "yahoo": "",
+ "yammer": "",
+ "yandex": "",
+ "zoom": "",
+ "metamask": "",
+ "web3onboard": "",
+ "custom": "",
+ "webauthnCredentials": null,
+ "preferredMfaType": "",
+ "recoveryCodes": null,
+ "totpSecret": "",
+ "mfaPhoneEnabled": false,
+ "mfaEmailEnabled": false,
+ "invitation": "",
+ "invitationCode": "",
+ "faceIds": null,
+ "ldap": "",
+ "properties": {},
+ "roles": null,
+ "permissions": null,
+ "groups": [],
+ "lastChangePasswordTime": "",
+ "lastSigninWrongTime": "",
+ "signinWrongTimes": 0,
+ "managedAccounts": null,
+ "mfaAccounts": null,
+ "mfaItems": null,
+ "mfaRememberDeadline": "",
+ "needUpdatePassword": false,
+ "ipWhitelist": ""
+ },
+ {
+ "owner": "built-in",
+ "name": "admin",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "updatedTime": "",
+ "deletedTime": "",
+ "id": "1e3b4ee5-7164-47c4-a421-a2ff4938c765",
+ "externalId": "",
+ "type": "normal-user",
+ "password": "123",
+ "passwordSalt": "",
+ "passwordType": "plain",
+ "displayName": "Admin",
+ "firstName": "",
+ "lastName": "",
+ "avatar": "https://cdn.casbin.org/img/casbin.svg",
+ "avatarType": "",
+ "permanentAvatar": "",
+ "email": "admin@example.com",
+ "emailVerified": false,
+ "phone": "12345678910",
+ "countryCode": "US",
+ "region": "",
+ "location": "",
+ "address": [],
+ "affiliation": "Example Inc.",
+ "title": "",
+ "idCardType": "",
+ "idCard": "",
+ "homepage": "",
+ "bio": "",
+ "tag": "staff",
+ "language": "",
+ "gender": "",
+ "birthday": "",
+ "education": "",
+ "score": 2000,
+ "karma": 0,
+ "ranking": 1,
+ "balance": 0,
+ "currency": "",
+ "isDefaultAvatar": false,
+ "isOnline": false,
+ "isAdmin": true,
+ "isForbidden": false,
+ "isDeleted": false,
+ "signupApplication": "app-built-in",
+ "hash": "",
+ "preHash": "",
+ "accessKey": "",
+ "accessSecret": "",
+ "accessToken": "",
+ "createdIp": "127.0.0.1",
+ "lastSigninTime": "",
+ "lastSigninIp": "",
+ "github": "",
+ "google": "",
+ "qq": "",
+ "wechat": "",
+ "facebook": "",
+ "dingtalk": "",
+ "weibo": "",
+ "gitee": "",
+ "linkedin": "",
+ "wecom": "",
+ "lark": "",
+ "gitlab": "",
+ "adfs": "",
+ "baidu": "",
+ "alipay": "",
+ "casdoor": "",
+ "infoflow": "",
+ "apple": "",
+ "azuread": "",
+ "azureadb2c": "",
+ "slack": "",
+ "steam": "",
+ "bilibili": "",
+ "okta": "",
+ "douyin": "",
+ "kwai": "",
+ "line": "",
+ "amazon": "",
+ "auth0": "",
+ "battlenet": "",
+ "bitbucket": "",
+ "box": "",
+ "cloudfoundry": "",
+ "dailymotion": "",
+ "deezer": "",
+ "digitalocean": "",
+ "discord": "",
+ "dropbox": "",
+ "eveonline": "",
+ "fitbit": "",
+ "gitea": "",
+ "heroku": "",
+ "influxcloud": "",
+ "instagram": "",
+ "intercom": "",
+ "kakao": "",
+ "lastfm": "",
+ "mailru": "",
+ "meetup": "",
+ "microsoftonline": "",
+ "naver": "",
+ "nextcloud": "",
+ "onedrive": "",
+ "oura": "",
+ "patreon": "",
+ "paypal": "",
+ "salesforce": "",
+ "shopify": "",
+ "soundcloud": "",
+ "spotify": "",
+ "strava": "",
+ "stripe": "",
+ "tiktok": "",
+ "tumblr": "",
+ "twitch": "",
+ "twitter": "",
+ "typetalk": "",
+ "uber": "",
+ "vk": "",
+ "wepay": "",
+ "xero": "",
+ "yahoo": "",
+ "yammer": "",
+ "yandex": "",
+ "zoom": "",
+ "metamask": "",
+ "web3onboard": "",
+ "custom": "",
+ "webauthnCredentials": null,
+ "preferredMfaType": "",
+ "recoveryCodes": null,
+ "totpSecret": "",
+ "mfaPhoneEnabled": false,
+ "mfaEmailEnabled": false,
+ "invitation": "",
+ "invitationCode": "",
+ "faceIds": null,
+ "ldap": "",
+ "properties": {},
+ "roles": null,
+ "permissions": null,
+ "groups": null,
+ "lastChangePasswordTime": "",
+ "lastSigninWrongTime": "",
+ "signinWrongTimes": 0,
+ "managedAccounts": null,
+ "mfaAccounts": null,
+ "mfaItems": null,
+ "mfaRememberDeadline": "",
+ "needUpdatePassword": false,
+ "ipWhitelist": ""
+ }
+ ],
+ "certs": [
+ {
+ "owner": "admin",
+ "name": "cert-built-in",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "displayName": "Built-in Cert",
+ "scope": "JWT",
+ "type": "x509",
+ "cryptoAlgorithm": "RS256",
+ "bitSize": 4096,
+ "expireInYears": 20,
+ "certificate": "-----BEGIN CERTIFICATE-----\nMIIE3TCCAsWgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMCgxDjAMBgNVBAoTBWFk\nbWluMRYwFAYDVQQDEw1jZXJ0LWJ1aWx0LWluMB4XDTI1MDkyMDEwNDcxOFoXDTQ1\nMDkyMDEwNDcxOFowKDEOMAwGA1UEChMFYWRtaW4xFjAUBgNVBAMTDWNlcnQtYnVp\nbHQtaW4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDL9L2vPZEXUP4z\nmbVhlgpQx35HzCvQGr+Hbi+ENf5rUadq2X8JVJ5Js3vsi/dMXoXuw2wScltgayEp\nOuyh775uXC/gOrKgfnwANE9VRU9jwBdpZcLeQBfZB2gVQZDYt2wZBSpCwp78I2AS\neWgpvBDXDTp8fV2bJBu7yaLPTdysXVCpdHEkEan7xCAY9yHl9psiqn8aVD4f9T+U\n/oHRIvyAVLpxgQ6w/Lsk2Zcw7lI4feRlF0PYQouoof91gnOIjkYEyFvF/imuL+Xg\nQuA6yMfJ04lfLt8lwu3jAwGw7VIA8LE2pQ6DmJ4pEbFBMU3H626HNel1dxEjW/j+\nTv47nkCPpXGI0w17fc54fbZ7VmtZ6LizxkBfhWf+j8Rm3hpMr6tJ6ISjUajyB17U\n1w9QWU9pOJa/cehOVczEehQLjhX5MZGAUovlmMNojKAtOO/tdcRsDMwOHJyXBqHa\noCykyJ00eIguTtmTTboAqdx3cpom8wNjRh5sUB+YkpsYTVVe/a5r0olfZHDos5bf\nknJXi9X+ppD7nvoCtwrki6AH5Dw9prsrql3sagUHgBKBVLG3EIhDIlkRTQDOO55C\n8OzuyVbDkIbcEX8BIFj8yoUAP/DcpazHHOGhjHRRcFvVHAGRtz8fH6qB9hX3N3a+\nTevIZoANOYLgN6ACWJ0bODLAO9pwWwIDAQABoxAwDjAMBgNVHRMBAf8EAjAAMA0G\nCSqGSIb3DQEBCwUAA4ICAQAT63JAwjc151XNWbMSr5ClNGd+bwEx+zDnQh/YD8V7\n8Wqj2E4r8B+wysDVu7YdOQkoIjtlFOLACg0469F7oC+kPi6N8vMFFZ+xXWk6qLed\nOFYsdUyH+l643GfMprFMA1qbJvqcOnymggeMA1rSJMu9PJ5g7WfoEg6UTKh7H9HV\nvHvgVzLweiZCS37FCC5+RDt/rOCzIrI2DfwDbf4CLKXft7mr3mnA3kVRuT+ufI9X\n+e6UqmM7EBFYeZAHi8/GXIQ8/j34ag5hiIISMhjuFYKEfMtqLYUCTH6X7NTGbGrv\nAs8YB2x5QnUj89N7IRv2hr1uJnaZOIx5feLRmuRmo35scLopAnNu0VT75TOS0Ifh\nyrouqlep4grk4DMFSWEMNodpkHHLr4pOVE8vCYphli75/beOkiilUpxGI6/osKKo\nSOC5CuC6hDarVBF3q6zpNEnqalQf9Vf2d4zeCKMfV2IuUOqKRaRFpVZfVJsbiYTi\nYF1VUCTFdfrIFsC5V9LknxvLyq8ubzpXHnIV6m/l95OxxCEDvI47l5RmwkblKk57\nH9IRmr6l66Ar2WRGQYAxpiOz3aF8IMg8f0+Vk91FNTsgNrSsk9uovQrrDDtm3KyO\nEzhOO4NA9WgaE1c6GzZD87GhrZukHWHE4KvTpkiQ3pGmTqUjIHvon+0lKtG8E5fz\nKg==\n-----END CERTIFICATE-----\n",
+ "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAy/S9rz2RF1D+M5m1YZYKUMd+R8wr0Bq/h24vhDX+a1Gnatl/\nCVSeSbN77Iv3TF6F7sNsEnJbYGshKTrsoe++blwv4DqyoH58ADRPVUVPY8AXaWXC\n3kAX2QdoFUGQ2LdsGQUqQsKe/CNgEnloKbwQ1w06fH1dmyQbu8miz03crF1QqXRx\nJBGp+8QgGPch5fabIqp/GlQ+H/U/lP6B0SL8gFS6cYEOsPy7JNmXMO5SOH3kZRdD\n2EKLqKH/dYJziI5GBMhbxf4pri/l4ELgOsjHydOJXy7fJcLt4wMBsO1SAPCxNqUO\ng5ieKRGxQTFNx+tuhzXpdXcRI1v4/k7+O55Aj6VxiNMNe33OeH22e1ZrWei4s8ZA\nX4Vn/o/EZt4aTK+rSeiEo1Go8gde1NcPUFlPaTiWv3HoTlXMxHoUC44V+TGRgFKL\n5ZjDaIygLTjv7XXEbAzMDhyclwah2qAspMidNHiILk7Zk026AKncd3KaJvMDY0Ye\nbFAfmJKbGE1VXv2ua9KJX2Rw6LOW35JyV4vV/qaQ+576ArcK5IugB+Q8Paa7K6pd\n7GoFB4ASgVSxtxCIQyJZEU0AzjueQvDs7slWw5CG3BF/ASBY/MqFAD/w3KWsxxzh\noYx0UXBb1RwBkbc/Hx+qgfYV9zd2vk3ryGaADTmC4DegAlidGzgywDvacFsCAwEA\nAQKCAgEAtERUJ4BuLkKa+3afF2qrIWzB06nFC8GoiYY9H0kt3yMjq1AjdVbCNPgb\nzx6C7JAbJsa5TbCfzR/DBpMbNaIWGasHcdPPsAU7il6xw/dnzQ2qY7DaxN+3dE6U\nkz0JTlMIizDCgpFMPiTyNEH0a/bal4kMqZ2Qz5/hl2AHs9zo77vmoG/X1H58VJer\nmwVLg9sskT5K6zWMV2jH0uQET5nxvWemBs5/8rTeoBpmBIyQRXgYF7WxdIKUt+6/\nQNiVTxwZDP8eBmi35EpXjpjtYWe3Fk8O+v8Nom2hHuW4Z+3KbiRPLbJDmtKY8Em/\n9pQiYFJZtc5T00vy7OLMt9GP6ZfdDMXHqGT/VFJuYNQhRWgaf9JE5zZuQ3O/D1BH\nAbY0+a825DcOn2XqP3KlHOR1CJyFhphZDowxg2pjhQ548+30eQ9/IoTvgrEnReRW\n3/USVvcUA4lgmzKv50BLiy9wK55FVglafdRm1fq3ykQwAZ/ok4ah2U+ZA98Hh8/V\n4xZ/KSBlNxSfBEPKlPkSBBrwqUwZtrwcHUg0qiRrJGvGxqKPUb0kOQmQjX2VNPoU\nPYzHO9G83RFeJU5KjMj9+apXV2qSL1m8wFJ/aHraUoaOhQ7AzETY0H8r0EbBunq3\nQ8s3Foeb3/p9DOxZxODaDKYDKHM43h1Oro+JjXy+21KSBBjAQrkCggEBAOZzNUlB\nayl/c6ynVlaYR2UGPdbymbAu8dB2Yj/OFjKQgWl2pKVpjUvva7A8p0smU+oC3fVQ\n9qBT6834N8jUqKUCxkHnVw1UDhXPJofqOkaLwf4cLL3X1PbPShaZbEM45YZqmU/c\npihL/myVcXFxntzQZ9NxzJU1wu4uwII0jd1ciFNssulxobgN0JDLM3jJ+Vf1yFUC\nJgCscZcRWBb0HW1IxHVic3GPEzg1hwgyt9V+e/xdKuy4+LQuPPsVBiy5lsVBtwXY\nN5Ve2k9dp0etnoQE+xjdcDwJVNgD46C4nw8e177u20cDyevILbxCRlSJ/ptQXgVu\nc3UZ9o9Y51N9HfUCggEBAOKRj7vGLhUf0Tv7+qroktTaj6jFUQr+JFQKuOTAHo1C\nghyIAtDjDPl3Be+DWPrvXSswEjOqI/haTjxUmTQgJEmuQEfebE7n0fOf25jcCFwb\nIEFgzvLJSvEiZX7y8nPFffP5TSzS/gxiqNZVJ0im3aAYUK48DXY+6Sg+xhOAPpzY\nysLifc6P2WlGt3OntuMYYgCqGwmlnRNVADdlqt5EH/mRRuxjqGQqXovozlQ8UBTL\nDX4fDLjkcqdG22mOD0qscvsG36LTjpcERposjQ6Qa6QFkGoNd5qYTILW3oq36NsJ\nevucMPSK6HWEbCkrC9Z6Q+PSuILHRNWJSThkAuzKkw8CggEAb6BEomxWvS4oWOxh\njOaMRqokUDcJLOdAaKq/YoqwA+QtW2mFzT34nFynvCFVI7i4EvU6kHacUAL2iLmA\nQ/6Ghg92+ztU1nbtr7C8yD8z5TITUMRTA85FMRwtlg7Q+yrXOyntg1qs/X36CpzE\n65+OxQUKFcjcwTXea0MoKqnMQfptaoOPkjZhkGbYrRpQn2SuK+Y5GLxGrjLZfsR+\n9/ddPa9uwjFjHBGizKpY8yamF3sCEbcLcMkUZyqyjSic6hMnrfrr7Z/TJL5iXulN\nexHlY6uJ+XxhviMC/vO7UgG7wjY9aRYIDzkNmPFI/hTYPmDtfEwMjvL2aDWgUcVN\noApN9QKCAQEAyhT21I7BD4pff1cSj1n9jOicdfX4gQuIr4UYwL8zAN+vWW9ew52g\nNumYW7cVqEvTF/A6a+Z3Ss6RNXJna3y3oRhQsUmL5R0TwG522XJ36l8vd+C29Qnh\nVA5P5Nkgs24VF4Tm9vICMl3VJcax0TU0O9U0MRPTFgKqx4Cl/0LFlfQvdX+6ooDf\nc+zlN70BfLCEyP7wOryCy3lnRgHiU3kD4/9V+QYybZT022l8jtl0u/cYQ8PB/y+T\nq+uhTBavQPVrYMcStRJo/f2MU3slHTZnK9biphT49uScaZ7ow2WhxaxBCyaW66by\nC89fAaEpX9WRtCSA+fRuSt+2dRuPGFDetQKCAQBNxBbtOrw79tpq7NaSWlylTR+K\nQJeUSol1NpktS17dLesFc6c4vVc90tOrQvmK9MoOdYpk7/ZMpIpXtgtoegEnN34r\nbfyb+O4UeOi44Y/cKr1Av3bZmp2lRQtTXZJkRlRI5kowVAHOP70WWytcfIYW+zbZ\nOSRiQOT2UFaIQjdZml9jvD/Zhr8TuLPZoaRuhWVLID0LZrix9ivYD028uHoiONl3\nbmzNhqTlcGC/skh5hn6ohEyizvHLIrpbUPK66xOWjcheFi+wKfGpKOZxt325y64D\nhQkmJquirnONmiUNuKWIUxOkbC/spnrAJ72dStfEo2V59hG5jitTlmoaXAo+\n-----END RSA PRIVATE KEY-----\n"
+ }
+ ],
+ "providers": [
+ {
+ "owner": "admin",
+ "name": "provider_captcha_default",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "displayName": "Captcha Default",
+ "category": "Captcha",
+ "type": "Default",
+ "subType": "",
+ "method": "",
+ "clientId": "",
+ "clientSecret": "",
+ "clientId2": "",
+ "clientSecret2": "",
+ "cert": "",
+ "customAuthUrl": "",
+ "customTokenUrl": "",
+ "customUserInfoUrl": "",
+ "customLogo": "",
+ "scopes": "",
+ "userMapping": null,
+ "httpHeaders": null,
+ "host": "",
+ "port": 0,
+ "disableSsl": false,
+ "title": "",
+ "content": "",
+ "receiver": "",
+ "regionId": "",
+ "signName": "",
+ "templateCode": "",
+ "appId": "",
+ "endpoint": "",
+ "intranetEndpoint": "",
+ "domain": "",
+ "bucket": "",
+ "pathPrefix": "",
+ "metadata": "",
+ "idP": "",
+ "issuerUrl": "",
+ "enableSignAuthnRequest": false,
+ "emailRegex": "",
+ "providerUrl": ""
+ }
+ ],
+ "ldaps": [
+ {
+ "id": "ldap-built-in",
+ "owner": "built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "serverName": "BuildIn LDAP Server",
+ "host": "example.com",
+ "port": 389,
+ "enableSsl": false,
+ "allowSelfSignedCert": false,
+ "username": "cn=buildin,dc=example,dc=com",
+ "password": "123",
+ "baseDn": "ou=BuildIn,dc=example,dc=com",
+ "filter": "",
+ "filterFields": null,
+ "defaultGroup": "",
+ "passwordType": "",
+ "autoSync": 0,
+ "lastSync": ""
+ }
+ ],
+ "models": [
+ {
+ "owner": "built-in",
+ "name": "api-model-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "displayName": "API Model",
+ "description": "",
+ "modelText": "[request_definition]\nr = subOwner, subName, method, urlPath, objOwner, objName\n\n[policy_definition]\np = subOwner, subName, method, urlPath, objOwner, objName\n\n[role_definition]\ng = _, _\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n[matchers]\nm = (r.subOwner == p.subOwner || p.subOwner == \"*\") \u0026\u0026 \\\n (r.subName == p.subName || p.subName == \"*\" || r.subName != \"anonymous\" \u0026\u0026 p.subName == \"!anonymous\") \u0026\u0026 \\\n (r.method == p.method || p.method == \"*\") \u0026\u0026 \\\n (r.urlPath == p.urlPath || p.urlPath == \"*\") \u0026\u0026 \\\n (r.objOwner == p.objOwner || p.objOwner == \"*\") \u0026\u0026 \\\n (r.objName == p.objName || p.objName == \"*\") || \\\n (r.subOwner == r.objOwner \u0026\u0026 r.subName == r.objName)"
+ },
+ {
+ "owner": "built-in",
+ "name": "user-model-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "displayName": "Built-in Model",
+ "description": "",
+ "modelText": "[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n[role_definition]\ng = _, _\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n[matchers]\nm = g(r.sub, p.sub) \u0026\u0026 r.obj == p.obj \u0026\u0026 r.act == p.act"
+ }
+ ],
+ "permissions": [
+ {
+ "owner": "built-in",
+ "name": "permission-built-in",
+ "createdTime": "2025-09-20T10:47:17Z",
+ "displayName": "Built-in Permission",
+ "description": "Built-in Permission",
+ "users": [
+ "built-in/*"
+ ],
+ "groups": [],
+ "roles": [],
+ "domains": [],
+ "model": "user-model-built-in",
+ "adapter": "",
+ "resourceType": "Application",
+ "resources": [
+ "app-built-in"
+ ],
+ "actions": [
+ "Read",
+ "Write",
+ "Admin"
+ ],
+ "effect": "Allow",
+ "isEnabled": true,
+ "submitter": "admin",
+ "approver": "admin",
+ "approveTime": "2025-09-20T10:47:17Z",
+ "state": "Approved"
+ }
+ ],
+ "payments": [],
+ "products": [],
+ "resources": [],
+ "roles": [
+ {
+ "owner": "example-org",
+ "name": "example-role",
+ "createdTime": "2025-09-20T18:58:05+08:00",
+ "displayName": "示例角色",
+ "description": "",
+ "users": [
+ "example-org/example-user"
+ ],
+ "groups": [],
+ "roles": [],
+ "domains": [],
+ "isEnabled": true
+ },
+ {
+ "owner": "built-in",
+ "name": "role_m06bnm",
+ "createdTime": "2025-09-20T18:57:13+08:00",
+ "displayName": "New Role - m06bnm",
+ "description": "",
+ "users": [],
+ "groups": [],
+ "roles": [],
+ "domains": [],
+ "isEnabled": true
+ }
+ ],
+ "syncers": [],
+ "tokens": [
+ {
+ "owner": "admin",
+ "name": "3511665e-fe71-4497-8a96-a2f298e3751d",
+ "createdTime": "2025-09-20T10:59:43Z",
+ "application": "app-built-in",
+ "organization": "built-in",
+ "user": "admin",
+ "code": "7f586f871be50a1b2671",
+ "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNlcnQtYnVpbHQtaW4iLCJ0eXAiOiJKV1QifQ.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoiYWNjZXNzLXRva2VuIiwidGFnIjoic3RhZmYiLCJzY29wZSI6InByb2ZpbGUiLCJhenAiOiIxZDFlYjNjODkxZmE4ZmFhN2Q2MyIsImlzcyI6Imh0dHA6Ly8xNzIuMzAuMzQuMTg0OjgwMDQiLCJzdWIiOiIxZTNiNGVlNS03MTY0LTQ3YzQtYTQyMS1hMmZmNDkzOGM3NjUiLCJhdWQiOlsiMWQxZWIzYzg5MWZhOGZhYTdkNjMiXSwiZXhwIjoxNzU4OTcwNzgzLCJuYmYiOjE3NTgzNjU5ODMsImlhdCI6MTc1ODM2NTk4MywianRpIjoiYWRtaW4vMzUxMTY2NWUtZmU3MS00NDk3LThhOTYtYTJmMjk4ZTM3NTFkIn0.TeqMA2waDtY2CQYNEkMdOdNHzXBpxdk203qMnu_6EbVgR4dEdEmEqaJ4wn-oiLvdTgoMfZ_Fb4De4iyp8jRNYdTMylo82RhcF4jsU_C8sLzeE6tENPcVQ3xJq7oOFtLCWa2V0lmE3SuypJtI2Vki5AEOds9en2Y1RCCryug6LgVo_94MnmCWlQW3pYZG5cmrKVGHBlyIK0Nw01IBBxfkPOga1s50QZaCR3Cva8SjaoY1Cx-AtU5vKzHRjp63bfrhP9JXfeP2xrL0ZyZ6LBHsCsA_1FyojAndZCNahCpAZH0_s6I0nJan0NLkBZhR89TE-Ys5SygvgkofSsyNJ7jhLh21oTdvpheUFDOqNtjBE36Et1EUFsm3jc88fRUsgzArR4pg_6YPybSMJeUrTtgpngBi-yKTs3jOo3hUszMixJrE1kXyVqMeesyrAt-HJWLBh_wx8L5BfwsgZgCRwULvbMRCH33Jfn3hG9jxu9DoLM9OvBm7HqPWXhioGG_4RII0PE9QBmN2cIPfJokf6f_FgO8O-kvwH5LUhW5HpiNJar6oOdn7aamkpuI7BBEn6YxF7c-qr1scVGBum46wQRSJ4Hl_pMwGZ1uyB76zXSaM2nqRO4MX3g7vaySq1FrL68JpYizW8ejDnuSqovVhyDwgJ_iEVbP5z7aDicOemrgoN-I",
+ "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoicmVmcmVzaC10b2tlbiIsInRhZyI6InN0YWZmIiwic2NvcGUiOiJwcm9maWxlIiwiYXpwIjoiMWQxZWIzYzg5MWZhOGZhYTdkNjMiLCJpc3MiOiJodHRwOi8vMTcyLjMwLjM0LjE4NDo4MDA0Iiwic3ViIjoiMWUzYjRlZTUtNzE2NC00N2M0LWE0MjEtYTJmZjQ5MzhjNzY1IiwiYXVkIjpbIjFkMWViM2M4OTFmYThmYWE3ZDYzIl0sImV4cCI6MTc1ODk3MDc4MywibmJmIjoxNzU4MzY1OTgzLCJpYXQiOjE3NTgzNjU5ODMsImp0aSI6ImFkbWluLzM1MTE2NjVlLWZlNzEtNDQ5Ny04YTk2LWEyZjI5OGUzNzUxZCJ9.vTiXef-KdyuwQdMEdReDdw5x4DDtSPRCLp5iDNlKaUTY5zWzUixGXU3GlYyNWqtjB8tl1saENUsvfD8k7womxdxJLnbpBu_DgQePF3woEeTaGrs27vIncbdi8ywUb_I8IEuYhjavi3hvaN5rXqouVr09sLZrbu6E65RBw4LM6GEQcUkwxUqd-qIhtV2p49ip9p2SIsvhe4BxVkbjg2EhbaS9Alf4uBeXl_vgjG9vEKj4vr3mol4yyVuwnr-rYYPjPL2QFsJfrUQCxZI9xpDeKgfoKNpmarPHcwxWK5wKcLCSCfc3Nhvnr_iQWr-lW0ZhkgUCbfATljfYSy_Vp6Ro_sSu9mw3Bke35wKt28nOMBQ2f1bx9Inhoi4ocZvV9AShtS56WXgHl5Ts7oM0HG3Z2LcYh-lCEPqTDjG-LW1R9xxaBFRJeV14g_YlUD_uTECjT94xaQs0eqUJHXebE3QJzHt2-rFagZV2iG-QDZDmij3f8VjLcG2zYG1EXfMR5H76xOu7Xyk5YnV5WQYECu_njTSbb-AEdt8syaqzM5LIvrN4Mp0dzbB6xfwXVZrlCF56zemcR17zdcspgSMa7RWphIBWgKPAm65GgAtGAdQEak81IHiF5dZ5xUyCjzrjS-z_RE_Vrf7ra1NUd0xJtJwUMf4AiIO0qI5JhFaTfjT4KlY",
+ "accessTokenHash": "ea2da0b06f8554f3ea1f3496e54e15cd87f7b5a53b31caecb8685e996ccf0390",
+ "refreshTokenHash": "a6943e013ca1031c6851c3220062b3591e94e99f1e98e1400ee7354b45cb475e",
+ "expiresIn": 604800,
+ "scope": "profile",
+ "tokenType": "Bearer",
+ "codeChallenge": "",
+ "codeIsUsed": true,
+ "codeExpireIn": 0
+ },
+ {
+ "owner": "admin",
+ "name": "96e45056-947a-4d29-9b59-46e7b2d25889",
+ "createdTime": "2025-09-20T10:56:34Z",
+ "application": "app-built-in",
+ "organization": "built-in",
+ "user": "admin",
+ "code": "a32cf35f5ca08f50732e",
+ "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNlcnQtYnVpbHQtaW4iLCJ0eXAiOiJKV1QifQ.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoiYWNjZXNzLXRva2VuIiwidGFnIjoic3RhZmYiLCJzY29wZSI6InByb2ZpbGUiLCJhenAiOiIxZDFlYjNjODkxZmE4ZmFhN2Q2MyIsImlzcyI6Imh0dHA6Ly8xNzIuMzAuMzQuMTg0OjgwMDQiLCJzdWIiOiIxZTNiNGVlNS03MTY0LTQ3YzQtYTQyMS1hMmZmNDkzOGM3NjUiLCJhdWQiOlsiMWQxZWIzYzg5MWZhOGZhYTdkNjMiXSwiZXhwIjoxNzU4OTcwNTk0LCJuYmYiOjE3NTgzNjU3OTQsImlhdCI6MTc1ODM2NTc5NCwianRpIjoiYWRtaW4vOTZlNDUwNTYtOTQ3YS00ZDI5LTliNTktNDZlN2IyZDI1ODg5In0.WzQyw21sLPwwhCZp6v7PlPM_Muztxl-XFwKtYdg96Fp0mxWzXB3oPfGECFfcqXjNi6n8C42M32deKKUERimOxESI9LehdDwsweyvZN_wyeJgkxmIhN1gQd59TjIglJ134IFYA2AoIxCC76M78ged9hEW_IzoOF2lQh12DISCLY7RuzYpkuY0DTe4QFcRocAp_ZRMTe6wQPN8zPDqhGvJBs173o1CFSdta7TzE9UyFgrfdhITSv2rNlPqi7hbWvOlazDDjY8Qzw40xi7_v2wnO33hbiX_WLMLlJpOTzWnPFWcw9-nYd4rCL3Vxo0CVteNwP3ZuK2HtN8errnM6vXFAng0ZgdtyIQ3wdI1-65cyG6PJGBBq02d6Hgfg1KktnsTtA0tklO2t9YM3ph-EDAMDj35gUyU3sXFfHpVmtB9qKwx6WmcvA6hJvo8Gc79_-SoLE92a_GW_sRojqJJ5rLmb9d6mrU6UqKVTsa1mfV28CnLMkQq7kyxpjpRD41yVh-vdXv4qquHxTIjPtgw6akS3sCjGbTszO5YvvOzQCcHN8brBKnDX0MYvKu7i8-d0roVF0TYLaCDW8EKm1AQlaP7ts6Pi3INeyf6xaXhJfNqQSeqDkmAWNDUUO3okTu8uq4br48inn7GK4e8Dc2QpZScerPlNjKTliOsKZzK1uNCpIA",
+ "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoicmVmcmVzaC10b2tlbiIsInRhZyI6InN0YWZmIiwic2NvcGUiOiJwcm9maWxlIiwiYXpwIjoiMWQxZWIzYzg5MWZhOGZhYTdkNjMiLCJpc3MiOiJodHRwOi8vMTcyLjMwLjM0LjE4NDo4MDA0Iiwic3ViIjoiMWUzYjRlZTUtNzE2NC00N2M0LWE0MjEtYTJmZjQ5MzhjNzY1IiwiYXVkIjpbIjFkMWViM2M4OTFmYThmYWE3ZDYzIl0sImV4cCI6MTc1ODk3MDU5NCwibmJmIjoxNzU4MzY1Nzk0LCJpYXQiOjE3NTgzNjU3OTQsImp0aSI6ImFkbWluLzk2ZTQ1MDU2LTk0N2EtNGQyOS05YjU5LTQ2ZTdiMmQyNTg4OSJ9.FxxMAsCarLLg6uDuP67z5zqTvgEufohViNlpM9z9127XStlOShcXRR3C4Z-OwDRP65QmqqidWY-Sn2VkaxWnpKB_Du-KLBHDKz2XRKv20Qg9E8Q25rcyiBl2RnRa3pFfnekaOw9fj21S0LAkUEuqqsbotDeOv_B9seMRptAGcuhdTY0pMnurRh6b4-tt0fhH1CxlI1LZX5fFS7EWPACqZ2OCYVujU43jicusnXPpPwq2mKiKBGDx-sBXyVIpBuocLJrJDIClV6S0yqMtfbkwM5n7q8GDg-Md9_wsQ9ZT6MmRHzEOP2LI3B8UNZ4EHkzclVRQSaQWlh4QBXmYpoHJWLH5-inNQ0XON4zGmgdsEY-PKY6sYa2Qkf6tXCY7J_l8lUMLtvU_PUkamrZYNyB29JsCTSBHUpk2CuZ2kVyhSZ2v2oAr6v80kywJrEesuP1ErYR3BXwXRUiDZ4qds5I8BolrNGS0Ew3aMyPVmcJ8FxoDcwViDzcy-aMnaUPnpa7yV1BgLShKp_KD0byd28cns5ARbEi2XZwA7pCsE59QHI655OQNGCIM9hofMK0PIl4aJGyNVOVRZeNc-JvrwNdjR_bGYVJYo5e2XygRoedtlCg5hPpUzr9V82eIqmY8noU1yN0vneH-h24GLeDcb0OILaGvYBuMn8l0cslfDMxeK7Y",
+ "accessTokenHash": "b731f1895b7fd0397844fb3926495c7791f8cd4670e10661b7d07c67d684ca51",
+ "refreshTokenHash": "fe4612153bba0a17aecefd1ca69da23dd2e30fcb55f91f16fbc052d4d12739fa",
+ "expiresIn": 604800,
+ "scope": "profile",
+ "tokenType": "Bearer",
+ "codeChallenge": "",
+ "codeIsUsed": true,
+ "codeExpireIn": 0
+ },
+ {
+ "owner": "admin",
+ "name": "c8fea633-4187-49d6-b94c-e43306f5a32e",
+ "createdTime": "2025-09-20T10:56:24Z",
+ "application": "example-app",
+ "organization": "example-org",
+ "user": "example-user",
+ "code": "032fd4699c85d5444a80",
+ "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNlcnQtYnVpbHQtaW4iLCJ0eXAiOiJKV1QifQ.eyJvd25lciI6ImV4YW1wbGUtb3JnIiwibmFtZSI6ImV4YW1wbGUtdXNlciIsImNyZWF0ZWRUaW1lIjoiMjAyNS0wOS0yMFQxMDo1NjoyMVoiLCJ1cGRhdGVkVGltZSI6IiIsImRlbGV0ZWRUaW1lIjoiIiwiaWQiOiIzZDdjY2EyYi04ZGE5LTQxYmYtYjUzNS02YzlmODNkNzMxZGIiLCJ0eXBlIjoibm9ybWFsLXVzZXIiLCJwYXNzd29yZCI6IiIsInBhc3N3b3JkU2FsdCI6IiIsInBhc3N3b3JkVHlwZSI6InBsYWluIiwiZGlzcGxheU5hbWUiOiLnpLrkvovnlKjmiLciLCJmaXJzdE5hbWUiOiIiLCJsYXN0TmFtZSI6IiIsImF2YXRhciI6Imh0dHBzOi8vY2RuLmNhc2Jpbi5vcmcvaW1nL2Nhc2Jpbi5zdmciLCJhdmF0YXJUeXBlIjoiIiwicGVybWFuZW50QXZhdGFyIjoiIiwiZW1haWwiOiIiLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxODg4ODg4ODg4OCIsImNvdW50cnlDb2RlIjoiQ04iLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjowLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjpmYWxzZSwiaXNGb3JiaWRkZW4iOmZhbHNlLCJpc0RlbGV0ZWQiOmZhbHNlLCJzaWdudXBBcHBsaWNhdGlvbiI6ImV4YW1wbGUtYXBwIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoiYWNjZXNzLXRva2VuIiwidGFnIjoiIiwic2NvcGUiOiJwcm9maWxlIiwiYXpwIjoiZTNiYTZmZWM0MmNmZTk5NjEyMWYiLCJpc3MiOiJodHRwOi8vMTcyLjMwLjM0LjE4NDo4MDA0Iiwic3ViIjoiM2Q3Y2NhMmItOGRhOS00MWJmLWI1MzUtNmM5ZjgzZDczMWRiIiwiYXVkIjpbImUzYmE2ZmVjNDJjZmU5OTYxMjFmIl0sImV4cCI6MTc1ODk3MDU4NCwibmJmIjoxNzU4MzY1Nzg0LCJpYXQiOjE3NTgzNjU3ODQsImp0aSI6ImFkbWluL2M4ZmVhNjMzLTQxODctNDlkNi1iOTRjLWU0MzMwNmY1YTMyZSJ9.buJ10cMxbnIS6LRrAjMkJrtOw-V0NJWJ2ySfk-uzWLTxA1SOXwA1DGa8wsn5k0WUyCy6RSN3d3Kl75hz2qwKZdABc4E9tiqx7J_obXxGjAZwZozmWrzBLMBCXjIHk89qc7b6AEbndNbSR0_MDRWunNNwqGvY6H4q3rSAA0Cu-sKLW1xUIpvRLVq3pF2ddWd3_4t30O4uDR7q3PBzmGeCKfRxiCEB3TYcq89rGfS9frKXl7vN9m9wSM6Q6Pqx1IFkALoUl1Z45GIHSn9nzRTuPr5dbeD2jOI9Y-3tHndRrKioyKGZcBgPhzEFmFY6oBWbzoK1TQEGJMk667RfVxew9aE-hXA07yxL7el1p6ZIG_RoUw9MYC0NzjYtiDWg8qamDX2Om7NRs6-e11hPbq3-kvyO9sFdu9aFzpPE-2vcpUfyI0jgg9QRpPjYJ00_WbtTW1caIfAu1RlWK7M56GFylKXtQkLDlz7R1d5pY9z6kMmoBOu-eiLdKfA1QWhGvDp6g3m3HBPCVd7J0lAGnQFFzTaO5adTpTlfN7PHnd1U4hhfSlU525s7-dRIiKjRLeKgehmAAlwlG_M7_huapbGpAxg_t2kDmOsGf9vxbbiHt9x-vYtnyT9rF6XRttAzRfddl3kEsg8vZpG1K9IV5b0mGmYMB2UAdC-YFlVD6FSAjrQ",
+ "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJvd25lciI6ImV4YW1wbGUtb3JnIiwibmFtZSI6ImV4YW1wbGUtdXNlciIsImNyZWF0ZWRUaW1lIjoiMjAyNS0wOS0yMFQxMDo1NjoyMVoiLCJ1cGRhdGVkVGltZSI6IiIsImRlbGV0ZWRUaW1lIjoiIiwiaWQiOiIzZDdjY2EyYi04ZGE5LTQxYmYtYjUzNS02YzlmODNkNzMxZGIiLCJ0eXBlIjoibm9ybWFsLXVzZXIiLCJwYXNzd29yZCI6IiIsInBhc3N3b3JkU2FsdCI6IiIsInBhc3N3b3JkVHlwZSI6InBsYWluIiwiZGlzcGxheU5hbWUiOiLnpLrkvovnlKjmiLciLCJmaXJzdE5hbWUiOiIiLCJsYXN0TmFtZSI6IiIsImF2YXRhciI6Imh0dHBzOi8vY2RuLmNhc2Jpbi5vcmcvaW1nL2Nhc2Jpbi5zdmciLCJhdmF0YXJUeXBlIjoiIiwicGVybWFuZW50QXZhdGFyIjoiIiwiZW1haWwiOiIiLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxODg4ODg4ODg4OCIsImNvdW50cnlDb2RlIjoiQ04iLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjowLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjpmYWxzZSwiaXNGb3JiaWRkZW4iOmZhbHNlLCJpc0RlbGV0ZWQiOmZhbHNlLCJzaWdudXBBcHBsaWNhdGlvbiI6ImV4YW1wbGUtYXBwIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoicmVmcmVzaC10b2tlbiIsInRhZyI6IiIsInNjb3BlIjoicHJvZmlsZSIsImF6cCI6ImUzYmE2ZmVjNDJjZmU5OTYxMjFmIiwiaXNzIjoiaHR0cDovLzE3Mi4zMC4zNC4xODQ6ODAwNCIsInN1YiI6IjNkN2NjYTJiLThkYTktNDFiZi1iNTM1LTZjOWY4M2Q3MzFkYiIsImF1ZCI6WyJlM2JhNmZlYzQyY2ZlOTk2MTIxZiJdLCJleHAiOjE3NTg5NzA1ODQsIm5iZiI6MTc1ODM2NTc4NCwiaWF0IjoxNzU4MzY1Nzg0LCJqdGkiOiJhZG1pbi9jOGZlYTYzMy00MTg3LTQ5ZDYtYjk0Yy1lNDMzMDZmNWEzMmUifQ.ncEansC9KmS4KRzM7J-8X1GWxOZPa6paHyTa71o1-kYA47IxZlnta5V8HPorNDL-uiKy6JaxIa9s9qd8SgBfqNcf1LWjOnsjScJuvbnDthS4db0RTPgqN_roaBAf4O_vSFxhvDAFy796zNXpbRbo3HfVIC7la_K5Y-3Qg4PaviX2oI5Rs9jQ1ao4iWUsADDMkQHTNS5LSlgQee_tAk_DbtJ7tB2Fw6z8xr23BM7FksfxnYLH9HFYxv7NxLkNeeskEGkTVpMgQaQzNs19bVDzfG0O5zcHjxjQIx76Q1GotTD2V-x59_VilM0Avr39exhPRc8v3y6Jp5j0TtMd2LvTMWrGkrINW25mBijbuGB1xfMy7LsyPR8lerFt--xG_kelRK4HL76XaZF9pz73FU6IYCv9QpYothE_zVYOEkrmkaNltpfFckoCPMwY5dIN8_sco3P88X607XervKFVcs3ArCA8sL3mHjX--xZ_FK2A_5Jmi_oriSytU-uwQPbIbMATr5CPKOpwuIrkCugjOIi1HyojzSYcmSdxmM9MiOW5Qx92nTBVd1g_b6Px5bZzENXey2mFtDVLy2P2Eh8ihaZXY1IymTXKbJfn5rgEg5PjkdA_7dyrX-bBTg8xwX3vQH6y8_j_69vDHd57rivUcy_ZbQCz6QQCgVkVallpZZuk7U4",
+ "accessTokenHash": "5d07861e82306dcbcb9b5dbb7ad4789c19ea0c0208613400af30f1e3bd7fb481",
+ "refreshTokenHash": "f4c32cfb6450ca7ad9209efe58b3c26dae45a708c6820edd022df31659255850",
+ "expiresIn": 604800,
+ "scope": "profile",
+ "tokenType": "Bearer",
+ "codeChallenge": "",
+ "codeIsUsed": true,
+ "codeExpireIn": 0
+ },
+ {
+ "owner": "admin",
+ "name": "b3bc9cc8-ebe9-4be5-9f1b-439120a263d7",
+ "createdTime": "2025-09-20T10:48:20Z",
+ "application": "app-built-in",
+ "organization": "built-in",
+ "user": "admin",
+ "code": "3d4550d03ef798879df7",
+ "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNlcnQtYnVpbHQtaW4iLCJ0eXAiOiJKV1QifQ.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoiYWNjZXNzLXRva2VuIiwidGFnIjoic3RhZmYiLCJzY29wZSI6InByb2ZpbGUiLCJhenAiOiIxZDFlYjNjODkxZmE4ZmFhN2Q2MyIsImlzcyI6Imh0dHA6Ly8xNzIuMzAuMzQuMTg0OjgwMDQiLCJzdWIiOiIxZTNiNGVlNS03MTY0LTQ3YzQtYTQyMS1hMmZmNDkzOGM3NjUiLCJhdWQiOlsiMWQxZWIzYzg5MWZhOGZhYTdkNjMiXSwiZXhwIjoxNzU4OTcwMTAwLCJuYmYiOjE3NTgzNjUzMDAsImlhdCI6MTc1ODM2NTMwMCwianRpIjoiYWRtaW4vYjNiYzljYzgtZWJlOS00YmU1LTlmMWItNDM5MTIwYTI2M2Q3In0.GD4vk_rKUZ_bgNcgombRXVfoHNQE0KKifFZ0jvlGN7Se_OeJOkLdhJmPnR9LYgAr4hjZnqW90IL-ADJYcWswSS0bqk-D6so5srbDjxWNTa5EIxVOfG9JLBvGJl7spjvwLj84OIQNrtEOdL-4iHGcnK5bxMLoqvYApWWMZjcA99_tlibV4EBk3ajFMnGQAzrl-GhXvsglK0mk6DRWPJuzvHwRu3PvAUBegtBr5guT2WUC1VCS_FSxiwpdHcKAcqe-zArLRH7w84gs9IiR6-5Mqvx2QDO1c4yYH9DqTDplGoqK6Ln4KiIjO5MHu78zG_Mv4ywY-t0-qolZS0EnQ5JiJarZANbsI9SGIj7z_M5HhDYOYtcPvDl5DTq-gFOwyq0F7YJ2VcT9_pPKVm71nZAAkR9D0UocGCu_AHQAdvBK_3gwUYkeotITTGPj4M3U-HLoZwyy54lPjscPpLxaS_HMgeCe_GZSErOvGLV_s9cLJL3IJ5gXo5gc84DQHID-UkmLTAoj7wRwmr64STtGCKjsellJI0Jf62g4TDhOVYerd8l1K5oa520RB-gQ_PkV0nyyPIpVYpzCbByBDPxvjFmVLFgKE1NnYAeoAE-1AJbqH3E2sh9hps9oiYMWBCnym_1eL515_spRRJlxJlGCEETyhTUAYh_RrrMXjEit77P1GIE",
+ "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoicmVmcmVzaC10b2tlbiIsInRhZyI6InN0YWZmIiwic2NvcGUiOiJwcm9maWxlIiwiYXpwIjoiMWQxZWIzYzg5MWZhOGZhYTdkNjMiLCJpc3MiOiJodHRwOi8vMTcyLjMwLjM0LjE4NDo4MDA0Iiwic3ViIjoiMWUzYjRlZTUtNzE2NC00N2M0LWE0MjEtYTJmZjQ5MzhjNzY1IiwiYXVkIjpbIjFkMWViM2M4OTFmYThmYWE3ZDYzIl0sImV4cCI6MTc1ODk3MDEwMCwibmJmIjoxNzU4MzY1MzAwLCJpYXQiOjE3NTgzNjUzMDAsImp0aSI6ImFkbWluL2IzYmM5Y2M4LWViZTktNGJlNS05ZjFiLTQzOTEyMGEyNjNkNyJ9.NJmBGdkB_B8g6ETE-w3s_X68rmy0xQ3V6u6117nZzkMlyhGrQLWNvzBnsS-IpqDpF-AlZs8_5RXbqqE8UjBQQF4Bzx4iVX9uQU5aq-3V2-pfSj_NJC8vUCMWT4m8PW397ca0YoPj9O5CbW_zLhakwHrYCFl5KiWbUeCymiJXi1ga3Bb9F0HfcGs2bQzLaDf3b9QyKcnymMpPAIMKGJtT57PUvSiVkSKFbunG3tXlczGzRG98lKamZ5NLS1pJMCeYt3Q2_IfHk0VsfOz--0SLbEzf1k-NtPMVMBWc95BSAoLwbCzCQAaI8Yt5dkVqVkSVu3xlpHOGYJoQHONxiiPfu9apy1gGlbctFASbOUUlnSliuQa_dBgRfQhPOy44Jbe_sL-jPYjLqdNU8R4eOsoKrjJTx55uSREDw_jdZItrhz_c2a62JKJLTW3GauYUiFNTCx351wRtorrNAa7yQhsP6iKPNLYKQpgtiwRMcMl5uafYqfYCx5WjUmPnh_Dh2YfTG6xZRI9bon-HOTiX3LGqox4lsCKE7xw8fORmFfiJoQHZMT1oZQxhUYOnNaDol2yU1ItKBdDU2OYXJO8CTWx2Pf-3RkhCAJ1GPeGmWb-5fBcTwdjZpUEAcripw3KuAUHtBcj6bh-QJBuMN6Mq_oRMKmj0a2TNaxjcBxOynxrvLB8",
+ "accessTokenHash": "e8ca10541c424511e14bcb78df856404c90c8214eecb028b6f7a2d397bc17959",
+ "refreshTokenHash": "3169b450f72be004ea22b63ef669e242fc7eaac1366a1b66bca3f3d15be5e7ab",
+ "expiresIn": 604800,
+ "scope": "profile",
+ "tokenType": "Bearer",
+ "codeChallenge": "",
+ "codeIsUsed": true,
+ "codeExpireIn": 0
+ },
+ {
+ "owner": "admin",
+ "name": "bf581cf2-8eeb-41c0-aee2-a9a395725af5",
+ "createdTime": "2025-09-20T10:47:51Z",
+ "application": "app-built-in",
+ "organization": "built-in",
+ "user": "admin",
+ "code": "41f84c4861e1d0024daf",
+ "accessToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNlcnQtYnVpbHQtaW4iLCJ0eXAiOiJKV1QifQ.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoiYWNjZXNzLXRva2VuIiwidGFnIjoic3RhZmYiLCJzY29wZSI6InByb2ZpbGUiLCJhenAiOiIxZDFlYjNjODkxZmE4ZmFhN2Q2MyIsImlzcyI6Imh0dHA6Ly8xNzIuMzAuMzQuMTg0OjgwMDQiLCJzdWIiOiIxZTNiNGVlNS03MTY0LTQ3YzQtYTQyMS1hMmZmNDkzOGM3NjUiLCJhdWQiOlsiMWQxZWIzYzg5MWZhOGZhYTdkNjMiXSwiZXhwIjoxNzU4OTcwMDcxLCJuYmYiOjE3NTgzNjUyNzEsImlhdCI6MTc1ODM2NTI3MSwianRpIjoiYWRtaW4vYmY1ODFjZjItOGVlYi00MWMwLWFlZTItYTlhMzk1NzI1YWY1In0.ysggMjMtH9BhmwQt87ds-9zCRNZqYLVcUPZuLU0XSBSOyz2pBWfcFHAwb_e5D2VGZx5AAQCOkOdQNASi6DbHMcA3eidejl4e6LsxxoEHQiumpLMcAFuyxLLlgnSrWbsBHAGZr5nvCnlUycX0Ioyt3Z-JRRrHnxg_7fr6ZRG-1-xyau8YeLlVt4j46Gc57dtoDQtv6R6_XFSfIrxBzQr6TITTtC_YVcKA3N_SvwlAuaulldEGWs_oBt4aYd_xyY8uZT-AsTLrf2WmvcDK4tN1gt7MAhPiyAZnsEoZA40T4495cAjaAXb0bPrON6v326A_irjqIayLiZvz3I9TYIId7tZrZG_0h17S9ebr4uK2bFGKAh5uRgrBI_x6rptctYs7IuxrwKRaoXklP530t1Lb7-aZRjKkG6Xx0C9XBGgOJmwx0nnZ0hxJsJ74J_8jJA9GepiItLzUvJNYQupBLQyVQLsdkCF7chiJtcZRNEIP8CtudEUcFT8y6fti_5XmIukCw_ftI5sAS8c7g-cm9Pwn9-6lvHwUjX3mvfccodnGsY2U7-6lj3u5LcTxcrvQ5Yc30-x-whaD9vzuMQ1Rf4UUCU_xFIq2nWyPzME6-XqieLY5TaxKMBqfHtG4fDNGeaYTFvx6to1ZD-v6LMUNJW-lGbZ4T9Ug2OpxV4mzL0lkHDY",
+ "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJvd25lciI6ImJ1aWx0LWluIiwibmFtZSI6ImFkbWluIiwiY3JlYXRlZFRpbWUiOiIyMDI1LTA5LTIwVDEwOjQ3OjE3WiIsInVwZGF0ZWRUaW1lIjoiIiwiZGVsZXRlZFRpbWUiOiIiLCJpZCI6IjFlM2I0ZWU1LTcxNjQtNDdjNC1hNDIxLWEyZmY0OTM4Yzc2NSIsInR5cGUiOiJub3JtYWwtdXNlciIsInBhc3N3b3JkIjoiIiwicGFzc3dvcmRTYWx0IjoiIiwicGFzc3dvcmRUeXBlIjoicGxhaW4iLCJkaXNwbGF5TmFtZSI6IkFkbWluIiwiZmlyc3ROYW1lIjoiIiwibGFzdE5hbWUiOiIiLCJhdmF0YXIiOiJodHRwczovL2Nkbi5jYXNiaW4ub3JnL2ltZy9jYXNiaW4uc3ZnIiwiYXZhdGFyVHlwZSI6IiIsInBlcm1hbmVudEF2YXRhciI6IiIsImVtYWlsIjoiYWRtaW5AZXhhbXBsZS5jb20iLCJlbWFpbFZlcmlmaWVkIjpmYWxzZSwicGhvbmUiOiIxMjM0NTY3ODkxMCIsImNvdW50cnlDb2RlIjoiVVMiLCJyZWdpb24iOiIiLCJsb2NhdGlvbiI6IiIsImFkZHJlc3MiOltdLCJhZmZpbGlhdGlvbiI6IkV4YW1wbGUgSW5jLiIsInRpdGxlIjoiIiwiaWRDYXJkVHlwZSI6IiIsImlkQ2FyZCI6IiIsImhvbWVwYWdlIjoiIiwiYmlvIjoiIiwibGFuZ3VhZ2UiOiIiLCJnZW5kZXIiOiIiLCJiaXJ0aGRheSI6IiIsImVkdWNhdGlvbiI6IiIsInNjb3JlIjoyMDAwLCJrYXJtYSI6MCwicmFua2luZyI6MSwiaXNEZWZhdWx0QXZhdGFyIjpmYWxzZSwiaXNPbmxpbmUiOmZhbHNlLCJpc0FkbWluIjp0cnVlLCJpc0ZvcmJpZGRlbiI6ZmFsc2UsImlzRGVsZXRlZCI6ZmFsc2UsInNpZ251cEFwcGxpY2F0aW9uIjoiYXBwLWJ1aWx0LWluIiwiaGFzaCI6IiIsInByZUhhc2giOiIiLCJhY2Nlc3NLZXkiOiIiLCJhY2Nlc3NTZWNyZXQiOiIiLCJnaXRodWIiOiIiLCJnb29nbGUiOiIiLCJxcSI6IiIsIndlY2hhdCI6IiIsImZhY2Vib29rIjoiIiwiZGluZ3RhbGsiOiIiLCJ3ZWlibyI6IiIsImdpdGVlIjoiIiwibGlua2VkaW4iOiIiLCJ3ZWNvbSI6IiIsImxhcmsiOiIiLCJnaXRsYWIiOiIiLCJjcmVhdGVkSXAiOiIxMjcuMC4wLjEiLCJsYXN0U2lnbmluVGltZSI6IiIsImxhc3RTaWduaW5JcCI6IiIsInByZWZlcnJlZE1mYVR5cGUiOiIiLCJyZWNvdmVyeUNvZGVzIjpudWxsLCJ0b3RwU2VjcmV0IjoiIiwibWZhUGhvbmVFbmFibGVkIjpmYWxzZSwibWZhRW1haWxFbmFibGVkIjpmYWxzZSwibGRhcCI6IiIsInByb3BlcnRpZXMiOnt9LCJyb2xlcyI6W10sInBlcm1pc3Npb25zIjpbXSwiZ3JvdXBzIjpbXSwibGFzdFNpZ25pbldyb25nVGltZSI6IiIsInNpZ25pbldyb25nVGltZXMiOjAsIm1hbmFnZWRBY2NvdW50cyI6bnVsbCwidG9rZW5UeXBlIjoicmVmcmVzaC10b2tlbiIsInRhZyI6InN0YWZmIiwic2NvcGUiOiJwcm9maWxlIiwiYXpwIjoiMWQxZWIzYzg5MWZhOGZhYTdkNjMiLCJpc3MiOiJodHRwOi8vMTcyLjMwLjM0LjE4NDo4MDA0Iiwic3ViIjoiMWUzYjRlZTUtNzE2NC00N2M0LWE0MjEtYTJmZjQ5MzhjNzY1IiwiYXVkIjpbIjFkMWViM2M4OTFmYThmYWE3ZDYzIl0sImV4cCI6MTc1ODk3MDA3MSwibmJmIjoxNzU4MzY1MjcxLCJpYXQiOjE3NTgzNjUyNzEsImp0aSI6ImFkbWluL2JmNTgxY2YyLThlZWItNDFjMC1hZWUyLWE5YTM5NTcyNWFmNSJ9.FwFmS1F0j9OoVguQiPghg4JsrwM7z_RI2o6hiBBxbL0OTG1uCtOtoHjlB7e8naoGZrfs8GoC7Q_GWjkVBv_uViWmMoOr9-LLjJb96UdhwcDW1jaeTx-ZoFqOr3xuoY9B44TKiXyP69IEI7fCn6_xDPSRlJNLXSE7UtIL3p_eEEYcI8GNhhSYwpQzSYbQLiFmU-du9Ic7kTscALQb6MRwF6duyqOtsjLuNnqX7fbauH3wwUqbP57PN0usniuIgpW5XPySBtm_688WCOqLipptSrQ12NZejTA2H8P3FZvZUj5VS7snN-1bQdX06B4rYNIU4bLZQd5Q-TfZEWX-JIYobhv8nyqBVcjUDYxl57cFzKVKPiXibrfp0Z5qHiBDZgRAvylRkFUsGgI1GCSXNBqJjVQw1eyhTXn3dlQpvMMZ7zqIWFDILtIV_9pFHH04XbHT7ogprKzXgHoxznFKnytM4BrloH-XNRGr9Ka6Mk-uVyVgMq3hS5Yk2wjQVnWbN8oQx4GwuxqMd3UqfY-2Ba4OxJsvWuW2PikW284q-QmcmphX75nuLlaP104WbBG7S1A9_WYdI37IPGAA7wLwBoLt2y1sx6pYRUsrqj9NG5CCWtgnMAWkkFXWAwA3_ipgcfEWDB7VzlGiqvsPFy54VAHJ1oZUvnmE2go5Dx7RIM1VHGk",
+ "accessTokenHash": "f2e809e71a22b29909f9f1c6e37133ee9a4d38c56f6646cfc9b671aad8ffadc7",
+ "refreshTokenHash": "5dea79cefe422c3e696e4f7245703b6c1e160cc1631787b5dddb206c35c2024b",
+ "expiresIn": 604800,
+ "scope": "profile",
+ "tokenType": "Bearer",
+ "codeChallenge": "",
+ "codeIsUsed": true,
+ "codeExpireIn": 0
+ }
+ ],
+ "webhooks": [],
+ "groups": [],
+ "adapters": [
+ {
+ "owner": "built-in",
+ "name": "api-adapter-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "table": "casbin_api_rule",
+ "useSameDb": true,
+ "type": "",
+ "databaseType": "",
+ "host": "",
+ "port": 0,
+ "user": "",
+ "password": "",
+ "database": ""
+ },
+ {
+ "owner": "built-in",
+ "name": "user-adapter-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "table": "casbin_user_rule",
+ "useSameDb": true,
+ "type": "",
+ "databaseType": "",
+ "host": "",
+ "port": 0,
+ "user": "",
+ "password": "",
+ "database": ""
+ }
+ ],
+ "enforcers": [
+ {
+ "owner": "built-in",
+ "name": "api-enforcer-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "updatedTime": "2025-09-20 10:47:18",
+ "displayName": "API Enforcer",
+ "description": "",
+ "model": "built-in/api-model-built-in",
+ "adapter": "built-in/api-adapter-built-in",
+ "modelCfg": null
+ },
+ {
+ "owner": "built-in",
+ "name": "user-enforcer-built-in",
+ "createdTime": "2025-09-20T10:47:18Z",
+ "updatedTime": "2025-09-20 10:47:18",
+ "displayName": "User Enforcer",
+ "description": "",
+ "model": "built-in/user-model-built-in",
+ "adapter": "built-in/user-adapter-built-in",
+ "modelCfg": null
+ }
+ ],
+ "plans": [],
+ "pricings": [],
+ "invitations": [],
+ "records": [
+ {
+ "id": 26,
+ "owner": "built-in",
+ "name": "2284aa5d-ad34-402a-a73b-d97a4819409b",
+ "createdTime": "2025-09-20T10:59:43Z",
+ "organization": "built-in",
+ "clientIp": "10.10.125.231",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/login",
+ "action": "login",
+ "language": "zh",
+ "object": "{\"application\":\"app-built-in\",\"organization\":\"built-in\",\"username\":\"admin\",\"password\":\"***\",\"autoSignin\":true,\"language\":\"\",\"signinMethod\":\"Password\",\"type\":\"login\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 25,
+ "owner": "built-in",
+ "name": "420a7908-d3fd-4630-aa30-f9afa20260d2",
+ "createdTime": "2025-09-20T10:58:45Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-role?id=example-org/example-role",
+ "action": "update-role",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-role\",\"createdTime\":\"2025-09-20T18:58:05+08:00\",\"displayName\":\"示例角色\",\"description\":\"\",\"users\":[\"example-org/example-user\"],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 24,
+ "owner": "built-in",
+ "name": "97faa108-d69f-449c-a2fa-c76e90a5fbd5",
+ "createdTime": "2025-09-20T10:58:41Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-role?id=example-org/example-role",
+ "action": "update-role",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-role\",\"createdTime\":\"2025-09-20T18:58:05+08:00\",\"displayName\":\"示例角色\",\"description\":\"\",\"users\":[\"example-org/example-user\"],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 23,
+ "owner": "built-in",
+ "name": "c54b07c2-b470-40d7-a542-3cf234b10e75",
+ "createdTime": "2025-09-20T10:58:33Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-role?id=built-in/example-role",
+ "action": "update-role",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-role\",\"createdTime\":\"2025-09-20T18:58:05+08:00\",\"displayName\":\"示例角色\",\"description\":\"\",\"users\":[],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 22,
+ "owner": "built-in",
+ "name": "e51bbeba-bdf3-4ef4-9130-b3c29007e5f2",
+ "createdTime": "2025-09-20T10:58:31Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-role?id=built-in/role_s52ltl",
+ "action": "update-role",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-role\",\"createdTime\":\"2025-09-20T18:58:05+08:00\",\"displayName\":\"示例角色\",\"description\":\"\",\"users\":[],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 21,
+ "owner": "built-in",
+ "name": "76c504fe-ef57-47e2-9b38-f37bc9254fdb",
+ "createdTime": "2025-09-20T10:58:05Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-role",
+ "action": "add-role",
+ "language": "zh",
+ "object": "{\"owner\":\"built-in\",\"name\":\"role_s52ltl\",\"createdTime\":\"2025-09-20T18:58:05+08:00\",\"displayName\":\"New Role - s52ltl\",\"users\":[],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 20,
+ "owner": "built-in",
+ "name": "d8f347d3-880c-4991-8094-b61c24aaeefd",
+ "createdTime": "2025-09-20T10:57:13Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-role",
+ "action": "add-role",
+ "language": "zh",
+ "object": "{\"owner\":\"built-in\",\"name\":\"role_m06bnm\",\"createdTime\":\"2025-09-20T18:57:13+08:00\",\"displayName\":\"New Role - m06bnm\",\"users\":[],\"groups\":[],\"roles\":[],\"domains\":[],\"isEnabled\":true}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 19,
+ "owner": "built-in",
+ "name": "00355afa-dbab-48a5-a3b1-fd6e96e834f6",
+ "createdTime": "2025-09-20T10:57:09Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-user?id=example-org/example-user",
+ "action": "update-user",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-user\",\"createdTime\":\"2025-09-20T10:56:21Z\",\"updatedTime\":\"\",\"deletedTime\":\"\",\"id\":\"3d7cca2b-8da9-41bf-b535-6c9f83d731db\",\"externalId\":\"\",\"type\":\"normal-user\",\"password\":\"***\",\"passwordSalt\":\"\",\"passwordType\":\"plain\",\"displayName\":\"示例用户\",\"firstName\":\"\",\"lastName\":\"\",\"avatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"avatarType\":\"\",\"permanentAvatar\":\"\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"18888888888\",\"countryCode\":\"CN\",\"region\":\"\",\"location\":\"\",\"address\":[],\"affiliation\":\"\",\"title\":\"\",\"idCardType\":\"\",\"idCard\":\"\",\"homepage\":\"\",\"bio\":\"\",\"tag\":\"\",\"language\":\"\",\"gender\":\"\",\"birthday\":\"\",\"education\":\"\",\"score\":0,\"karma\":0,\"ranking\":1,\"balance\":0,\"currency\":\"\",\"isDefaultAvatar\":false,\"isOnline\":false,\"isAdmin\":false,\"isForbidden\":false,\"isDeleted\":false,\"signupApplication\":\"example-app\",\"hash\":\"\",\"preHash\":\"\",\"accessKey\":\"\",\"accessSecret\":\"\",\"accessToken\":\"\",\"createdIp\":\"\",\"lastSigninTime\":\"\",\"lastSigninIp\":\"\",\"github\":\"\",\"google\":\"\",\"qq\":\"\",\"wechat\":\"\",\"facebook\":\"\",\"dingtalk\":\"\",\"weibo\":\"\",\"gitee\":\"\",\"linkedin\":\"\",\"wecom\":\"\",\"lark\":\"\",\"gitlab\":\"\",\"adfs\":\"\",\"baidu\":\"\",\"alipay\":\"\",\"casdoor\":\"\",\"infoflow\":\"\",\"apple\":\"\",\"azuread\":\"\",\"azureadb2c\":\"\",\"slack\":\"\",\"steam\":\"\",\"bilibili\":\"\",\"okta\":\"\",\"douyin\":\"\",\"kwai\":\"\",\"line\":\"\",\"amazon\":\"\",\"auth0\":\"\",\"battlenet\":\"\",\"bitbucket\":\"\",\"box\":\"\",\"cloudfoundry\":\"\",\"dailymotion\":\"\",\"deezer\":\"\",\"digitalocean\":\"\",\"discord\":\"\",\"dropbox\":\"\",\"eveonline\":\"\",\"fitbit\":\"\",\"gitea\":\"\",\"heroku\":\"\",\"influxcloud\":\"\",\"instagram\":\"\",\"intercom\":\"\",\"kakao\":\"\",\"lastfm\":\"\",\"mailru\":\"\",\"meetup\":\"\",\"microsoftonline\":\"\",\"naver\":\"\",\"nextcloud\":\"\",\"onedrive\":\"\",\"oura\":\"\",\"patreon\":\"\",\"paypal\":\"\",\"salesforce\":\"\",\"shopify\":\"\",\"soundcloud\":\"\",\"spotify\":\"\",\"strava\":\"\",\"stripe\":\"\",\"tiktok\":\"\",\"tumblr\":\"\",\"twitch\":\"\",\"twitter\":\"\",\"typetalk\":\"\",\"uber\":\"\",\"vk\":\"\",\"wepay\":\"\",\"xero\":\"\",\"yahoo\":\"\",\"yammer\":\"\",\"yandex\":\"\",\"zoom\":\"\",\"metamask\":\"\",\"web3onboard\":\"\",\"custom\":\"\",\"webauthnCredentials\":null,\"preferredMfaType\":\"\",\"recoveryCodes\":null,\"totpSecret\":\"\",\"mfaPhoneEnabled\":false,\"mfaEmailEnabled\":false,\"multiFactorAuths\":[{\"enabled\":false,\"isPreferred\":false,\"mfaType\":\"sms\",\"mfaRememberInHours\":0},{\"enabled\":false,\"isPreferred\":false,\"mfaType\":\"email\",\"mfaRememberInHours\":0},{\"enabled\":false,\"isPreferred\":false,\"mfaType\":\"app\",\"mfaRememberInHours\":0}],\"invitation\":\"\",\"invitationCode\":\"\",\"faceIds\":null,\"ldap\":\"\",\"properties\":{},\"roles\":[],\"permissions\":[],\"groups\":[],\"lastChangePasswordTime\":\"\",\"lastSigninWrongTime\":\"\",\"signinWrongTimes\":0,\"managedAccounts\":null,\"mfaAccounts\":null,\"mfaItems\":null,\"mfaRememberDeadline\":\"\",\"needUpdatePassword\":false,\"ipWhitelist\":\"\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 18,
+ "owner": "built-in",
+ "name": "5a427026-bfbf-48fc-8a1e-ae8e4e573e0e",
+ "createdTime": "2025-09-20T10:56:34Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/login",
+ "action": "login",
+ "language": "zh",
+ "object": "{\"application\":\"app-built-in\",\"organization\":\"built-in\",\"username\":\"admin\",\"password\":\"***\",\"autoSignin\":true,\"language\":\"\",\"signinMethod\":\"Password\",\"type\":\"login\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 17,
+ "owner": "example-org",
+ "name": "7f808e0b-2a32-4b18-9cd1-97e732113c1c",
+ "createdTime": "2025-09-20T10:56:27Z",
+ "organization": "example-org",
+ "clientIp": "10.10.122.195",
+ "user": "example-user",
+ "method": "POST",
+ "requestUri": "/api/logout",
+ "action": "logout",
+ "language": "zh",
+ "object": "",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 16,
+ "owner": "example-org",
+ "name": "4bc172fd-f388-4423-8314-c7f9b6f6a433",
+ "createdTime": "2025-09-20T10:56:21Z",
+ "organization": "example-org",
+ "clientIp": "10.10.122.195",
+ "user": "example-user",
+ "method": "POST",
+ "requestUri": "/api/signup",
+ "action": "new-user",
+ "language": "zh",
+ "object": "{\"owner\":\"example-org\",\"name\":\"example-user\",\"createdTime\":\"2025-09-20T10:56:21Z\",\"updatedTime\":\"\",\"deletedTime\":\"\",\"id\":\"3d7cca2b-8da9-41bf-b535-6c9f83d731db\",\"externalId\":\"\",\"type\":\"normal-user\",\"password\":\"***\",\"passwordSalt\":\"\",\"passwordType\":\"plain\",\"displayName\":\"示例用户\",\"firstName\":\"\",\"lastName\":\"\",\"avatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"avatarType\":\"\",\"permanentAvatar\":\"\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"18888888888\",\"countryCode\":\"CN\",\"region\":\"\",\"location\":\"\",\"address\":[],\"affiliation\":\"\",\"title\":\"\",\"idCardType\":\"\",\"idCard\":\"\",\"homepage\":\"\",\"bio\":\"\",\"tag\":\"\",\"language\":\"\",\"gender\":\"\",\"birthday\":\"\",\"education\":\"\",\"score\":0,\"karma\":0,\"ranking\":1,\"balance\":0,\"currency\":\"\",\"isDefaultAvatar\":false,\"isOnline\":false,\"isAdmin\":false,\"isForbidden\":false,\"isDeleted\":false,\"signupApplication\":\"example-app\",\"hash\":\"\",\"preHash\":\"\",\"accessKey\":\"\",\"accessSecret\":\"\",\"accessToken\":\"\",\"createdIp\":\"\",\"lastSigninTime\":\"\",\"lastSigninIp\":\"\",\"github\":\"\",\"google\":\"\",\"qq\":\"\",\"wechat\":\"\",\"facebook\":\"\",\"dingtalk\":\"\",\"weibo\":\"\",\"gitee\":\"\",\"linkedin\":\"\",\"wecom\":\"\",\"lark\":\"\",\"gitlab\":\"\",\"adfs\":\"\",\"baidu\":\"\",\"alipay\":\"\",\"casdoor\":\"\",\"infoflow\":\"\",\"apple\":\"\",\"azuread\":\"\",\"azureadb2c\":\"\",\"slack\":\"\",\"steam\":\"\",\"bilibili\":\"\",\"okta\":\"\",\"douyin\":\"\",\"kwai\":\"\",\"line\":\"\",\"amazon\":\"\",\"auth0\":\"\",\"battlenet\":\"\",\"bitbucket\":\"\",\"box\":\"\",\"cloudfoundry\":\"\",\"dailymotion\":\"\",\"deezer\":\"\",\"digitalocean\":\"\",\"discord\":\"\",\"dropbox\":\"\",\"eveonline\":\"\",\"fitbit\":\"\",\"gitea\":\"\",\"heroku\":\"\",\"influxcloud\":\"\",\"instagram\":\"\",\"intercom\":\"\",\"kakao\":\"\",\"lastfm\":\"\",\"mailru\":\"\",\"meetup\":\"\",\"microsoftonline\":\"\",\"naver\":\"\",\"nextcloud\":\"\",\"onedrive\":\"\",\"oura\":\"\",\"patreon\":\"\",\"paypal\":\"\",\"salesforce\":\"\",\"shopify\":\"\",\"soundcloud\":\"\",\"spotify\":\"\",\"strava\":\"\",\"stripe\":\"\",\"tiktok\":\"\",\"tumblr\":\"\",\"twitch\":\"\",\"twitter\":\"\",\"typetalk\":\"\",\"uber\":\"\",\"vk\":\"\",\"wepay\":\"\",\"xero\":\"\",\"yahoo\":\"\",\"yammer\":\"\",\"yandex\":\"\",\"zoom\":\"\",\"metamask\":\"\",\"web3onboard\":\"\",\"custom\":\"\",\"webauthnCredentials\":null,\"preferredMfaType\":\"\",\"recoveryCodes\":null,\"totpSecret\":\"\",\"mfaPhoneEnabled\":false,\"mfaEmailEnabled\":false,\"invitation\":\"\",\"invitationCode\":\"\",\"faceIds\":null,\"ldap\":\"\",\"properties\":{},\"roles\":null,\"permissions\":null,\"groups\":null,\"lastChangePasswordTime\":\"\",\"lastSigninWrongTime\":\"\",\"signinWrongTimes\":0,\"managedAccounts\":null,\"mfaAccounts\":null,\"mfaItems\":null,\"mfaRememberDeadline\":\"\",\"needUpdatePassword\":false,\"ipWhitelist\":\"\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 0,
+ "isTriggered": true
+ },
+ {
+ "id": 15,
+ "owner": "example-org",
+ "name": "4bc172fd-f388-4423-8314-c7f9b6f6a433",
+ "createdTime": "2025-09-20T10:56:21Z",
+ "organization": "example-org",
+ "clientIp": "10.10.122.195",
+ "user": "example-user",
+ "method": "POST",
+ "requestUri": "/api/signup",
+ "action": "signup",
+ "language": "zh",
+ "object": "{\"application\":\"example-app\",\"organization\":\"example-org\",\"username\":\"example-user\",\"name\":\"示例用户\",\"password\":\"***\",\"confirm\":\"123456\",\"countryCode\":\"CN\",\"phone\":\"18888888888\",\"agreement\":true,\"plan\":null,\"pricing\":null}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 14,
+ "owner": "",
+ "name": "745e0e6a-5194-4d79-a3cd-90551104fd3c",
+ "createdTime": "2025-09-20T10:55:17Z",
+ "organization": "",
+ "clientIp": "10.10.122.195",
+ "user": "",
+ "method": "POST",
+ "requestUri": "/api/login",
+ "action": "login",
+ "language": "zh",
+ "object": "{\"application\":\"example-app\",\"organization\":\"example-org\",\"username\":\"admin\",\"password\":\"***\",\"autoSignin\":true,\"language\":\"\",\"signinMethod\":\"Password\",\"type\":\"login\"}",
+ "response": "{status:\"error\", msg:\"用户: example-org/admin不存在\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 13,
+ "owner": "built-in",
+ "name": "cb140d8a-27a4-4a60-8185-e60db48ddbfb",
+ "createdTime": "2025-09-20T10:54:57Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/logout",
+ "action": "logout",
+ "language": "zh",
+ "object": "",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 12,
+ "owner": "built-in",
+ "name": "deb63bd0-cfb4-4ddb-80da-0878359aeee9",
+ "createdTime": "2025-09-20T10:54:42Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-user",
+ "action": "add-user",
+ "language": "zh",
+ "object": "{\"owner\":\"built-in\",\"name\":\"user_wght4c\",\"createdTime\":\"2025-09-20T18:54:42+08:00\",\"type\":\"normal-user\",\"password\":\"***\",\"passwordSalt\":\"\",\"displayName\":\"New User - wght4c\",\"avatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"email\":\"wght4c@example.com\",\"phone\":\"30037413383\",\"countryCode\":\"US\",\"address\":[],\"groups\":[],\"affiliation\":\"Example Inc.\",\"tag\":\"staff\",\"region\":\"\",\"isAdmin\":true,\"IsForbidden\":false,\"score\":2000,\"isDeleted\":false,\"properties\":{},\"signupApplication\":\"\"}",
+ "response": "{status:\"error\", msg:\"目前,向'built-in'组织添加新用户的功能已禁用。请注意:'built-in'组织中的所有用户均为Casdoor的全局管理员。请参阅文档:https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself。如果您仍希望为built-in组织创建用户,请转到该组织的设置页面并启用“特权同意”选项。\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 11,
+ "owner": "built-in",
+ "name": "e56c3765-842f-4d9e-adda-9a526d99aed9",
+ "createdTime": "2025-09-20T10:54:37Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-user",
+ "action": "add-user",
+ "language": "zh",
+ "object": "{\"owner\":\"built-in\",\"name\":\"user_1ub2fs\",\"createdTime\":\"2025-09-20T18:54:37+08:00\",\"type\":\"normal-user\",\"password\":\"***\",\"passwordSalt\":\"\",\"displayName\":\"New User - 1ub2fs\",\"avatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"email\":\"1ub2fs@example.com\",\"phone\":\"23130608354\",\"countryCode\":\"US\",\"address\":[],\"groups\":[],\"affiliation\":\"Example Inc.\",\"tag\":\"staff\",\"region\":\"\",\"isAdmin\":true,\"IsForbidden\":false,\"score\":2000,\"isDeleted\":false,\"properties\":{},\"signupApplication\":\"\"}",
+ "response": "{status:\"error\", msg:\"目前,向'built-in'组织添加新用户的功能已禁用。请注意:'built-in'组织中的所有用户均为Casdoor的全局管理员。请参阅文档:https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself。如果您仍希望为built-in组织创建用户,请转到该组织的设置页面并启用“特权同意”选项。\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 10,
+ "owner": "built-in",
+ "name": "9bcd72a5-d4dc-47ab-b300-d7b984507762",
+ "createdTime": "2025-09-20T10:54:29Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-organization?id=admin/example-org",
+ "action": "update-organization",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-org\",\"createdTime\":\"2025-09-20T18:48:24+08:00\",\"displayName\":\"示例组织\",\"websiteUrl\":\"https://door.casdoor.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/favicon.png\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"Plain\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"CN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"example-app\",\"userTypes\":null,\"tags\":[],\"languages\":[\"en\",\"es\",\"fr\",\"de\",\"zh\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\",\"it\",\"ms\",\"tr\",\"ar\",\"he\",\"nl\",\"pl\",\"fi\",\"sv\",\"uk\",\"kk\",\"fa\",\"cs\",\"sk\",\"az\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":0,\"enableSoftDeletion\":false,\"isProfilePublic\":true,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":12,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Address\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card info\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Language\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Gender\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Birthday\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Education\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Score\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Karma\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Ranking\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"API key\",\"visible\":false,\"viewRule\":\"\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":false,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is online\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}],\"enableDarkLogo\":false}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 9,
+ "owner": "built-in",
+ "name": "17e560c6-e5a8-4f16-8f0b-8b86b39dcab9",
+ "createdTime": "2025-09-20T10:54:20Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-application?id=admin/example-app",
+ "action": "update-application",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-app\",\"createdTime\":\"2025-09-20T18:50:08+08:00\",\"displayName\":\"示例应用\",\"logo\":\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\",\"order\":0,\"homepageUrl\":\"\",\"description\":\"\",\"organization\":\"example-org\",\"cert\":\"cert-built-in\",\"defaultGroup\":\"\",\"headerHtml\":\"\",\"enablePassword\":true,\"enableSignUp\":true,\"disableSignin\":false,\"enableSigninSession\":false,\"enableAutoSignin\":false,\"enableCodeSignin\":false,\"enableSamlCompress\":false,\"enableSamlC14n10\":false,\"enableSamlPostBinding\":false,\"useEmailAsSamlNameId\":false,\"enableWebAuthn\":false,\"enableLinkWithEmail\":false,\"orgChoiceMode\":\"\",\"samlReplyUrl\":\"\",\"providers\":[{\"owner\":\"\",\"name\":\"provider_captcha_default\",\"canSignUp\":false,\"canSignIn\":false,\"canUnlink\":false,\"countryCodes\":null,\"prompted\":false,\"signupGroup\":\"\",\"rule\":\"\",\"provider\":{\"owner\":\"admin\",\"name\":\"provider_captcha_default\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Captcha Default\",\"category\":\"Captcha\",\"type\":\"Default\",\"subType\":\"\",\"method\":\"\",\"clientId\":\"\",\"clientSecret\":\"\",\"clientId2\":\"\",\"clientSecret2\":\"\",\"cert\":\"\",\"customAuthUrl\":\"\",\"customTokenUrl\":\"\",\"customUserInfoUrl\":\"\",\"customLogo\":\"\",\"scopes\":\"\",\"userMapping\":null,\"httpHeaders\":null,\"host\":\"\",\"port\":0,\"disableSsl\":false,\"title\":\"\",\"content\":\"\",\"receiver\":\"\",\"regionId\":\"\",\"signName\":\"\",\"templateCode\":\"\",\"appId\":\"\",\"endpoint\":\"\",\"intranetEndpoint\":\"\",\"domain\":\"\",\"bucket\":\"\",\"pathPrefix\":\"\",\"metadata\":\"\",\"idP\":\"\",\"issuerUrl\":\"\",\"enableSignAuthnRequest\":false,\"emailRegex\":\"\",\"providerUrl\":\"\"}}],\"signinMethods\":[{\"name\":\"Password\",\"displayName\":\"Password\",\"rule\":\"All\"}],\"signupItems\":[{\"name\":\"ID\",\"visible\":false,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"Random\"},{\"name\":\"Username\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Display name\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Confirm password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Email\",\"visible\":false,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Phone\",\"visible\":true,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Agreement\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Signup button\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Providers\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n }\\n .provider-big-img {\\n margin-bottom: 10px;\\n }\\n \",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"small\"}],\"signinItems\":[{\"name\":\"Back button\",\"visible\":true,\"label\":\"\",\"customCss\":\".back-button {\\n top: 65px;\\n left: 15px;\\n position: absolute;\\n}\\n.back-inner-button{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Languages\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-languages {\\n top: 55px;\\n right: 5px;\\n position: absolute;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Logo\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-logo-box {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signin methods\",\"visible\":true,\"label\":\"\",\"customCss\":\".signin-methods {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Username\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-username {}\\n.login-username-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Password\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-password {}\\n.login-password-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Agreement\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-agreement {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Forgot password?\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-forget-password {\\n display: inline-flex;\\n justify-content: space-between;\\n width: 320px;\\n margin-bottom: 25px;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Login button\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-button-box {\\n margin-bottom: 5px;\\n}\\n.login-button {\\n width: 100%;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signup link\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-signup-link {\\n margin-bottom: 24px;\\n display: flex;\\n justify-content: end;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Providers\",\"visible\":true,\"label\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n}\\n.provider-big-img {\\n margin-bottom: 10px;\\n}\",\"placeholder\":\"\",\"rule\":\"small\",\"isCustom\":false}],\"grantTypes\":[\"authorization_code\",\"password\",\"client_credentials\",\"token\",\"id_token\",\"refresh_token\"],\"organizationObj\":{\"owner\":\"admin\",\"name\":\"built-in\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Built-in Organization\",\"websiteUrl\":\"https://example.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/casbin/favicon.ico\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"US\",\"ES\",\"FR\",\"DE\",\"GB\",\"CN\",\"JP\",\"KR\",\"VN\",\"ID\",\"SG\",\"IN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"userTypes\":[],\"tags\":[],\"languages\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":2000,\"enableSoftDeletion\":false,\"isProfilePublic\":false,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":0,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}]},\"certPublicKey\":\"\",\"tags\":[],\"samlAttributes\":null,\"isShared\":false,\"ipRestriction\":\"\",\"clientId\":\"e3ba6fec42cfe996121f\",\"clientSecret\":\"0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd\",\"redirectUris\":[\"http://localhost:9000/callback\"],\"forcedRedirectOrigin\":\"\",\"tokenFormat\":\"JWT\",\"tokenSigningMethod\":\"\",\"tokenFields\":[],\"expireInHours\":168,\"refreshExpireInHours\":168,\"signupUrl\":\"\",\"signinUrl\":\"\",\"forgetUrl\":\"\",\"affiliationUrl\":\"\",\"ipWhitelist\":\"\",\"termsOfUse\":\"\",\"signupHtml\":\"\",\"signinHtml\":\"\",\"themeData\":null,\"footerHtml\":\"\",\"formCss\":\"\",\"formCssMobile\":\"\",\"formOffset\":2,\"formSideHtml\":\"\",\"formBackgroundUrl\":\"\",\"formBackgroundUrlMobile\":\"\",\"failedSigninLimit\":5,\"failedSigninFrozenTime\":15}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 8,
+ "owner": "built-in",
+ "name": "c3c1335b-785b-4a27-b12a-96bc649a78fb",
+ "createdTime": "2025-09-20T10:54:20Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-application?id=admin/example-app",
+ "action": "update-application",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-app\",\"createdTime\":\"2025-09-20T18:50:08+08:00\",\"displayName\":\"示例应用\",\"logo\":\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\",\"order\":0,\"homepageUrl\":\"\",\"description\":\"\",\"organization\":\"example-org\",\"cert\":\"cert-built-in\",\"defaultGroup\":\"\",\"headerHtml\":\"\",\"enablePassword\":true,\"enableSignUp\":true,\"disableSignin\":false,\"enableSigninSession\":false,\"enableAutoSignin\":false,\"enableCodeSignin\":false,\"enableSamlCompress\":false,\"enableSamlC14n10\":false,\"enableSamlPostBinding\":false,\"useEmailAsSamlNameId\":false,\"enableWebAuthn\":false,\"enableLinkWithEmail\":false,\"orgChoiceMode\":\"\",\"samlReplyUrl\":\"\",\"providers\":[{\"owner\":\"\",\"name\":\"provider_captcha_default\",\"canSignUp\":false,\"canSignIn\":false,\"canUnlink\":false,\"countryCodes\":null,\"prompted\":false,\"signupGroup\":\"\",\"rule\":\"\",\"provider\":{\"owner\":\"admin\",\"name\":\"provider_captcha_default\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Captcha Default\",\"category\":\"Captcha\",\"type\":\"Default\",\"subType\":\"\",\"method\":\"\",\"clientId\":\"\",\"clientSecret\":\"\",\"clientId2\":\"\",\"clientSecret2\":\"\",\"cert\":\"\",\"customAuthUrl\":\"\",\"customTokenUrl\":\"\",\"customUserInfoUrl\":\"\",\"customLogo\":\"\",\"scopes\":\"\",\"userMapping\":null,\"httpHeaders\":null,\"host\":\"\",\"port\":0,\"disableSsl\":false,\"title\":\"\",\"content\":\"\",\"receiver\":\"\",\"regionId\":\"\",\"signName\":\"\",\"templateCode\":\"\",\"appId\":\"\",\"endpoint\":\"\",\"intranetEndpoint\":\"\",\"domain\":\"\",\"bucket\":\"\",\"pathPrefix\":\"\",\"metadata\":\"\",\"idP\":\"\",\"issuerUrl\":\"\",\"enableSignAuthnRequest\":false,\"emailRegex\":\"\",\"providerUrl\":\"\"}}],\"signinMethods\":[{\"name\":\"Password\",\"displayName\":\"Password\",\"rule\":\"All\"}],\"signupItems\":[{\"name\":\"ID\",\"visible\":false,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"Random\"},{\"name\":\"Username\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Display name\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Confirm password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Email\",\"visible\":false,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Phone\",\"visible\":true,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Agreement\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Signup button\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Providers\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n }\\n .provider-big-img {\\n margin-bottom: 10px;\\n }\\n \",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"small\"}],\"signinItems\":[{\"name\":\"Back button\",\"visible\":true,\"label\":\"\",\"customCss\":\".back-button {\\n top: 65px;\\n left: 15px;\\n position: absolute;\\n}\\n.back-inner-button{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Languages\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-languages {\\n top: 55px;\\n right: 5px;\\n position: absolute;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Logo\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-logo-box {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signin methods\",\"visible\":true,\"label\":\"\",\"customCss\":\".signin-methods {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Username\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-username {}\\n.login-username-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Password\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-password {}\\n.login-password-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Agreement\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-agreement {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Forgot password?\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-forget-password {\\n display: inline-flex;\\n justify-content: space-between;\\n width: 320px;\\n margin-bottom: 25px;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Login button\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-button-box {\\n margin-bottom: 5px;\\n}\\n.login-button {\\n width: 100%;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signup link\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-signup-link {\\n margin-bottom: 24px;\\n display: flex;\\n justify-content: end;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Providers\",\"visible\":true,\"label\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n}\\n.provider-big-img {\\n margin-bottom: 10px;\\n}\",\"placeholder\":\"\",\"rule\":\"small\",\"isCustom\":false}],\"grantTypes\":[\"authorization_code\",\"password\",\"client_credentials\",\"token\",\"id_token\",\"refresh_token\"],\"organizationObj\":{\"owner\":\"admin\",\"name\":\"built-in\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Built-in Organization\",\"websiteUrl\":\"https://example.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/casbin/favicon.ico\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"US\",\"ES\",\"FR\",\"DE\",\"GB\",\"CN\",\"JP\",\"KR\",\"VN\",\"ID\",\"SG\",\"IN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"userTypes\":[],\"tags\":[],\"languages\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":2000,\"enableSoftDeletion\":false,\"isProfilePublic\":false,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":0,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}]},\"certPublicKey\":\"\",\"tags\":[],\"samlAttributes\":null,\"isShared\":false,\"ipRestriction\":\"\",\"clientId\":\"e3ba6fec42cfe996121f\",\"clientSecret\":\"0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd\",\"redirectUris\":[\"http://localhost:9000/callback\"],\"forcedRedirectOrigin\":\"\",\"tokenFormat\":\"JWT\",\"tokenSigningMethod\":\"\",\"tokenFields\":[],\"expireInHours\":168,\"refreshExpireInHours\":168,\"signupUrl\":\"\",\"signinUrl\":\"\",\"forgetUrl\":\"\",\"affiliationUrl\":\"\",\"ipWhitelist\":\"\",\"termsOfUse\":\"\",\"signupHtml\":\"\",\"signinHtml\":\"\",\"themeData\":null,\"footerHtml\":\"\",\"formCss\":\"\",\"formCssMobile\":\"\",\"formOffset\":2,\"formSideHtml\":\"\",\"formBackgroundUrl\":\"\",\"formBackgroundUrlMobile\":\"\",\"failedSigninLimit\":5,\"failedSigninFrozenTime\":15}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 7,
+ "owner": "built-in",
+ "name": "c617f6f6-d4a3-4f77-a111-49c775b9a01d",
+ "createdTime": "2025-09-20T10:54:11Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-application?id=admin/example-app",
+ "action": "update-application",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-app\",\"createdTime\":\"2025-09-20T18:50:08+08:00\",\"displayName\":\"示例应用\",\"logo\":\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\",\"order\":0,\"homepageUrl\":\"\",\"description\":\"\",\"organization\":\"built-in\",\"cert\":\"cert-built-in\",\"defaultGroup\":\"\",\"headerHtml\":\"\",\"enablePassword\":true,\"enableSignUp\":true,\"disableSignin\":false,\"enableSigninSession\":false,\"enableAutoSignin\":false,\"enableCodeSignin\":false,\"enableSamlCompress\":false,\"enableSamlC14n10\":false,\"enableSamlPostBinding\":false,\"useEmailAsSamlNameId\":false,\"enableWebAuthn\":false,\"enableLinkWithEmail\":false,\"orgChoiceMode\":\"\",\"samlReplyUrl\":\"\",\"providers\":[{\"owner\":\"\",\"name\":\"provider_captcha_default\",\"canSignUp\":false,\"canSignIn\":false,\"canUnlink\":false,\"countryCodes\":null,\"prompted\":false,\"signupGroup\":\"\",\"rule\":\"\",\"provider\":{\"owner\":\"admin\",\"name\":\"provider_captcha_default\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Captcha Default\",\"category\":\"Captcha\",\"type\":\"Default\",\"subType\":\"\",\"method\":\"\",\"clientId\":\"\",\"clientSecret\":\"\",\"clientId2\":\"\",\"clientSecret2\":\"\",\"cert\":\"\",\"customAuthUrl\":\"\",\"customTokenUrl\":\"\",\"customUserInfoUrl\":\"\",\"customLogo\":\"\",\"scopes\":\"\",\"userMapping\":null,\"httpHeaders\":null,\"host\":\"\",\"port\":0,\"disableSsl\":false,\"title\":\"\",\"content\":\"\",\"receiver\":\"\",\"regionId\":\"\",\"signName\":\"\",\"templateCode\":\"\",\"appId\":\"\",\"endpoint\":\"\",\"intranetEndpoint\":\"\",\"domain\":\"\",\"bucket\":\"\",\"pathPrefix\":\"\",\"metadata\":\"\",\"idP\":\"\",\"issuerUrl\":\"\",\"enableSignAuthnRequest\":false,\"emailRegex\":\"\",\"providerUrl\":\"\"}}],\"signinMethods\":[{\"name\":\"Password\",\"displayName\":\"Password\",\"rule\":\"All\"}],\"signupItems\":[{\"name\":\"ID\",\"visible\":false,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"Random\"},{\"name\":\"Username\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Display name\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Confirm password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Email\",\"visible\":false,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Phone\",\"visible\":true,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Agreement\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Signup button\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Providers\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n }\\n .provider-big-img {\\n margin-bottom: 10px;\\n }\\n \",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"small\"}],\"signinItems\":[{\"name\":\"Back button\",\"visible\":true,\"label\":\"\",\"customCss\":\".back-button {\\n top: 65px;\\n left: 15px;\\n position: absolute;\\n}\\n.back-inner-button{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Languages\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-languages {\\n top: 55px;\\n right: 5px;\\n position: absolute;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Logo\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-logo-box {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signin methods\",\"visible\":true,\"label\":\"\",\"customCss\":\".signin-methods {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Username\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-username {}\\n.login-username-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Password\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-password {}\\n.login-password-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Agreement\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-agreement {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Forgot password?\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-forget-password {\\n display: inline-flex;\\n justify-content: space-between;\\n width: 320px;\\n margin-bottom: 25px;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Login button\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-button-box {\\n margin-bottom: 5px;\\n}\\n.login-button {\\n width: 100%;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signup link\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-signup-link {\\n margin-bottom: 24px;\\n display: flex;\\n justify-content: end;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Providers\",\"visible\":true,\"label\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n}\\n.provider-big-img {\\n margin-bottom: 10px;\\n}\",\"placeholder\":\"\",\"rule\":\"small\",\"isCustom\":false}],\"grantTypes\":[\"authorization_code\",\"password\",\"client_credentials\",\"token\",\"id_token\",\"refresh_token\"],\"organizationObj\":{\"owner\":\"admin\",\"name\":\"built-in\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Built-in Organization\",\"websiteUrl\":\"https://example.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/casbin/favicon.ico\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"US\",\"ES\",\"FR\",\"DE\",\"GB\",\"CN\",\"JP\",\"KR\",\"VN\",\"ID\",\"SG\",\"IN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"userTypes\":[],\"tags\":[],\"languages\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":2000,\"enableSoftDeletion\":false,\"isProfilePublic\":false,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":0,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}]},\"certPublicKey\":\"\",\"tags\":[],\"samlAttributes\":null,\"isShared\":false,\"ipRestriction\":\"\",\"clientId\":\"e3ba6fec42cfe996121f\",\"clientSecret\":\"0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd\",\"redirectUris\":[\"http://localhost:9000/callback\"],\"forcedRedirectOrigin\":\"\",\"tokenFormat\":\"JWT\",\"tokenSigningMethod\":\"\",\"tokenFields\":[],\"expireInHours\":168,\"refreshExpireInHours\":168,\"signupUrl\":\"\",\"signinUrl\":\"\",\"forgetUrl\":\"\",\"affiliationUrl\":\"\",\"ipWhitelist\":\"\",\"termsOfUse\":\"\",\"signupHtml\":\"\",\"signinHtml\":\"\",\"themeData\":null,\"footerHtml\":\"\",\"formCss\":\"\",\"formCssMobile\":\"\",\"formOffset\":2,\"formSideHtml\":\"\",\"formBackgroundUrl\":\"\",\"formBackgroundUrlMobile\":\"\",\"failedSigninLimit\":5,\"failedSigninFrozenTime\":15}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 6,
+ "owner": "built-in",
+ "name": "612a4272-23f0-4289-b7de-e75e0567050d",
+ "createdTime": "2025-09-20T10:53:39Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-application?id=admin/application_1o1gji",
+ "action": "update-application",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-app\",\"createdTime\":\"2025-09-20T18:50:08+08:00\",\"displayName\":\"示例应用\",\"logo\":\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\",\"order\":0,\"homepageUrl\":\"\",\"description\":\"\",\"organization\":\"built-in\",\"cert\":\"cert-built-in\",\"defaultGroup\":\"\",\"headerHtml\":\"\",\"enablePassword\":true,\"enableSignUp\":true,\"disableSignin\":false,\"enableSigninSession\":false,\"enableAutoSignin\":false,\"enableCodeSignin\":false,\"enableSamlCompress\":false,\"enableSamlC14n10\":false,\"enableSamlPostBinding\":false,\"useEmailAsSamlNameId\":false,\"enableWebAuthn\":false,\"enableLinkWithEmail\":false,\"orgChoiceMode\":\"\",\"samlReplyUrl\":\"\",\"providers\":[{\"owner\":\"\",\"name\":\"provider_captcha_default\",\"canSignUp\":false,\"canSignIn\":false,\"canUnlink\":false,\"countryCodes\":null,\"prompted\":false,\"signupGroup\":\"\",\"rule\":\"\",\"provider\":{\"owner\":\"admin\",\"name\":\"provider_captcha_default\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Captcha Default\",\"category\":\"Captcha\",\"type\":\"Default\",\"subType\":\"\",\"method\":\"\",\"clientId\":\"\",\"clientSecret\":\"\",\"clientId2\":\"\",\"clientSecret2\":\"\",\"cert\":\"\",\"customAuthUrl\":\"\",\"customTokenUrl\":\"\",\"customUserInfoUrl\":\"\",\"customLogo\":\"\",\"scopes\":\"\",\"userMapping\":null,\"httpHeaders\":null,\"host\":\"\",\"port\":0,\"disableSsl\":false,\"title\":\"\",\"content\":\"\",\"receiver\":\"\",\"regionId\":\"\",\"signName\":\"\",\"templateCode\":\"\",\"appId\":\"\",\"endpoint\":\"\",\"intranetEndpoint\":\"\",\"domain\":\"\",\"bucket\":\"\",\"pathPrefix\":\"\",\"metadata\":\"\",\"idP\":\"\",\"issuerUrl\":\"\",\"enableSignAuthnRequest\":false,\"emailRegex\":\"\",\"providerUrl\":\"\"}}],\"signinMethods\":[{\"name\":\"Password\",\"displayName\":\"Password\",\"rule\":\"All\"}],\"signupItems\":[{\"name\":\"ID\",\"visible\":false,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"Random\"},{\"name\":\"Username\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Display name\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Confirm password\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Email\",\"visible\":false,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Phone\",\"visible\":true,\"required\":false,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"No verification\"},{\"name\":\"Agreement\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Signup button\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\"\",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"None\"},{\"name\":\"Providers\",\"visible\":true,\"required\":true,\"prompted\":false,\"type\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n }\\n .provider-big-img {\\n margin-bottom: 10px;\\n }\\n \",\"label\":\"\",\"placeholder\":\"\",\"options\":null,\"regex\":\"\",\"rule\":\"small\"}],\"signinItems\":[{\"name\":\"Back button\",\"visible\":true,\"label\":\"\",\"customCss\":\".back-button {\\n top: 65px;\\n left: 15px;\\n position: absolute;\\n}\\n.back-inner-button{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Languages\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-languages {\\n top: 55px;\\n right: 5px;\\n position: absolute;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Logo\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-logo-box {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signin methods\",\"visible\":true,\"label\":\"\",\"customCss\":\".signin-methods {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Username\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-username {}\\n.login-username-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Password\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-password {}\\n.login-password-input{}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Agreement\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-agreement {}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Forgot password?\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-forget-password {\\n display: inline-flex;\\n justify-content: space-between;\\n width: 320px;\\n margin-bottom: 25px;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Login button\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-button-box {\\n margin-bottom: 5px;\\n}\\n.login-button {\\n width: 100%;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Signup link\",\"visible\":true,\"label\":\"\",\"customCss\":\".login-signup-link {\\n margin-bottom: 24px;\\n display: flex;\\n justify-content: end;\\n}\",\"placeholder\":\"\",\"rule\":\"None\",\"isCustom\":false},{\"name\":\"Providers\",\"visible\":true,\"label\":\"\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n}\\n.provider-big-img {\\n margin-bottom: 10px;\\n}\",\"placeholder\":\"\",\"rule\":\"small\",\"isCustom\":false}],\"grantTypes\":[\"authorization_code\",\"password\",\"client_credentials\",\"token\",\"id_token\",\"refresh_token\"],\"organizationObj\":{\"owner\":\"admin\",\"name\":\"built-in\",\"createdTime\":\"2025-09-20T10:47:17Z\",\"displayName\":\"Built-in Organization\",\"websiteUrl\":\"https://example.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/casbin/favicon.ico\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"US\",\"ES\",\"FR\",\"DE\",\"GB\",\"CN\",\"JP\",\"KR\",\"VN\",\"ID\",\"SG\",\"IN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"userTypes\":[],\"tags\":[],\"languages\":[\"en\",\"zh\",\"es\",\"fr\",\"de\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":2000,\"enableSoftDeletion\":false,\"isProfilePublic\":false,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":0,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}]},\"certPublicKey\":\"\",\"tags\":[],\"samlAttributes\":null,\"isShared\":false,\"ipRestriction\":\"\",\"clientId\":\"e3ba6fec42cfe996121f\",\"clientSecret\":\"0c18bf2a11ffbb756ec6ce47dae9e09bdd48e3dd\",\"redirectUris\":[\"http://localhost:9000/callback\"],\"forcedRedirectOrigin\":\"\",\"tokenFormat\":\"JWT\",\"tokenSigningMethod\":\"\",\"tokenFields\":[],\"expireInHours\":168,\"refreshExpireInHours\":168,\"signupUrl\":\"\",\"signinUrl\":\"\",\"forgetUrl\":\"\",\"affiliationUrl\":\"\",\"ipWhitelist\":\"\",\"termsOfUse\":\"\",\"signupHtml\":\"\",\"signinHtml\":\"\",\"themeData\":null,\"footerHtml\":\"\",\"formCss\":\"\",\"formCssMobile\":\"\",\"formOffset\":2,\"formSideHtml\":\"\",\"formBackgroundUrl\":\"\",\"formBackgroundUrlMobile\":\"\",\"failedSigninLimit\":5,\"failedSigninFrozenTime\":15}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 5,
+ "owner": "built-in",
+ "name": "aa5b7413-d9fb-4fee-9565-1a39712643da",
+ "createdTime": "2025-09-20T10:50:09Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-application",
+ "action": "add-application",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"application_1o1gji\",\"organization\":\"built-in\",\"createdTime\":\"2025-09-20T18:50:08+08:00\",\"displayName\":\"New Application - 1o1gji\",\"logo\":\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\",\"enablePassword\":true,\"enableSignUp\":true,\"disableSignin\":false,\"enableSigninSession\":false,\"enableCodeSignin\":false,\"enableSamlCompress\":false,\"providers\":[{\"name\":\"provider_captcha_default\",\"canSignUp\":false,\"canSignIn\":false,\"canUnlink\":false,\"prompted\":false,\"signupGroup\":\"\",\"rule\":\"\"}],\"SigninMethods\":[{\"name\":\"Password\",\"displayName\":\"Password\",\"rule\":\"All\"},{\"name\":\"Verification code\",\"displayName\":\"Verification code\",\"rule\":\"All\"},{\"name\":\"WebAuthn\",\"displayName\":\"WebAuthn\",\"rule\":\"None\"},{\"name\":\"Face ID\",\"displayName\":\"Face ID\",\"rule\":\"None\"}],\"signupItems\":[{\"name\":\"ID\",\"visible\":false,\"required\":true,\"rule\":\"Random\"},{\"name\":\"Username\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Display name\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Password\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Confirm password\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Email\",\"visible\":true,\"required\":true,\"rule\":\"Normal\"},{\"name\":\"Phone\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Agreement\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Signup button\",\"visible\":true,\"required\":true,\"rule\":\"None\"},{\"name\":\"Providers\",\"visible\":true,\"required\":true,\"rule\":\"None\",\"customCss\":\".provider-img {\\n width: 30px;\\n margin: 5px;\\n }\\n .provider-big-img {\\n margin-bottom: 10px;\\n }\\n \"}],\"grantTypes\":[\"authorization_code\",\"password\",\"client_credentials\",\"token\",\"id_token\",\"refresh_token\"],\"cert\":\"cert-built-in\",\"redirectUris\":[\"http://localhost:9000/callback\"],\"tokenFormat\":\"JWT\",\"tokenFields\":[],\"expireInHours\":168,\"refreshExpireInHours\":168,\"formOffset\":2}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 4,
+ "owner": "built-in",
+ "name": "fb9b52f4-cee5-4c76-b577-0fa18d5e2162",
+ "createdTime": "2025-09-20T10:50:04Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/update-organization?id=admin/organization_qe0w97",
+ "action": "update-organization",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"example-org\",\"createdTime\":\"2025-09-20T18:48:24+08:00\",\"displayName\":\"示例组织\",\"websiteUrl\":\"https://door.casdoor.com\",\"logo\":\"\",\"logoDark\":\"\",\"favicon\":\"https://cdn.casbin.org/img/favicon.png\",\"hasPrivilegeConsent\":false,\"passwordType\":\"plain\",\"passwordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"Plain\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"CN\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"userTypes\":null,\"tags\":[],\"languages\":[\"en\",\"es\",\"fr\",\"de\",\"zh\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\",\"it\",\"ms\",\"tr\",\"ar\",\"he\",\"nl\",\"pl\",\"fi\",\"sv\",\"uk\",\"kk\",\"fa\",\"cs\",\"sk\",\"az\"],\"themeData\":null,\"masterPassword\":\"\",\"defaultPassword\":\"\",\"masterVerificationCode\":\"\",\"ipWhitelist\":\"\",\"initScore\":0,\"enableSoftDeletion\":false,\"isProfilePublic\":true,\"useEmailAsUsername\":false,\"enableTour\":true,\"disableSignin\":false,\"ipRestriction\":\"\",\"navItems\":null,\"widgetItems\":null,\"mfaItems\":null,\"mfaRememberInHours\":12,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Address\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"ID card info\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Language\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Gender\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Birthday\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Education\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Score\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Karma\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Ranking\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"API key\",\"visible\":false,\"viewRule\":\"\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\",\"regex\":\"\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Properties\",\"visible\":false,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is online\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\",\"regex\":\"\"},{\"name\":\"Multi-factor authentication\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"WebAuthn credentials\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"Managed accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"},{\"name\":\"MFA accounts\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\",\"regex\":\"\"}],\"enableDarkLogo\":false}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 3,
+ "owner": "built-in",
+ "name": "b0de7ab6-eda7-4d2d-8221-5ac506b21d3e",
+ "createdTime": "2025-09-20T10:48:24Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/add-organization",
+ "action": "add-organization",
+ "language": "zh",
+ "object": "{\"owner\":\"admin\",\"name\":\"organization_qe0w97\",\"createdTime\":\"2025-09-20T18:48:24+08:00\",\"displayName\":\"New Organization - qe0w97\",\"websiteUrl\":\"https://door.casdoor.com\",\"favicon\":\"https://cdn.casbin.org/img/favicon.png\",\"passwordType\":\"plain\",\"PasswordSalt\":\"\",\"passwordOptions\":[\"AtLeast6\"],\"passwordObfuscatorType\":\"Plain\",\"passwordObfuscatorKey\":\"\",\"passwordExpireDays\":0,\"countryCodes\":[\"US\"],\"defaultAvatar\":\"https://cdn.casbin.org/img/casbin.svg\",\"defaultApplication\":\"\",\"tags\":[],\"languages\":[\"en\",\"es\",\"fr\",\"de\",\"zh\",\"id\",\"ja\",\"ko\",\"ru\",\"vi\",\"pt\",\"it\",\"ms\",\"tr\",\"ar\",\"he\",\"nl\",\"pl\",\"fi\",\"sv\",\"uk\",\"kk\",\"fa\",\"cs\",\"sk\",\"az\"],\"masterPassword\":\"\",\"defaultPassword\":\"\",\"enableSoftDeletion\":false,\"isProfilePublic\":true,\"enableTour\":true,\"disableSignin\":false,\"mfaRememberInHours\":12,\"accountItems\":[{\"name\":\"Organization\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"ID\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\"},{\"name\":\"Name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Display name\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Avatar\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"User type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Password\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\"},{\"name\":\"Email\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Phone\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Country code\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Country/Region\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Location\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Address\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Affiliation\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Title\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"ID card type\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"ID card\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"ID card info\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Homepage\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Bio\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Self\"},{\"name\":\"Tag\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Language\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Gender\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Birthday\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Education\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Score\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Karma\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Ranking\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Signup application\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"API key\",\"label\":\"API 密钥\",\"modifyRule\":\"Self\"},{\"name\":\"Groups\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Admin\"},{\"name\":\"Roles\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\"},{\"name\":\"Permissions\",\"visible\":true,\"viewRule\":\"Public\",\"modifyRule\":\"Immutable\"},{\"name\":\"3rd-party logins\",\"visible\":true,\"viewRule\":\"Self\",\"modifyRule\":\"Self\"},{\"name\":\"Properties\",\"visible\":false,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\"},{\"name\":\"Is online\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\"},{\"name\":\"Is admin\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\"},{\"name\":\"Is forbidden\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\"},{\"name\":\"Is deleted\",\"visible\":true,\"viewRule\":\"Admin\",\"modifyRule\":\"Admin\"},{\"Name\":\"Multi-factor authentication\",\"Visible\":true,\"ViewRule\":\"Self\",\"ModifyRule\":\"Self\"},{\"Name\":\"WebAuthn credentials\",\"Visible\":true,\"ViewRule\":\"Self\",\"ModifyRule\":\"Self\"},{\"Name\":\"Managed accounts\",\"Visible\":true,\"ViewRule\":\"Self\",\"ModifyRule\":\"Self\"},{\"Name\":\"MFA accounts\",\"Visible\":true,\"ViewRule\":\"Self\",\"ModifyRule\":\"Self\"}]}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 2,
+ "owner": "built-in",
+ "name": "81b8cee1-efe6-4522-a915-11fc0dc77ff2",
+ "createdTime": "2025-09-20T10:48:20Z",
+ "organization": "built-in",
+ "clientIp": "10.10.122.195",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/login",
+ "action": "login",
+ "language": "zh",
+ "object": "{\"application\":\"app-built-in\",\"organization\":\"built-in\",\"username\":\"admin\",\"password\":\"***\",\"autoSignin\":true,\"language\":\"\",\"signinMethod\":\"Password\",\"type\":\"login\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ },
+ {
+ "id": 1,
+ "owner": "built-in",
+ "name": "3041e2e0-2ff5-43cc-8fad-7d424ebc2e07",
+ "createdTime": "2025-09-20T10:47:51Z",
+ "organization": "built-in",
+ "clientIp": "10.10.125.231",
+ "user": "admin",
+ "method": "POST",
+ "requestUri": "/api/login",
+ "action": "login",
+ "language": "zh",
+ "object": "{\"application\":\"app-built-in\",\"organization\":\"built-in\",\"username\":\"admin\",\"autoSignin\":true,\"password\":\"***\",\"language\":\"\",\"signinMethod\":\"Password\",\"type\":\"login\"}",
+ "response": "{status:\"ok\", msg:\"\"}",
+ "statusCode": 200,
+ "isTriggered": true
+ }
+ ],
+ "sessions": [
+ {
+ "owner": "built-in",
+ "name": "admin",
+ "application": "app-built-in",
+ "createdTime": "2025-09-20T10:47:51Z",
+ "sessionId": [
+ "12692e708d7f7348e75ba800df9606d2",
+ "d60a540308bd43cfae3b88e83c50abd2",
+ "ae85d04cfecb47522ceeec61a166fcc8"
+ ]
+ }
+ ],
+ "subscriptions": [],
+ "transactions": [],
+ "enforcerPolicies": {
+ "built-in/api-enforcer-built-in": [
+ [
+ "built-in",
+ "*",
+ "*",
+ "*",
+ "*",
+ "*"
+ ],
+ [
+ "app",
+ "*",
+ "*",
+ "*",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/signup",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-email-and-phone",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/login",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-app-login",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/logout",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/logout",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/callback",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/device-auth",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-account",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/userinfo",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/user",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/health",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/api/webhook",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-qrcode",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-webhook-event",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-captcha-status",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/api/login/oauth",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-application",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-organization-applications",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-user",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-user-application",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-resources",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-records",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-product",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/buy-product",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-payment",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/update-payment",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/invoice-payment",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/notify-payment",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/unlink",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/set-password",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/send-verification-code",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-captcha",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/verify-captcha",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/verify-code",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/reset-email-or-phone",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/upload-resource",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/.well-known/openid-configuration",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/.well-known/webfinger",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/.well-known/jwks",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-saml-login",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/acs",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/saml/metadata",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/api/saml/redirect",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/cas",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/scim",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/api/webauthn",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-release",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-default-application",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-prometheus-info",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "*",
+ "/api/metrics",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-pricing",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-plan",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-subscription",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-provider",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-organization-names",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-all-objects",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-all-actions",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-all-roles",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/run-casbin-command",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "POST",
+ "/api/refresh-engines",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/get-invitation-info",
+ "*",
+ "*"
+ ],
+ [
+ "*",
+ "*",
+ "GET",
+ "/api/faceid-signin-begin",
+ "*",
+ "*"
+ ]
+ ],
+ "built-in/user-enforcer-built-in": null
+ }
+}
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/init_app_market_dict_data.sql b/docker/astronAgent/astronRPA/volumes/mysql/init_app_market_dict_data.sql
new file mode 100644
index 00000000..09e5a563
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/init_app_market_dict_data.sql
@@ -0,0 +1,37 @@
+INSERT INTO rpa.app_market_dict (business_code,name,dict_code,dict_value,user_type,description,seq,creator_id,create_time,updater_id,update_time,deleted) VALUES
+ ('marketRoleFunc','编辑市场','market_team_edit','T','owner',NULL,NULL,'73','2025-05-19 17:33:11','73','2025-05-19 17:33:11',0),
+ ('marketRoleFunc','编辑市场','market_team_edit','F','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','编辑市场','market_team_edit','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','编辑市场','market_team_edit','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','解散市场','market_team_dissolve','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','解散市场','market_team_dissolve','F','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','解散市场','market_team_dissolve','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','解散市场','market_team_dissolve','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','离开市场','market_team_leave','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','离开市场','market_team_leave','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','离开市场','market_team_leave','T','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','离开市场','market_team_leave','T','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','删除应用','market_resource_delete','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','删除应用','market_resource_delete','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','删除应用','market_resource_delete','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','删除应用','market_resource_delete','T','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','更新到市场','market_resource_upgrade_to_market','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','更新到市场','market_resource_upgrade_to_market','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','更新到市场','market_resource_upgrade_to_market','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','更新到市场','market_resource_upgrade_to_market','T','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员-查询员工','market_user_get_user','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员-查询员工','market_user_get_user','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员-查询员工','market_user_get_user','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员-查询员工','market_user_get_user','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员','market_user_invite','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员','market_user_invite','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员','market_user_invite','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','邀请成员','market_user_invite','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','移出成员','market_user_delete','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','移出成员','market_user_delete','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','移出成员','market_user_delete','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','移出成员','market_user_delete','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','设置角色','market_user_role','T','owner',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','设置角色','market_user_role','T','admin',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','设置角色','market_user_role','F','acquirer',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0),
+ ('marketRoleFunc','设置角色','market_user_role','F','author',NULL,NULL,'73','2024-03-25 10:27:56','73','2024-03-25 10:27:56',0);
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/init_c_atom_meta_data.sql b/docker/astronAgent/astronRPA/volumes/mysql/init_c_atom_meta_data.sql
new file mode 100644
index 00000000..269c3500
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/init_c_atom_meta_data.sql
@@ -0,0 +1 @@
+INSERT INTO `c_atom_meta` VALUES (14,'root','atomCommon','{\"atomicTree\":[{\"key\":\"ai\",\"title\":\"AI\",\"atomics\":[{\"key\":\"aim\",\"title\":\"大模型\",\"atomics\":[{\"key\":\"ChatAI.chat\",\"title\":\"多轮会话\",\"icon\":\"multi-chat\"},{\"key\":\"ChatAI.single_turn_chat\",\"title\":\"单轮会话\",\"icon\":\"single-chat\"},{\"key\":\"ChatAI.knowledge_chat\",\"title\":\"知识问答\",\"icon\":\"knowledge-qa\"},{\"key\":\"DocumentAI.theme_expand\",\"title\":\"主题扩写\",\"icon\":\"topic-expand\"},{\"key\":\"DocumentAI.sentence_expand\",\"title\":\"段落扩写\",\"icon\":\"paragraph-expand\"},{\"key\":\"DocumentAI.sentence_reduce\",\"title\":\"段落缩写\",\"icon\":\"paragraph-abbreviate\"},{\"key\":\"RecruitAI.rating_resume\",\"title\":\"简历评分\",\"icon\":\"resume-scoring\"},{\"key\":\"RecruitAI.generate_keywords\",\"title\":\"职位关键词生成\",\"icon\":\"job-keyword-generation\"},{\"key\":\"ContractAI.get_factors\",\"title\":\"合同要素提取\",\"icon\":\"contract-element-extraction\"},{\"key\":\"Agent.call_dify\",\"title\":\"调用Dify流程\",\"icon\":\"call-dify-workflow\"},{\"key\":\"Agent.call_xcagent\",\"title\":\"调用星辰Agent流程\",\"icon\":\"call-dify-workflow\"}]},{\"key\":\"ocr\",\"title\":\"OCR\",\"atomics\":[{\"key\":\"OpenApi.business_license\",\"title\":\"营业执照识别\",\"icon\":\"business-license-recognition\"},{\"key\":\"OpenApi.id_card\",\"title\":\"身份证识别\",\"icon\":\"id-card-recognition\"},{\"key\":\"OpenApi.vat_invoice\",\"title\":\"增值税发票识别\",\"icon\":\"vat-invoice-recognition\"},{\"key\":\"OpenApi.train_ticket\",\"title\":\"火车票识别\",\"icon\":\"train-ticket-recognition\"},{\"key\":\"OpenApi.taxi_ticket\",\"title\":\"出租车发票识别\",\"icon\":\"taxi-invoice-recognition\"},{\"key\":\"OpenApi.common_ocr\",\"title\":\"通用文字识别\",\"icon\":\"general-text-recognition\"}]},{\"key\":\"verify\",\"title\":\"验证码\",\"atomics\":[{\"key\":\"VerifyCode.picture_code\",\"title\":\"通用数英验证码\",\"icon\":\"captcha-text\"},{\"key\":\"VerifyCode.slider_code\",\"title\":\"通用滑块验证码\",\"icon\":\"captcha-slider\"},{\"key\":\"VerifyCode.click_code\",\"title\":\"通用点击验证码\",\"icon\":\"captcha-click\"}]}]},{\"key\":\"process\",\"title\":\"流程\",\"atomics\":[{\"key\":\"Code.Process\",\"title\":\"运行子流程\",\"icon\":\"run-sub-process\"},{\"key\":\"Script.module\",\"title\":\"运行Python模块\",\"icon\":\"run-python-module\"}]},{\"key\":\"code\",\"title\":\"代码流程\",\"atomics\":[{\"key\":\"DataProcess.set_variable_value\",\"title\":\"设置变量值\",\"icon\":\"set-variable-value\"},{\"key\":\"Report.print\",\"title\":\"日志打印\",\"icon\":\"log-print\"},{\"key\":\"if\",\"title\":\"条件判断\",\"atomics\":[{\"key\":\"Code.If\",\"title\":\"IF条件\",\"icon\":\"if-condition\"},{\"key\":\"CV.is_image_exist\",\"title\":\"IF图像存在\",\"icon\":\"if-image-exists\"},{\"key\":\"Folder.folder_exist\",\"title\":\"IF文件夹存在\",\"icon\":\"check-folder-exists\"},{\"key\":\"File.file_exist\",\"title\":\"IF文件存在\",\"icon\":\"check-file-exists\"},{\"key\":\"Window.exist\",\"title\":\"IF窗口存在\",\"icon\":\"check-window-exists\"},{\"key\":\"Code.ElseIf\",\"title\":\"ELSE IF条件\",\"icon\":\"else-if-condition\"},{\"key\":\"Code.Else\",\"title\":\"ELSE条件\",\"icon\":\"else-condition\"}]},{\"key\":\"for\",\"title\":\"循环\",\"atomics\":[{\"key\":\"Code.ForDict\",\"title\":\"字典For循环\",\"icon\":\"dictionary-for-loop\"},{\"key\":\"Code.ForList\",\"title\":\"列表For循环\",\"icon\":\"list-for-loop\"},{\"key\":\"Code.ForStep\",\"title\":\"计数For循环\",\"icon\":\"count-for-loop\"},{\"key\":\"Excel.loop_excel_content\",\"title\":\"循环Excel内容\",\"icon\":\"excel-loop-content\"},{\"key\":\"BrowserElement.loop_similar\",\"title\":\"循环相似元素列表(web)\",\"icon\":\"loop-similar-elements-web\"},{\"key\":\"Code.While\",\"title\":\"While循环\",\"icon\":\"while-loop\"},{\"key\":\"Code.Break\",\"title\":\"退出循环(Break)\",\"icon\":\"break-loop\"},{\"key\":\"Code.Continue\",\"title\":\"继续下次循环(Continue)\",\"icon\":\"continue-next-loop\"}]},{\"key\":\"error\",\"title\":\"错误处理\",\"atomics\":[{\"key\":\"Code.Try\",\"title\":\"捕获异常(Try)\",\"icon\":\"try-exception\"},{\"key\":\"Code.Finally\",\"title\":\"捕获异常(Finally)\",\"icon\":\"finally-exception\"}]}]},{\"key\":\"web\",\"title\":\"网页自动化\",\"atomics\":[{\"key\":\"BrowserSoftware.browser_open\",\"title\":\"打开浏览器\",\"icon\":\"open-browser\"},{\"key\":\"BrowserSoftware.browser_close\",\"title\":\"关闭浏览器\",\"icon\":\"close-browser\"},{\"key\":\"BrowserSoftware.get_current_obj\",\"title\":\"获取已打开的浏览器对象\",\"icon\":\"get-open-browser-objects\"},{\"key\":\"BrowserElement.element_exist\",\"title\":\"元素是否存在(web)\",\"icon\":\"wait-element-web\"},{\"key\":\"BrowserElement.wait_element\",\"title\":\"等待元素(web)\",\"icon\":\"wait-element-web\"},{\"key\":\"BrowserElement.element_operation\",\"title\":\"元素操作(web)\",\"icon\":\"element-operation-web\"},{\"key\":\"BrowserElement.click\",\"title\":\"点击元素(web)\",\"icon\":\"click-element-web\"},{\"key\":\"BrowserElement.element_text\",\"title\":\"获取元素文本内容(web)\",\"icon\":\"get-element-text-web\"},{\"key\":\"BrowserElement.input\",\"title\":\"填写输入框(web)\",\"icon\":\"fill-input-web\"},{\"key\":\"BrowserElement.get_checked\",\"title\":\"获取复选框(web)\",\"icon\":\"get-checkbox-web\"},{\"key\":\"BrowserElement.set_checked\",\"title\":\"操作复选框(web)\",\"icon\":\"operate-checkbox-web\"},{\"key\":\"BrowserElement.get_select\",\"title\":\"获取下拉框(web)\",\"icon\":\"get-dropdown-web\"},{\"key\":\"BrowserElement.set_select\",\"title\":\"操作下拉框(web)\",\"icon\":\"operate-dropdown-web\"},{\"key\":\"BrowserElement.slider_hover\",\"title\":\"拾取滑块拖拽(web)\",\"icon\":\"pick-slider-drag-web\"},{\"key\":\"BrowserElement.hover_over\",\"title\":\"鼠标悬停在元素上(web)\",\"icon\":\"mouse-hover-element-web\"},{\"key\":\"BrowserElement.screenshot\",\"title\":\"拾取元素截图(web)\",\"icon\":\"pick-element-screenshot-web\"},{\"key\":\"BrowserElement.position_screenshot\",\"title\":\"元素位置截图(web)\",\"icon\":\"element-position-screenshot-web\"},{\"key\":\"BrowserElement.scroll_into_view\",\"title\":\"元素置于可视区域(web)\",\"icon\":\"element-to-visible-web\"},{\"key\":\"BrowserElement.get_table\",\"title\":\"获取表格数据(web)\",\"icon\":\"get-table-data-web\"},{\"key\":\"BrowserElement.data_batch\",\"title\":\"数据抓取(web)\",\"icon\":\"data-scraping-web\"},{\"key\":\"BrowserElement.similar\",\"title\":\"获取相似元素列表(web)\",\"icon\":\"get-similar-elements-web\"},{\"key\":\"BrowserElement.loop_similar\",\"title\":\"循环相似元素列表(web)\",\"icon\":\"loop-similar-elements-web\"},{\"key\":\"BrowserElement.create_element\",\"title\":\"获取元素对象(web)\",\"icon\":\"get-element-object-web\"},{\"key\":\"BrowserElement.get_relative_element\",\"title\":\"获取关联元素(web)\",\"icon\":\"get-related-elements-web\"},{\"key\":\"web.cookie\",\"title\":\"Cookie\",\"atomics\":[{\"key\":\"BrowserSoftware.set_cookies\",\"title\":\"设置Cookie\",\"icon\":\"set-cookie\"},{\"key\":\"BrowserSoftware.get_cookies\",\"title\":\"获取Cookie\",\"icon\":\"get-cookie\"}]},{\"key\":\"web.page\",\"title\":\"网页操作\",\"atomics\":[{\"key\":\"BrowserSoftware.web_open\",\"title\":\"打开新网页\",\"icon\":\"open-new-webpage\"},{\"key\":\"BrowserSoftware.get_current_tab_id\",\"title\":\"获取当前标签页ID\",\"icon\":\"get-current-tab-id\"},{\"key\":\"BrowserSoftware.web_switch\",\"title\":\"切换到已存在标签页\",\"icon\":\"switch-existing-tab\"},{\"key\":\"BrowserSoftware.web_close\",\"title\":\"关闭网页\",\"icon\":\"close-webpage\"},{\"key\":\"BrowserSoftware.web_refresh\",\"title\":\"刷新当前网页\",\"icon\":\"refresh-current-page\"},{\"key\":\"BrowserSoftware.stop_web_load\",\"title\":\"停止加载网页\",\"icon\":\"stop-loading-page\"},{\"key\":\"BrowserSoftware.browser_forward\",\"title\":\"网页前进\",\"icon\":\"webpage-forward\"},{\"key\":\"BrowserSoftware.browser_back\",\"title\":\"网页后退\",\"icon\":\"webpage-backward\"},{\"key\":\"BrowserSoftware.screenshot\",\"title\":\"网页截图\",\"icon\":\"webpage-screenshot\"},{\"key\":\"BrowserElement.scroll\",\"title\":\"鼠标滚动网页\",\"icon\":\"mouse-scroll-webpage\"},{\"key\":\"BrowserSoftware.get_current_url\",\"title\":\"获取网页URL\",\"icon\":\"get-webpage-url\"},{\"key\":\"BrowserSoftware.get_current_title\",\"title\":\"获取网页标题\",\"icon\":\"get-webpage-title\"},{\"key\":\"BrowserSoftware.wait_web_load\",\"title\":\"等待页面加载完成\",\"icon\":\"wait-page-load\"}]},{\"key\":\"web.file\",\"title\":\"网页文件\",\"atomics\":[{\"key\":\"BrowserSoftware.download_web_file\",\"title\":\"文件下载(web)\",\"icon\":\"file-download-web\"},{\"key\":\"BrowserSoftware.upload_web_file\",\"title\":\"文件上传(web)\",\"icon\":\"file-upload-web\"}]}]},{\"key\":\"desktop\",\"title\":\"桌面自动化\",\"atomics\":[{\"key\":\"Software.close\",\"title\":\"关闭程序\",\"icon\":\"close-program\"},{\"key\":\"Software.open\",\"title\":\"打开程序\",\"icon\":\"open-program\"},{\"key\":\"WinEle.click_element\",\"title\":\"点击元素(桌面)\",\"icon\":\"click-element-win\"},{\"key\":\"WinEle.screenshot_element\",\"title\":\"元素截图(桌面)\",\"icon\":\"element-screenshot-win\"},{\"key\":\"WinEle.hover_element\",\"title\":\"鼠标悬停元素(桌面)\",\"icon\":\"mouse-hover-element-win\"},{\"key\":\"WinEle.input_text_element\",\"title\":\"填写输入框(桌面)\",\"icon\":\"fill-input-win\"},{\"key\":\"WinEle.get_element_text\",\"title\":\"获取元素文本(桌面)\",\"icon\":\"get-element-text-win\"},{\"key\":\"WinEle.similar\",\"title\":\"获取相似元素列表(桌面)\",\"icon\":\"get-similar-elements-win\"},{\"key\":\"desktop.window\",\"title\":\"窗口操作\",\"atomics\":[{\"key\":\"Window.exist\",\"title\":\"IF窗口存在\",\"icon\":\"check-window-exists\"},{\"key\":\"Window.top\",\"title\":\"置顶窗口\",\"icon\":\"pin-window\"},{\"key\":\"Window.close\",\"title\":\"关闭窗口\",\"icon\":\"close-window\"},{\"key\":\"Window.set_size\",\"title\":\"调整窗口大小\",\"icon\":\"resize-window\"}]}]},{\"key\":\"document\",\"title\":\"文档处理\",\"atomics\":[{\"key\":\"document.PDF\",\"title\":\"PDF\",\"atomics\":[{\"key\":\"PDF.get_pages_num\",\"title\":\"获取PDF文档页数\",\"icon\":\"pdf-get-page-count\"},{\"key\":\"PDF.get_pdf_text\",\"title\":\"提取PDF文档文本\",\"icon\":\"pdf-extract-text\"},{\"key\":\"PDF.merge_pdf_files\",\"title\":\"合并PDF文件\",\"icon\":\"merge-pdf-files\"},{\"key\":\"PDF.get_pdf_images\",\"title\":\"提取PDF文档图片\",\"icon\":\"pdf-extract-images\"},{\"key\":\"PDF.extract_pdf_file\",\"title\":\"抽取PDF指定页\",\"icon\":\"pdf-extract-page\"},{\"key\":\"PDF.extract_forms_from_pdf\",\"title\":\"提取PDF表格到Excel\",\"icon\":\"pdf-extract-table-to-excel\"},{\"key\":\"PDF.convert_pdf_to_img\",\"title\":\"PDF页面转图片\",\"icon\":\"pdf-page-to-image\"}]},{\"key\":\"document.Word\",\"title\":\"Word\",\"atomics\":[{\"key\":\"Docx.open_document\",\"title\":\"打开Word\",\"icon\":\"word-open-document\"},{\"key\":\"Docx.read_document_content\",\"title\":\"读取Word内容\",\"icon\":\"word-read-content\"},{\"key\":\"Docx.create_docx\",\"title\":\"创建Word\",\"icon\":\"word-new-document\"},{\"key\":\"Docx.save_docx\",\"title\":\"保存Word\",\"icon\":\"word-save-document\"},{\"key\":\"Docx.close_docx\",\"title\":\"关闭Word\",\"icon\":\"word-close-document\"},{\"key\":\"Docx.insert_docx\",\"title\":\"插入文本到Word\",\"icon\":\"word-insert-text\"},{\"key\":\"Docx.select_text\",\"title\":\"选择Word文本\",\"icon\":\"word-select-text\"},{\"key\":\"Docx.get_cursor_position\",\"title\":\"定位Word光标\",\"icon\":\"word-position-cursor\"},{\"key\":\"Docx.move_cursor\",\"title\":\"移动Word光标\",\"icon\":\"word-move-cursor\"},{\"key\":\"Docx.insert_sep\",\"title\":\"Word插入页/段落\",\"icon\":\"word-insert-page\"},{\"key\":\"Docx.insert_hyperlink\",\"title\":\"Word插入超链接\",\"icon\":\"word-insert-hyperlink\"},{\"key\":\"Docx.insert_img\",\"title\":\"Word插入图片\",\"icon\":\"word-insert-image\"},{\"key\":\"Docx.read_table\",\"title\":\"Word读取表格\",\"icon\":\"word-read-table\"},{\"key\":\"Docx.insert_table\",\"title\":\"Word插入表格\",\"icon\":\"word-insert-table\"},{\"key\":\"Docx.delete\",\"title\":\"Word删除内容\",\"icon\":\"word-delete-content\"},{\"key\":\"Docx.replace\",\"title\":\"Word替换内容\",\"icon\":\"word-replace-content\"},{\"key\":\"Docx.create_comment\",\"title\":\"Word创建批注\",\"icon\":\"word-create-comment\"},{\"key\":\"Docx.delete_comment\",\"title\":\"Word删除批注\",\"icon\":\"word-delete-comment\"},{\"key\":\"Docx.convert_format\",\"title\":\"Word导出为PDF/TXT\",\"icon\":\"word-export-pdf-txt\"}]},{\"key\":\"document.Excel\",\"title\":\"Excel\",\"atomics\":[{\"key\":\"Excel.open_excel\",\"title\":\"打开Excel\",\"icon\":\"excel-open-file\"},{\"key\":\"Excel.add_excel_worksheet\",\"title\":\"添加Excel工作表\",\"icon\":\"add-excel-worksheet\"},{\"key\":\"Excel.get_excel\",\"title\":\"获取已打开的Excel对象\",\"icon\":\"get-open-excel-objects\"},{\"key\":\"Excel.loop_excel_content\",\"title\":\"循环Excel内容\",\"icon\":\"excel-loop-content\"},{\"key\":\"Excel.create_excel\",\"title\":\"创建Excel\",\"icon\":\"excel-create-file\"},{\"key\":\"Excel.save_excel\",\"title\":\"保存Excel\",\"icon\":\"excel-save-file\"},{\"key\":\"Excel.close_excel\",\"title\":\"关闭Excel\",\"icon\":\"excel-close-file\"},{\"key\":\"Excel.edit_excel\",\"title\":\"写入Excel\",\"icon\":\"excel-write-file\"},{\"key\":\"Excel.read_excel\",\"title\":\"读取Excel内容\",\"icon\":\"excel-read-content\"},{\"key\":\"Excel.design_cell_type\",\"title\":\"设置单元格格式\",\"icon\":\"excel-set-cell-format\"},{\"key\":\"Excel.copy_excel\",\"title\":\"复制Excel单元格\",\"icon\":\"excel-copy-cell\"},{\"key\":\"Excel.paste_excel\",\"title\":\"粘贴Excel单元格\",\"icon\":\"excel-paste-cell\"},{\"key\":\"Excel.delete_excel_cell\",\"title\":\"删除Excel单元格\",\"icon\":\"excel-delete-cell\"},{\"key\":\"Excel.get_excel_worksheet_names\",\"title\":\"获取Excel工作表名称\",\"icon\":\"excel-get-sheet-names\"},{\"key\":\"Excel.clear_excel_content\",\"title\":\"清除Excel区域内容\",\"icon\":\"excel-clear-range\"},{\"key\":\"Excel.insert_excel_row_or_column\",\"title\":\"插入Excel行或列\",\"icon\":\"excel-insert-row-column\"},{\"key\":\"Excel.get_excel_row_num\",\"title\":\"获取Excel行数\",\"icon\":\"excel-get-row-count\"},{\"key\":\"Excel.get_excel_col_num\",\"title\":\"获取Excel列数\",\"icon\":\"excel-get-column-count\"},{\"key\":\"Excel.get_excel_first_available_row\",\"title\":\"获取Excel第一个可用行\",\"icon\":\"excel-get-first-available-row\"},{\"key\":\"Excel.get_excel_first_available_col\",\"title\":\"获取Excel第一个可用列\",\"icon\":\"excel-get-first-available-column\"},{\"key\":\"Excel.excel_set_row_height\",\"title\":\"设置Excel行高\",\"icon\":\"excel-set-row-height\"},{\"key\":\"Excel.excel_set_col_width\",\"title\":\"设置Excel列宽\",\"icon\":\"excel-set-column-width\"},{\"key\":\"Excel.excel_get_cell_color\",\"title\":\"获取Excel单元格颜色\",\"icon\":\"excel-get-cell-color\"},{\"key\":\"Excel.merge_split_excel_cell\",\"title\":\"合并或拆分Excel单元格\",\"icon\":\"excel-split-cell\"},{\"key\":\"Excel.move_excel_worksheet\",\"title\":\"移动Excel工作表\",\"icon\":\"excel-move-sheet\"},{\"key\":\"Excel.delete_excel_worksheet\",\"title\":\"删除Excel工作表\",\"icon\":\"excel-delete-sheet\"},{\"key\":\"Excel.rename_excel_worksheet\",\"title\":\"重命名Excel工作表\",\"icon\":\"excel-rename-sheet\"},{\"key\":\"Excel.copy_excel_worksheet\",\"title\":\"复制Excel工作表\",\"icon\":\"excel-copy-sheet\"},{\"key\":\"Excel.search_and_replace_excel_content\",\"title\":\"查找或替换Excel内容\",\"icon\":\"excel-find-replace\"},{\"key\":\"Excel.insert_pic\",\"title\":\"插入Excel图片\",\"icon\":\"excel-insert-image\"},{\"key\":\"Excel.insert_formula\",\"title\":\"插入Excel公式\",\"icon\":\"excel-insert-formula\"},{\"key\":\"Excel.create_excel_comment\",\"title\":\"创建Excel批注\",\"icon\":\"excel-create-comment\"},{\"key\":\"Excel.delete_excel_comment\",\"title\":\"删除Excel批注\",\"icon\":\"excel-delete-comment\"},{\"key\":\"Excel.excel_text_to_number\",\"title\":\"Excel区域文本转数字\",\"icon\":\"excel-text-to-number\"},{\"key\":\"Excel.excel_number_to_text\",\"title\":\"Excel区域数字转文本\",\"icon\":\"excel-number-to-text\"}]}]},{\"key\":\"keyboard\",\"title\":\"鼠标键盘\",\"atomics\":[{\"key\":\"Gui.keyboard\",\"title\":\"键盘输入\",\"icon\":\"keyboard-input\"},{\"key\":\"Gui.mouse\",\"title\":\"鼠标点击\",\"icon\":\"mouse-click\"},{\"key\":\"Gui.mouse_wheel\",\"title\":\"鼠标滚动\",\"icon\":\"mouse-scroll-webpage\"},{\"key\":\"Gui.mouse_move\",\"title\":\"鼠标移动\",\"icon\":\"mouse-move\"},{\"key\":\"Gui.mouse_drag\",\"title\":\"鼠标拖拽\",\"icon\":\"mouse-drag\"},{\"key\":\"Gui.mouse_position\",\"title\":\"获取鼠标位置\",\"icon\":\"get-mouse-position\"},{\"key\":\"Gui.key_input\",\"title\":\"键盘模拟按键\",\"icon\":\"keyboard-simulate-key\"}]},{\"key\":\"data\",\"title\":\"数据处理\",\"atomics\":[{\"key\":\"data.Math\",\"title\":\"数学操作\",\"atomics\":[{\"key\":\"MathProcess.generate_random_number\",\"title\":\"生成随机数\",\"icon\":\"generate-random-number\"},{\"key\":\"MathProcess.get_rounding_number\",\"title\":\"四舍五入\",\"icon\":\"round-number\"},{\"key\":\"MathProcess.self_calculation_number\",\"title\":\"自增自减\",\"icon\":\"increment-decrement\"},{\"key\":\"MathProcess.get_absolute_number\",\"title\":\"获取绝对值\",\"icon\":\"get-absolute-value\"},{\"key\":\"MathProcess.calculate_expression\",\"title\":\"数学计算\",\"icon\":\"math-calculation\"}]},{\"key\":\"data.String\",\"title\":\"字符串操作\",\"atomics\":[{\"key\":\"StringProcess.extract_content_from_string\",\"title\":\"文本提取内容\",\"icon\":\"text-extract-content\"},{\"key\":\"StringProcess.replace_content_in_string\",\"title\":\"文本替换内容\",\"icon\":\"text-replace-content\"},{\"key\":\"StringProcess.merge_list_to_string\",\"title\":\"列表聚合为文本\",\"icon\":\"list-to-text\"},{\"key\":\"StringProcess.split_string_to_list\",\"title\":\"文本分割为列表\",\"icon\":\"text-split-to-list\"},{\"key\":\"StringProcess.concatenate_string\",\"title\":\"文本合并\",\"icon\":\"text-merge\"},{\"key\":\"StringProcess.fill_string_to_length\",\"title\":\"文本补齐至固定长度\",\"icon\":\"text-pad-to-length\"},{\"key\":\"StringProcess.strip_string\",\"title\":\"文本去除两侧空格\",\"icon\":\"text-trim-spaces\"},{\"key\":\"StringProcess.cut_string_to_length\",\"title\":\"截取固定长度文本\",\"icon\":\"screenshot-fixed-text\"},{\"key\":\"StringProcess.change_case_of_string\",\"title\":\"更改文本大小写\",\"icon\":\"change-text-case\"},{\"key\":\"StringProcess.get_string_length\",\"title\":\"获取文本长度\",\"icon\":\"get-text-length\"},{\"key\":\"DataConvertProcess.json_convertor\",\"title\":\"JSON字符串互转\",\"icon\":\"json-string-convert\"},{\"key\":\"DataConvertProcess.other_to_str\",\"title\":\"其他格式转文本\",\"icon\":\"format-to-text\"},{\"key\":\"DataConvertProcess.str_to_other\",\"title\":\"文本转其他格式\",\"icon\":\"text-convert-format\"}]},{\"key\":\"data.List\",\"title\":\"列表操作\",\"atomics\":[{\"key\":\"ListProcess.create_new_list\",\"title\":\"创建新列表\",\"icon\":\"create-new-list\"},{\"key\":\"ListProcess.clear_list\",\"title\":\"清空列表\",\"icon\":\"list-clear\"},{\"key\":\"ListProcess.insert_value_to_list\",\"title\":\"列表插入值\",\"icon\":\"list-insert-value\"},{\"key\":\"ListProcess.change_value_in_list\",\"title\":\"列表修改值\",\"icon\":\"list-modify-value\"},{\"key\":\"ListProcess.get_list_position\",\"title\":\"获取值在列表位置\",\"icon\":\"get-value-position\"},{\"key\":\"ListProcess.remove_value_from_list\",\"title\":\"列表删除值\",\"icon\":\"list-remove-value\"},{\"key\":\"ListProcess.sort_list\",\"title\":\"列表排序\",\"icon\":\"list-sort\"},{\"key\":\"ListProcess.random_shuffle_list\",\"title\":\"列表随机打乱顺序\",\"icon\":\"list-shuffle\"},{\"key\":\"ListProcess.filter_elements_from_list\",\"title\":\"剔除列表中的多项\",\"icon\":\"list-remove-multiple\"},{\"key\":\"ListProcess.reverse_list\",\"title\":\"列表反转\",\"icon\":\"list-reverse\"},{\"key\":\"ListProcess.merge_list\",\"title\":\"列表合并\",\"icon\":\"list-merge\"},{\"key\":\"ListProcess.get_unique_list\",\"title\":\"列表去重\",\"icon\":\"list-remove-duplicates\"},{\"key\":\"ListProcess.get_common_elements_from_list\",\"title\":\"获取两个列表的重复项\",\"icon\":\"get-list-duplicates\"},{\"key\":\"ListProcess.get_value_from_list\",\"title\":\"根据索引获取列表值\",\"icon\":\"get-list-value-by-index\"},{\"key\":\"ListProcess.get_length_of_list\",\"title\":\"获取列表长度\",\"icon\":\"get-list-length\"}]},{\"key\":\"data.Dict\",\"title\":\"字典操作\",\"atomics\":[{\"key\":\"DictProcess.create_new_dict\",\"title\":\"创建新字典\",\"icon\":\"create-new-dict\"},{\"key\":\"DictProcess.set_value_to_dict\",\"title\":\"字典设置值\",\"icon\":\"dict-set-value\"},{\"key\":\"DictProcess.delete_value_from_dict\",\"title\":\"字典删除值\",\"icon\":\"dict-delete-value\"},{\"key\":\"DictProcess.get_value_from_dict\",\"title\":\"字典获取值\",\"icon\":\"dict-get-value\"},{\"key\":\"DictProcess.get_keys_from_dict\",\"title\":\"获取字典所有键\",\"icon\":\"get-dict-all-keys\"},{\"key\":\"DictProcess.get_values_from_dict\",\"title\":\"获取字典所有值\",\"icon\":\"get-dict-all-values\"}]},{\"key\":\"data.Time\",\"title\":\"时间操作\",\"atomics\":[{\"key\":\"TimeProcess.get_current_time\",\"title\":\"获取当前时间\",\"icon\":\"get-current-time\"},{\"key\":\"TimeProcess.set_time\",\"title\":\"设置时间\",\"icon\":\"set-time\"},{\"key\":\"TimeProcess.time_to_timestamp\",\"title\":\"时间对象转时间戳\",\"icon\":\"datetime-to-timestamp\"},{\"key\":\"TimeProcess.timestamp_to_time\",\"title\":\"时间戳转时间对象\",\"icon\":\"timestamp-to-datetime\"},{\"key\":\"TimeProcess.get_time_difference\",\"title\":\"获取时间差\",\"icon\":\"get-time-difference\"},{\"key\":\"TimeProcess.format_datetime\",\"title\":\"输出指定格式时间文本\",\"icon\":\"output-formatted-time\"}]}]},{\"key\":\"os\",\"title\":\"操作系统\",\"atomics\":[{\"key\":\"os.file\",\"title\":\"文件操作\",\"atomics\":[{\"key\":\"File.file_exist\",\"title\":\"IF文件存在\",\"icon\":\"check-file-exists\"},{\"key\":\"File.file_create\",\"title\":\"创建文件\",\"icon\":\"create-new-file\"},{\"key\":\"File.file_write\",\"title\":\"写入文件\",\"icon\":\"write-file\"},{\"key\":\"File.file_read\",\"title\":\"读取文件\",\"icon\":\"read-file\"},{\"key\":\"File.file_copy\",\"title\":\"复制文件\",\"icon\":\"copy-file\"},{\"key\":\"File.file_move\",\"title\":\"移动文件\",\"icon\":\"move-file\"},{\"key\":\"File.file_rename\",\"title\":\"重命名文件\",\"icon\":\"rename-file\"},{\"key\":\"File.file_delete\",\"title\":\"删除文件\",\"icon\":\"delete-file\"},{\"key\":\"File.file_search\",\"title\":\"查找文件\",\"icon\":\"find-file\"},{\"key\":\"File.file_wait_status\",\"title\":\"等待文件\",\"icon\":\"wait-file\"},{\"key\":\"File.get_file_encoding_type\",\"title\":\"获取文件编码类型\",\"icon\":\"get-file-encoding\"},{\"key\":\"File.file_info\",\"title\":\"获取文件信息\",\"icon\":\"get-file-info\"},{\"key\":\"File.get_file_list\",\"title\":\"获取文件列表\",\"icon\":\"get-file-list\"},{\"key\":\"Enterprise.upload_to_sharefolder\",\"title\":\"上传文件至共享文件夹\",\"icon\":\"upload-to-shared-folder\"},{\"key\":\"Enterprise.download_from_sharefolder\",\"title\":\"从共享文件夹下载文件\",\"icon\":\"download-from-shared-folder\"}]},{\"key\":\"os.path\",\"title\":\"文件夹操作\",\"atomics\":[{\"key\":\"Folder.folder_exist\",\"title\":\"IF文件夹存在\",\"icon\":\"check-folder-exists\"},{\"key\":\"Folder.folder_create\",\"title\":\"创建文件夹\",\"icon\":\"create-folder\"},{\"key\":\"Folder.folder_open\",\"title\":\"打开文件夹\",\"icon\":\"open-folder\"},{\"key\":\"Folder.folder_copy\",\"title\":\"复制文件夹\",\"icon\":\"copy-folder\"},{\"key\":\"Folder.folder_move\",\"title\":\"移动文件夹\",\"icon\":\"move-folder\"},{\"key\":\"Folder.folder_rename\",\"title\":\"重命名文件夹\",\"icon\":\"rename-folder\"},{\"key\":\"Folder.folder_clear\",\"title\":\"清空文件夹\",\"icon\":\"clear-folder\"},{\"key\":\"Folder.folder_delete\",\"title\":\"删除文件夹\",\"icon\":\"delete-folder-ftp\"},{\"key\":\"Folder.get_folder_list\",\"title\":\"获取文件夹列表\",\"icon\":\"get-folder-list\"}]},{\"key\":\"os.zip\",\"title\":\"压缩/解压\",\"atomics\":[{\"key\":\"System.compress\",\"title\":\"压缩\",\"icon\":\"compress\"},{\"key\":\"System.uncompress\",\"title\":\"解压\",\"icon\":\"decompress\"}]},{\"key\":\"os.system\",\"title\":\"系统命令\",\"atomics\":[{\"key\":\"System.run_command\",\"title\":\"运行或打开\",\"icon\":\"run-or-open\"},{\"key\":\"System.get_pid\",\"title\":\"获取进程PID\",\"icon\":\"get-process-pid\"},{\"key\":\"System.terminate_process\",\"title\":\"终止进程\",\"icon\":\"terminate-process\"}]},{\"key\":\"os.screenshot\",\"title\":\"截图\",\"atomics\":[{\"key\":\"System.screen_shot\",\"title\":\"屏幕截图\",\"icon\":\"screen-screenshot\"}]},{\"key\":\"os.clipboard\",\"title\":\"剪切板\",\"atomics\":[{\"key\":\"System.copy_clip\",\"title\":\"复制到剪切板\",\"icon\":\"copy-to-clipboard\"},{\"key\":\"System.clear_clip\",\"title\":\"清空剪切板\",\"icon\":\"clear-clipboard\"},{\"key\":\"System.paste_clip\",\"title\":\"获取剪切板\",\"icon\":\"get-clipboard\"}]},{\"key\":\"encrypt\",\"title\":\"加解密/编解码\",\"atomics\":[{\"key\":\"Encrypt.sha_encrypt\",\"title\":\"SHA加密\",\"icon\":\"sha-encrypt\"},{\"key\":\"Encrypt.md5_encrypt\",\"title\":\"MD5加密\",\"icon\":\"md5-encrypt\"},{\"key\":\"Encrypt.symmetric_decrypt\",\"title\":\"对称解密\",\"icon\":\"symmetric-decrypt\"},{\"key\":\"Encrypt.symmetric_encrypt\",\"title\":\"对称加密\",\"icon\":\"symmetric-encrypt\"},{\"key\":\"Encrypt.base64_decoding\",\"title\":\"Base64解码\",\"icon\":\"base64-decode\"},{\"key\":\"Encrypt.base64_encoding\",\"title\":\"Base64编码\",\"icon\":\"base64-encode\"}]}]},{\"key\":\"network\",\"title\":\"网络\",\"atomics\":[{\"key\":\"email\",\"title\":\"邮件\",\"atomics\":[{\"key\":\"Email.send_email\",\"title\":\"发送邮件\",\"icon\":\"send-email\"},{\"key\":\"Email.receive_email\",\"title\":\"接收邮件\",\"icon\":\"receive-email\"}]},{\"key\":\"http\",\"title\":\"HTTP\",\"atomics\":[{\"key\":\"Network.http_request\",\"title\":\"HTTP请求\",\"icon\":\"http-request\"},{\"key\":\"Network.http_download\",\"title\":\"HTTP下载\",\"icon\":\"http-download\"}]},{\"key\":\"ftp\",\"title\":\"FTP\",\"atomics\":[{\"key\":\"Network.ftp_create\",\"title\":\"创建FTP连接\",\"icon\":\"ftp-create-connection\"},{\"key\":\"Network.ftp_close\",\"title\":\"关闭FTP连接\",\"icon\":\"ftp-close-connection\"},{\"key\":\"Network.get_work_dir\",\"title\":\"获取工作目录(FTP)\",\"icon\":\"get-work-directory\"},{\"key\":\"Network.change_working_dir\",\"title\":\"切换工作目录(FTP)\",\"icon\":\"change-work-directory\"},{\"key\":\"Network.create_folder\",\"title\":\"创建文件夹(FTP)\",\"icon\":\"create-folder\"},{\"key\":\"Network.get_ftp_list\",\"title\":\"获取文件/文件夹(FTP)\",\"icon\":\"get-folder\"},{\"key\":\"Network.ftp_upload\",\"title\":\"上传文件/文件夹(FTP)\",\"icon\":\"upload-folder\"},{\"key\":\"Network.ftp_rename\",\"title\":\"重命名文件/文件夹(FTP)\",\"icon\":\"rename-folder\"},{\"key\":\"Network.ftp_download\",\"title\":\"下载文件/文件夹(FTP)\",\"icon\":\"download-folder\"},{\"key\":\"Network.ftp_delete\",\"title\":\"删除文件/文件夹(FTP)\",\"icon\":\"delete-folder-ftp\"}]}]},{\"key\":\"cv\",\"title\":\"CV图像\",\"atomics\":[{\"key\":\"CV.is_image_exist\",\"title\":\"IF图像存在\",\"icon\":\"if-image-exists\"},{\"key\":\"CV.cv_click\",\"title\":\"点击图像\",\"icon\":\"click-image\"},{\"key\":\"CV.hover_image\",\"title\":\"鼠标悬浮在图像上\",\"icon\":\"mouse-hover-image\"},{\"key\":\"CV.wait_image\",\"title\":\"等待图像\",\"icon\":\"wait-image\"},{\"key\":\"CV.image_input\",\"title\":\"图像输入框输入\",\"icon\":\"image-input-box\"}]},{\"key\":\"dialog\",\"title\":\"对话框\",\"atomics\":[{\"key\":\"Dialog.message_box\",\"title\":\"消息提示框\",\"icon\":\"message-dialog\"},{\"key\":\"Dialog.input_box\",\"title\":\"输入对话框\",\"icon\":\"input-dialog\"},{\"key\":\"Dialog.select_box\",\"title\":\"选择对话框\",\"icon\":\"select-dialog\"},{\"key\":\"Dialog.select_time_box\",\"title\":\"日期时间选择框\",\"icon\":\"datetime-picker\"},{\"key\":\"Dialog.select_file_box\",\"title\":\"文件选择对话框\",\"icon\":\"file-select-dialog\"},{\"key\":\"Dialog.custom_box\",\"title\":\"自定义对话框\",\"icon\":\"custom-dialog\"}]},{\"key\":\"script\",\"title\":\"自定义脚本\",\"atomics\":[{\"key\":\"BrowserScript.js_run\",\"title\":\"JS脚本\",\"icon\":\"js-script\"},{\"key\":\"Script.module\",\"title\":\"运行Python模块\",\"icon\":\"run-python-module\"}]},{\"key\":\"remote\",\"title\":\"卓越中心\",\"atomics\":[{\"key\":\"Enterprise.get_shared_variable\",\"title\":\"获取共享变量\",\"icon\":\"get-shared-variable\"},{\"key\":\"Enterprise.upload_to_sharefolder\",\"title\":\"上传文件至共享文件夹\",\"icon\":\"upload-to-shared-folder\"},{\"key\":\"Enterprise.download_from_sharefolder\",\"title\":\"从共享文件夹下载文件\",\"icon\":\"download-from-shared-folder\"}]}],\"atomicTreeExtend\":[{\"key\":\"feishu\",\"title\":\"飞书\",\"atomics\":[{\"key\":\"FeishuBase.connect_base\",\"title\":\"连接到多维表格\",\"icon\":\"connect-to-multi-sheet\"},{\"key\":\"FeishuBase.search_records\",\"title\":\"列出记录(筛选)\",\"icon\":\"list-records-filtered\"},{\"key\":\"FeishuBase.read_records\",\"title\":\"列出记录(指定)\",\"icon\":\"list-records-specified\"},{\"key\":\"FeishuBase.create_records\",\"title\":\"创建记录\",\"icon\":\"create-record\"},{\"key\":\"FeishuBase.update_records\",\"title\":\"更新记录\",\"icon\":\"update-record\"},{\"key\":\"FeishuBase.delete_records\",\"title\":\"删除记录\",\"icon\":\"delete-record\"},{\"key\":\"FeishuBase.get_table_list\",\"title\":\"列出数据表\",\"icon\":\"list-tables\"},{\"key\":\"FeishuBase.add_table\",\"title\":\"新增数据表\",\"icon\":\"add-table\"},{\"key\":\"FeishuBase.delete_table\",\"title\":\"删除数据表\",\"icon\":\"delete-table\"},{\"key\":\"FeishuBase.update_table\",\"title\":\"重命名数据表\",\"icon\":\"rename-table\"},{\"key\":\"FeishuBase.get_field_list\",\"title\":\"列出字段\",\"icon\":\"list-fields\"},{\"key\":\"FeishuBase.add_field\",\"title\":\"新增字段\",\"icon\":\"add-field\"},{\"key\":\"FeishuBase.update_field\",\"title\":\"更新字段\",\"icon\":\"update-field\"},{\"key\":\"FeishuBase.delete_field\",\"title\":\"删除字段\",\"icon\":\"delete-field\"},{\"key\":\"FeishuSheet.connect_spreadsheet\",\"title\":\"连接数据表\",\"icon\":\"connect-table\"},{\"key\":\"FeishuSheet.get_sheet_info\",\"title\":\"获取工作表信息\",\"icon\":\"get-worksheet-info\"},{\"key\":\"FeishuSheet.set_filter\",\"title\":\"设置筛选器\",\"icon\":\"set-filter\"},{\"key\":\"FeishuSheet.get_filter\",\"title\":\"获取筛选结果\",\"icon\":\"get-filter-result\"},{\"key\":\"FeishuSheet.read_data\",\"title\":\"读取工作表数据\",\"icon\":\"read-worksheet-data\"},{\"key\":\"FeishuSheet.write_data\",\"title\":\"写入数据\",\"icon\":\"write-data\"}]}],\"commonAdvancedParameter\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RADIO\",\"params\":{}},\"key\":\"__res_print__\",\"title\":\"打印输出变量值\",\"name\":\"__res_print__\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{}},\"key\":\"__delay_before__\",\"title\":\"执行前延迟(秒)\",\"name\":\"__delay_before__\",\"default\":0},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"__delay_after__\",\"title\":\"执行后延迟(秒)\",\"name\":\"__delay_after__\",\"default\":0},{\"types\":\"Str\",\"formType\":{\"type\":\"RADIO\",\"params\":{}},\"key\":\"__skip_err__\",\"title\":\"执行异常时\",\"name\":\"__skip_err__\",\"options\":[{\"label\":\"退出\",\"value\":\"exit\"},{\"label\":\"跳过\",\"value\":\"skip\"},{\"label\":\"重试\",\"value\":\"retry\"}],\"default\":\"exit\"},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"__retry_time__\",\"title\":\"重试次数(次)\",\"name\":\"__retry_time__\",\"dynamics\":[{\"key\":\"$this.__retry_time__.show\",\"expression\":\"return $this.__skip_err__.value == \'retry\'\"}],\"default\":0},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"__retry_interval__\",\"title\":\"重试间隔(秒)\",\"name\":\"__retry_interval__\",\"dynamics\":[{\"key\":\"$this.__retry_interval__.show\",\"expression\":\"return $this.__skip_err__.value == \'retry\'\"}],\"default\":0}],\"types\":{\"Any\":{\"key\":\"Any\",\"src\":\"\",\"desc\":\"任意值\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"任何值\",\"funcList\":[]},\"Float\":{\"key\":\"Float\",\"src\":\"\",\"desc\":\"数值\",\"version\":\"1.1.47\",\"channel\":\"global,main\",\"template\":\"10.1\",\"funcList\":[{\"key\":\"Float.toStr\",\"funcDesc\":\"转文本\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"str(@{self:self})\"},{\"key\":\"Float.toInt\",\"funcDesc\":\"取整数部分\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"int(@{self:self})\"}]},\"Int\":{\"key\":\"Int\",\"src\":\"\",\"desc\":\"整数\",\"version\":\"1.1.47\",\"channel\":\"global,main\",\"template\":\"10\",\"funcList\":[{\"key\":\"Int.toStr\",\"funcDesc\":\"转文本\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"str(@{self:self})\"}]},\"Bool\":{\"key\":\"Bool\",\"src\":\"\",\"desc\":\"布尔值\",\"version\":\"1.1.47\",\"channel\":\"global,main\",\"template\":\"true或者false\",\"funcList\":[]},\"Str\":{\"key\":\"Str\",\"src\":\"\",\"desc\":\"字符串\",\"version\":\"1.1.47\",\"channel\":\"global,main\",\"template\":\"“你好”\",\"funcList\":[{\"key\":\"Str.strip\",\"funcDesc\":\"删除两端空格\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.strip()\"},{\"key\":\"Str.toInt\",\"funcDesc\":\"转整数\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"int(@{self:self})\"},{\"key\":\"Str.toFloat\",\"funcDesc\":\"转数值\",\"resType\":\"Float\",\"resDesc\":\"数值\",\"useSrc\":\"float(@{self:self})\"}]},\"List\":{\"key\":\"List\",\"src\":\"\",\"desc\":\"列表\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"[1,2,3]\",\"funcList\":[{\"key\":\"List.get\",\"funcDesc\":\"列表第@{p1:int}项\",\"resType\":\"Any\",\"resDesc\":\"任意值\",\"useSrc\":\"@{self:self}[@(p1:int)]\"},{\"key\":\"List.getEnd\",\"funcDesc\":\"列表倒数第@{p1:int}项\",\"resType\":\"Any\",\"resDesc\":\"任意值\",\"useSrc\":\"@{self:self}[-@(p1:int)]\"},{\"key\":\"List.getLen\",\"funcDesc\":\"列表长度\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"len(@{self:self})\"},{\"key\":\"List.getSlice\",\"funcDesc\":\"列表第@{p1:int}项到第@{p2:int}项\",\"resType\":\"List\",\"resDesc\":\"列表\",\"useSrc\":\"@{self:self}[@(p1:int):@(p2:int)]\"}]},\"Dict\":{\"key\":\"Dict\",\"src\":\"\",\"desc\":\"字典\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"{\\\"name\\\":\\\"小明\\\"}\",\"funcList\":[{\"key\":\"Dict.get\",\"funcDesc\":\"字典键@{p1:str}的值\",\"resType\":\"Any\",\"resDesc\":\"任意值\",\"useSrc\":\"@{self:self}[\\\"@(p1:str)\\\"]\"},{\"key\":\"List.getLen\",\"funcDesc\":\"字典包含元素个数\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"len(@{self:self})\"}]},\"PATH\":{\"key\":\"PATH\",\"src\":\"\",\"desc\":\"文件路径\",\"version\":\"1.1.47\",\"channel\":\"global,main\",\"template\":\"C://Users\",\"funcList\":[{\"key\":\"PATH.root\",\"funcDesc\":\"根目录\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.root()\"},{\"key\":\"PATH.directory\",\"funcDesc\":\"父目录\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.directory()\"},{\"key\":\"PATH.file_name\",\"funcDesc\":\"文件名称\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.file_name()\"},{\"key\":\"PATH.file_name_without_extension\",\"funcDesc\":\"文件名称(不带扩展名)\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.file_name_without_extension()\"},{\"key\":\"PATH.file_extension\",\"funcDesc\":\"文件扩展名\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.file_extension()\"}]},\"DIRPATH\":{\"key\":\"DIRPATH\",\"src\":\"\",\"desc\":\"文件夹路径\",\"version\":\"1.1.79\",\"channel\":\"global,main\",\"template\":\"C://Users\",\"funcList\":[{\"key\":\"DIRPATH.root\",\"funcDesc\":\"root\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.root()\"},{\"key\":\"DIRPATH.directory\",\"funcDesc\":\"directory\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.directory()\"}]},\"Date\":{\"key\":\"Date\",\"src\":\"\",\"desc\":\"日期时间\",\"version\":\"1.1.53\",\"channel\":\"global,main\",\"template\":\"2025-01-10 17:00:00\",\"funcList\":[{\"key\":\"Date.get_time_year\",\"funcDesc\":\"获取年份\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_year()\"},{\"key\":\"Date.get_time_month\",\"funcDesc\":\"获取月份\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_month()\"},{\"key\":\"Date.get_time_day\",\"funcDesc\":\"获取日\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_day()\"},{\"key\":\"Date.get_time_hour\",\"funcDesc\":\"获取小时\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_hour()\"},{\"key\":\"Date.get_time_minute\",\"funcDesc\":\"获取分钟\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_minute()\"},{\"key\":\"Date.get_time_second\",\"funcDesc\":\"获取秒\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_second()\"},{\"key\":\"Date.get_time_weekday\",\"funcDesc\":\"获取周几\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_weekday()\"},{\"key\":\"Date.get_time_week\",\"funcDesc\":\"获取周数\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_time_week()\"}]},\"URL\":{\"key\":\"URL\",\"src\":\"\",\"desc\":\"地址\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"https://www.iflytek.com/\",\"funcList\":[]},\"Pick\":{\"key\":\"Pick\",\"src\":\"\",\"desc\":\"元素\",\"version\":\"1.1.47\",\"channel\":\"\",\"template\":\"JSON字符串\",\"funcList\":[]},\"WebPick\":{\"key\":\"WebPick\",\"src\":\"\",\"desc\":\"网页元素\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"JSON字符串\",\"funcList\":[]},\"WinPick\":{\"key\":\"WinPick\",\"src\":\"\",\"desc\":\"桌面元素\",\"version\":\"1.1.47\",\"channel\":\"global\",\"template\":\"JSON字符串\",\"funcList\":[]},\"IMGPick\":{\"key\":\"IMGPick\",\"src\":\"\",\"desc\":\"图像元素\",\"version\":\"1.1.79\",\"channel\":\"global\",\"template\":\"JSON字符串\",\"funcList\":[]},\"Password\":{\"key\":\"Password\",\"src\":\"\",\"desc\":\"密码\",\"version\":\"1.1.51\",\"channel\":\"global,main\",\"template\":\"******\",\"funcList\":[]},\"FeishuBaseInstance\":{\"key\":\"FeishuBaseInstance\",\"src\":\"\",\"desc\":\"飞书对象\",\"version\":\"1.1.47\",\"channel\":\"\",\"template\":\"飞书对象.\",\"funcList\":[]},\"DialogResult\":{\"key\":\"DialogResult\",\"src\":\"\",\"desc\":\"对话框输出结果\",\"version\":\"1.1.47\",\"channel\":\"\",\"template\":\"JSON字符串\",\"funcList\":[]},\"Browser\":{\"key\":\"Browser\",\"src\":\"rpabrowser.browser.Browser()\",\"desc\":\"浏览器对象\",\"version\":\"1.0.22\",\"channel\":\"global\",\"template\":\"Browser对象\",\"funcList\":[{\"key\":\"Browser.get_url\",\"funcDesc\":\"该网页的地址\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.get_url()\"},{\"key\":\"Browser.get_title\",\"funcDesc\":\"该网页的标题\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.get_title()\"}]},\"DocumentObject\":{\"key\":\"DocumentObject\",\"src\":\"astronverse.word.docx_obj.DocumentObject()\",\"desc\":\"Word对象\",\"version\":\"1.0.1\",\"channel\":\"global\",\"template\":\"DocumentObject对象\",\"funcList\":[]},\"ExcelObj\":{\"key\":\"ExcelObj\",\"src\":\"astronverse.excel.excel_obj.ExcelObj()\",\"desc\":\"Excel对象\",\"version\":\"1.0.38\",\"channel\":\"global\",\"template\":\"ExcelObj对象\",\"funcList\":[{\"key\":\"ExcelObj.get_full_name\",\"funcDesc\":\"文件所在位置\",\"resType\":\"Str\",\"resDesc\":\"字符串\",\"useSrc\":\"@{self:self}.get_full_name()\"},{\"key\":\"ExcelObj.get_first_free_row\",\"funcDesc\":\"第一个可用行\",\"resType\":\"Int\",\"resDesc\":\"整数\",\"useSrc\":\"@{self:self}.get_first_free_row()\"}]}}}',0,'1','2025-02-21 19:54:57',1,'2025-10-15 17:38:05','1',NULL,'1000000'),(15,'ai/aim','Agent.call_dify','{\"key\":\"Agent.call_dify\",\"title\":\"调用Dify流程\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.agent.Agent().call_dify\",\"comment\":\"调用Dify流程 @{app_token} ,完成您指定的任务。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"user\",\"title\":\"用户名\",\"name\":\"user\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"app_token\",\"title\":\"Dify流程密钥\",\"name\":\"app_token\",\"tip\":\"Dify流程密钥\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"file_flag\",\"title\":\"是否上传文件\",\"name\":\"file_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"variable_name\",\"title\":\"变量名\",\"name\":\"variable_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"variable_value\",\"title\":\"变量值\",\"name\":\"variable_value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.variable_value.show\",\"expression\":\"return $this.file_flag.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.file_flag.value == true\"}],\"required\":true},{\"types\":\"DifyFileTypes\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"file_type\",\"title\":\"文件类型\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"文档\",\"value\":\"document\"},{\"label\":\"图像\",\"value\":\"image\"},{\"label\":\"视频\",\"value\":\"video\"},{\"label\":\"音频\",\"value\":\"audio\"},{\"label\":\"其他格式\",\"value\":\"custom\"}],\"default\":\"document\",\"dynamics\":[{\"key\":\"$this.file_type.show\",\"expression\":\"return $this.file_flag.value == true\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"dify_result\",\"title\":\"Dify流程结果输出\",\"tip\":\"\"}],\"icon\":\"call-dify-workflow\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(16,'ai/aim','Agent.call_xcagent','{\"key\":\"Agent.call_xcagent\",\"title\":\"调用星辰Agent流程\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.agent.Agent().call_xcagent\",\"comment\":\"调用星辰Agent流程 @{flow_id} ,完成您指定的任务。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"api_key\",\"title\":\"api_key\",\"name\":\"api_key\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"api_secret\",\"title\":\"api_secret\",\"name\":\"api_secret\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"flow_id\",\"title\":\"流程id\",\"name\":\"flow_id\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"input_value\",\"title\":\"工作流默认输入\",\"name\":\"input_value\",\"tip\":\"\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"xcagent_result\",\"title\":\"星辰Agent返回值\",\"tip\":\"\"}],\"icon\":\"call-dify-workflow\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(17,'ai/aim','ChatAI.single_turn_chat','{\"key\":\"ChatAI.single_turn_chat\",\"title\":\"单轮会话\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.chat.ChatAI().single_turn_chat\",\"comment\":\"大模型将对你提出的问题: @{query} 进行回答。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"query\",\"title\":\"用户输入\",\"name\":\"query\",\"tip\":\"用户输入的问题。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"LLMModelTypes\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"model\",\"title\":\"模型选择\",\"name\":\"model\",\"tip\":\"可以选择使用的模型。\",\"options\":[{\"label\":\"DeepSeek-V3\",\"value\":\"deepseek-v3-0324\"},{\"label\":\"DeepSeek-R1\",\"value\":\"claude-4-sonnet\"}],\"default\":\"deepseek-v3-0324\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"single_chat_res\",\"title\":\"答案输出\",\"tip\":\"将大模型生成的答案输出为变量。\"}],\"icon\":\"single-chat\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(18,'ai/aim','ChatAI.chat','{\"key\":\"ChatAI.chat\",\"title\":\"多轮会话\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.chat.ChatAI().chat\",\"comment\":\"大模型将扮演角色,并进行最多 (@{max_turns}) 问答次数。\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"保存会话\",\"name\":\"is_save\",\"tip\":\"是否在对话结束时保存对话。\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"title\",\"title\":\"窗口标题\",\"name\":\"title\",\"tip\":\"定义对话窗口标题。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"max_turns\",\"title\":\"最大问答次数\",\"name\":\"max_turns\",\"tip\":\"最大问答次数,完整的Q&A(question & answer)为一次。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"LLMModelTypes\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"model\",\"title\":\"模型选择\",\"name\":\"model\",\"tip\":\"可以选择使用的模型。\",\"options\":[{\"label\":\"DeepSeek-V3\",\"value\":\"deepseek-v3-0324\"},{\"label\":\"DeepSeek-R1\",\"value\":\"claude-4-sonnet\"}],\"default\":\"deepseek-v3-0324\",\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"chat_res\",\"title\":\"对话聊天记录输出\",\"tip\":\"将对话聊天记录输出为变量。\"}],\"icon\":\"multi-chat\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(19,'ai/aim','ChatAI.knowledge_chat','{\"key\":\"ChatAI.knowledge_chat\",\"title\":\"知识问答\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.chat.ChatAI().knowledge_chat\",\"comment\":\"大模型将读取的文件位于 @{file_path} 。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"请选择需要进行知识问答的文件路径。\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"保存会话\",\"name\":\"is_save\",\"tip\":\"是否在对话结束时保存对话。\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"max_turns\",\"title\":\"最大问答次数\",\"name\":\"max_turns\",\"tip\":\"最大问答次数,完整的Q&A(question & answer)为一次。\",\"default\":20,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"knowledge_chat_res\",\"title\":\"对话聊天记录输出\",\"tip\":\"将对话聊天记录输出为变量。\"}],\"icon\":\"knowledge-qa\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(20,'ai/aim','ContractAI.get_factors','{\"key\":\"ContractAI.get_factors\",\"title\":\"合同要素提取\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.contract.ContractAI().get_factors\",\"comment\":\"提取合同 @{contract_content} 中的要素。\",\"inputList\":[{\"types\":\"InputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"contract_type\",\"title\":\"输入合同方式\",\"name\":\"contract_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件形式\",\"value\":\"file\"},{\"label\":\"文本形式\",\"value\":\"text\"}],\"default\":\"text\",\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"contract_path\",\"title\":\"合同路径\",\"name\":\"contract_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.contract_path.show\",\"expression\":\"return $this.contract_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"contract_content\",\"title\":\"合同文本内容\",\"name\":\"contract_content\",\"tip\":\"合同内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.contract_content.show\",\"expression\":\"return $this.contract_type.value == \'text\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"FACTORELEMENT\",\"params\":{\"code\":3,\"options\":[\"合同名称\",\"合同编号\",\"合同签订日期\",\"合同开始日期\",\"合同结束日期\",\"合同标的\",\"标的数量\",\"单价\",\"税率\",\"税额\",\"合同总金额\",\"付款方式\",\"甲方\",\"乙方\",\"甲方开户行\",\"甲方银行账号\",\"乙方开户行\",\"乙方银行账号\"]}},\"key\":\"custom_factors\",\"title\":\"要素\",\"name\":\"custom_factors\",\"tip\":\"\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"MODALBUTTON\",\"params\":{\"loading\":false}},\"key\":\"contract_validate\",\"title\":\"效果验证\",\"name\":\"contract_validate\",\"tip\":\"\",\"default\":\"\",\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"factor_result\",\"title\":\"返回要素结果\",\"tip\":\"\"}],\"icon\":\"contract-element-extraction\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(21,'ai/aim','DocumentAI.theme_expand','{\"key\":\"DocumentAI.theme_expand\",\"title\":\"主题扩写\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.document.DocumentAI().theme_expand\",\"comment\":\"大模型将根据您给定的主题: @{text} ,对其进行扩写。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"主题\",\"name\":\"text\",\"tip\":\"输入您需要扩展的主题。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"theme_expand_res\",\"title\":\"主题扩展结果输出\",\"tip\":\"将主题扩展结果输出为变量。\"}],\"icon\":\"topic-expand\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(22,'ai/aim','DocumentAI.sentence_expand','{\"key\":\"DocumentAI.sentence_expand\",\"title\":\"段落扩写\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.document.DocumentAI().sentence_expand\",\"comment\":\"大模型将根据您给定的段落 @{text} ,对其进行扩写。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"段落\",\"name\":\"text\",\"tip\":\"输入您需要扩展的段落。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"sentence_expand_res\",\"title\":\"段落扩展结果输出\",\"tip\":\"将段落扩展结果输出为变量。\"}],\"icon\":\"paragraph-expand\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(23,'ai/aim','DocumentAI.sentence_reduce','{\"key\":\"DocumentAI.sentence_reduce\",\"title\":\"段落缩写\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.document.DocumentAI().sentence_reduce\",\"comment\":\"大模型将根据您给定的段落 @{text} ,对其进行缩写。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"段落\",\"name\":\"text\",\"tip\":\"输入您需要缩写的段落。\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"sentence_reduce_res\",\"title\":\"段落缩写结果输出\",\"tip\":\"将段落缩写结果输出为变量。\"}],\"icon\":\"paragraph-abbreviate\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(24,'ai/aim','RecruitAI.generate_keywords','{\"key\":\"RecruitAI.generate_keywords\",\"title\":\"职位关键词生成\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.recruit.RecruitAI().generate_keywords\",\"comment\":\"根据职位描述 @{job_description} 生成关键词。\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"job_name\",\"title\":\"职位名称\",\"name\":\"job_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_PYTHON_TEXTAREAMODAL_VARIABLE\"},\"key\":\"job_description\",\"title\":\"职位描述\",\"name\":\"job_description\",\"tip\":\"\",\"default\":\"\",\"required\":true},{\"types\":\"JobWebsitesTypes\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"job_website\",\"title\":\"招聘网站\",\"name\":\"job_website\",\"tip\":\"\",\"options\":[{\"label\":\"BOSS直聘\",\"value\":\"boss\"},{\"label\":\"猎聘\",\"value\":\"liepin\"},{\"label\":\"智联招聘\",\"value\":\"zhilian\"}],\"default\":\"boss\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"recruit_keywords\",\"title\":\"职位关键词\",\"tip\":\"\"}],\"icon\":\"job-keyword-generation\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(25,'ai/aim','RecruitAI.rating_resume','{\"key\":\"RecruitAI.rating_resume\",\"title\":\"简历评分\",\"version\":\"1.0.0\",\"src\":\"astronverse.ai.recruit.RecruitAI().rating_resume\",\"comment\":\"根据简历 @{resume_content} 评分。\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"job_name\",\"title\":\"职位名称\",\"name\":\"job_name\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"InputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"resume_input_type\",\"title\":\"简历输入方式\",\"name\":\"resume_input_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件形式\",\"value\":\"file\"},{\"label\":\"文本形式\",\"value\":\"text\"}],\"default\":\"text\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"resume_file_path\",\"title\":\"简历文件路径\",\"name\":\"resume_file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.resume_file_path.show\",\"expression\":\"return $this.resume_input_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"resume_content\",\"title\":\"简历文本内容\",\"name\":\"resume_content\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.resume_content.show\",\"expression\":\"return $this.resume_input_type.value == \'text\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"job_description\",\"title\":\"职位描述\",\"name\":\"job_description\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"RatingSystemTypes\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"rating_system\",\"title\":\"岗位评分体系\",\"name\":\"rating_system\",\"tip\":\"\",\"options\":[{\"label\":\"根据岗位描述生成\",\"value\":\"default\"},{\"label\":\"自定义判断标准\",\"value\":\"custom\"}],\"default\":\"default\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"rating_dimensions\",\"title\":\"岗位评分画像\",\"name\":\"rating_dimensions\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.rating_dimensions.show\",\"expression\":\"return $this.rating_system.value == \'custom\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"recruit_rating\",\"title\":\"简历匹配结果\",\"tip\":\"\"}],\"icon\":\"resume-scoring\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(26,'web','BrowserElement.wait_element','{\"key\":\"BrowserElement.wait_element\",\"title\":\"等待元素(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().wait_element\",\"comment\":\"等待浏览器对象 @{browser_obj} 中元素 @{element_data} 的(@{ele_status})\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择指定的网页元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"元素拾取\",\"name\":\"element_data\",\"tip\":\"拾取需要等待的网页元素\",\"required\":true,\"noInput\":true},{\"types\":\"WaitElementForStatusFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"ele_status\",\"title\":\"等待类型\",\"name\":\"ele_status\",\"tip\":\"等待出现或者等待消失\",\"options\":[{\"label\":\"等待元素出现\",\"value\":\"y\"},{\"label\":\"等待元素消失\",\"value\":\"n\"}],\"default\":\"y\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"wait_element\",\"title\":\"等待结果\",\"tip\":\"输出元素是否出现/消失,出现/消失为true,反之为false\"}],\"icon\":\"wait-element-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(27,'web','BrowserElement.click','{\"key\":\"BrowserElement.click\",\"title\":\"点击元素(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().click\",\"comment\":\"通过 @{button_type:点击} 的形式点击浏览器对象 @{browser_obj} 中的元素 @{element_data}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取元素\",\"name\":\"element_data\",\"tip\":\"拾取需要操作的元素信息\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"simulate_flag\",\"title\":\"模拟人工点击\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工点击是模拟人为操作方式点击,否则将根据拾取元素的自动化接口进行点击\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":false},{\"types\":\"ButtonForAssistiveKeyFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"assistive_key\",\"title\":\"辅助按键\",\"name\":\"assistive_key\",\"tip\":\"在点击时需要按下的键盘功能按键\",\"options\":[{\"label\":\"无\",\"value\":\"None\"},{\"label\":\"Alt\",\"value\":\"Alt\"},{\"label\":\"Ctrl\",\"value\":\"Ctrl\"},{\"label\":\"Shift\",\"value\":\"Shift\"},{\"label\":\"Win\",\"value\":\"Win\"}],\"default\":\"None\",\"dynamics\":[{\"key\":\"$this.assistive_key.show\",\"expression\":\"return $this.simulate_flag.value == true\"}],\"required\":true},{\"types\":\"ButtonForClickTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"button_type\",\"title\":\"点击键位\",\"name\":\"button_type\",\"tip\":\"选择模拟鼠标点击的方式\",\"options\":[{\"label\":\"左击\",\"value\":\"click\"},{\"label\":\"双击\",\"value\":\"dbclick\"},{\"label\":\"右击\",\"value\":\"right\"}],\"default\":\"click\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"click-element-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(28,'web','BrowserElement.input','{\"key\":\"BrowserElement.input\",\"title\":\"填写输入框(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().input\",\"comment\":\"在指定的浏览器对象 @{browser_obj} 中拾取输入框 @{element_data} ,以 @{fill_type:键盘输入/剪贴板输入} 的形式输入内容 @{fill_input} ,将执行结果输出至 @{form_input}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取输入框\",\"name\":\"element_data\",\"tip\":\"拾取需要填写内容的输入框元素\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"simulate_flag\",\"title\":\"模拟人工输入\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工输入是模拟人为操作方式输入,否则将根据元素的自动化接口进行输入\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":false},{\"types\":\"FillInputForFillTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"fill_type\",\"title\":\"输入类型\",\"name\":\"fill_type\",\"tip\":\"选择填写输入框的方式\",\"options\":[{\"label\":\"键盘输入\",\"value\":\"text\"},{\"label\":\"剪贴板\",\"value\":\"clipboard\"}],\"default\":\"text\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"fill_input\",\"title\":\"输入内容\",\"name\":\"fill_input\",\"tip\":\"填写输入框的内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.fill_input.show\",\"expression\":\"return $this.fill_type.value == \'text\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"focus_time\",\"title\":\"焦点睡眠时间\",\"name\":\"focus_time\",\"tip\":\"焦点停顿时间(ms)\",\"default\":1000,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.focus_time.show\",\"expression\":\"return $this.simulate_flag.value == true\"}],\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"write_gap_time\",\"title\":\"按键输入间隔\",\"name\":\"write_gap_time\",\"tip\":\"输入内容输入的时间间隔(s)\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.write_gap_time.show\",\"expression\":\"return $this.simulate_flag.value == true\"}],\"required\":false},{\"types\":\"FillInputForInputTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"input_type\",\"title\":\"追加输入\",\"name\":\"input_type\",\"tip\":\"是否对输入框进行追加输入\",\"options\":[{\"label\":\"追加\",\"value\":\"append\"},{\"label\":\"覆盖\",\"value\":\"overwrite\"}],\"default\":\"overwrite\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"form_input\",\"title\":\"用户输入内容\",\"tip\":\"\"}],\"icon\":\"fill-input-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(29,'web','BrowserElement.hover_over','{\"key\":\"BrowserElement.hover_over\",\"title\":\"鼠标悬停在元素上(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().hover_over\",\"comment\":\"鼠标悬停在浏览器对象 @{browser_obj} 中的元素 @{element_data} 上\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"悬停元素拾取\",\"name\":\"element_data\",\"tip\":\"拾取鼠标要悬停的元素\",\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"mouse-hover-element-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(30,'web','BrowserElement.screenshot','{\"key\":\"BrowserElement.screenshot\",\"title\":\"拾取元素截图(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().screenshot\",\"comment\":\"拾取浏览器对象 @{browser_obj} 的元素 @{element_data} 并输出为图片\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择截图元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取元素\",\"name\":\"element_data\",\"tip\":\"拾取需要截图的元素\",\"required\":true,\"noInput\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"截图保存路径\",\"name\":\"file_path\",\"tip\":\"文件夹路径\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"image_name\",\"title\":\"图片名称\",\"name\":\"image_name\",\"tip\":\"携带后缀的名称比如图片.jpg\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"xpath_shot\",\"title\":\"截图文件路径\",\"tip\":\"截图文件路径\"}],\"icon\":\"pick-element-screenshot-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(31,'web','BrowserElement.position_screenshot','{\"key\":\"BrowserElement.position_screenshot\",\"title\":\"元素位置截图(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().position_screenshot\",\"comment\":\"获取浏览器对象 @{browser_obj} 的元素 @{element_data} 位置截图并输出为图片\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取元素\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"截图保存路径\",\"name\":\"file_path\",\"tip\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"image_name\",\"title\":\"图片名称\",\"name\":\"image_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"position_shot\",\"title\":\"截图文件路径\",\"tip\":\"\"}],\"icon\":\"element-position-screenshot-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(32,'web/web.page','BrowserElement.scroll','{\"key\":\"BrowserElement.scroll\",\"title\":\"鼠标滚动网页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().scroll\",\"comment\":\"通过 @{scroll_direction:横向/纵向} 的方向,从(@{x_scroll_type||y_scroll_type})滚动浏览器对象 @{browser_obj} 的滚动条\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要滑动滚动条的网页所在浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ScrollbarType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"scrollbar_type\",\"title\":\"滚动目标\",\"name\":\"scrollbar_type\",\"tip\":\"选择网页窗口滚动条或指定网页上某个滚动条元素\",\"options\":[{\"label\":\"窗口\",\"value\":\"window\"},{\"label\":\"自定义目标\",\"value\":\"customEle\"}],\"default\":\"window\",\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取滚动条\",\"name\":\"element_data\",\"tip\":\"拾取需要在网页上滚动操作的滚动条元素\",\"dynamics\":[{\"key\":\"$this.element_data.show\",\"expression\":\"return $this.scrollbar_type.value == \'customEle\'\"}],\"required\":true,\"noInput\":true},{\"types\":\"ScrollDirection\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"scroll_direction\",\"title\":\"滚动方向\",\"name\":\"scroll_direction\",\"tip\":\"\",\"options\":[{\"label\":\"横向\",\"value\":\"horizontal\"},{\"label\":\"纵向\",\"value\":\"vertical\"}],\"default\":\"horizontal\",\"required\":true},{\"types\":\"ScrollbarForXScrollTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"x_scroll_type\",\"title\":\"横向滚动位置\",\"name\":\"x_scroll_type\",\"tip\":\"\",\"options\":[{\"label\":\"最左\",\"value\":\"left\"},{\"label\":\"最右\",\"value\":\"right\"},{\"label\":\"自定义\",\"value\":\"defined\"}],\"default\":\"left\",\"dynamics\":[{\"key\":\"$this.x_scroll_type.show\",\"expression\":\"return $this.scroll_direction.value == \'horizontal\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"x_custom_scroll_dis\",\"title\":\"横向自定义滚动距离\",\"name\":\"x_custom_scroll_dis\",\"tip\":\"单位为屏幕的分辨率像素px,一般为0-9999之间的数值\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.x_custom_scroll_dis.show\",\"expression\":\"return $this.scroll_direction.value == \'horizontal\' && $this.x_scroll_type.value == \'defined\'\"}],\"required\":true},{\"types\":\"ScrollbarForYScrollTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"y_scroll_type\",\"title\":\"纵向滚动位置\",\"name\":\"y_scroll_type\",\"tip\":\"\",\"options\":[{\"label\":\"顶部\",\"value\":\"top\"},{\"label\":\"底部\",\"value\":\"bottom\"},{\"label\":\"自定义\",\"value\":\"defined\"}],\"default\":\"top\",\"dynamics\":[{\"key\":\"$this.y_scroll_type.show\",\"expression\":\"return $this.scroll_direction.value == \'vertical\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"y_custom_scroll_dis\",\"title\":\"纵向自定义滚动距离\",\"name\":\"y_custom_scroll_dis\",\"tip\":\"单位为屏幕的分辨率像素px,一般为0-9999之间的数值\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.y_custom_scroll_dis.show\",\"expression\":\"return $this.scroll_direction.value == \'vertical\' && $this.y_scroll_type.value == \'defined\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.element_timeout.show\",\"expression\":\"return $this.scrollbar_type.value == \'customEle\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"mouse-scroll-webpage\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(33,'web','BrowserElement.scroll_into_view','{\"key\":\"BrowserElement.scroll_into_view\",\"title\":\"元素置于可视区域(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().scroll_into_view\",\"comment\":\"将网页元素 @{element_data} 置于可视区域\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要可视的元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取可视目标\",\"name\":\"element_data\",\"tip\":\"拾取需要可视的目标元素\",\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"element-to-visible-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(34,'web','BrowserElement.similar','{\"key\":\"BrowserElement.similar\",\"title\":\"获取相似元素列表(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().similar\",\"comment\":\"获取浏览器对象 @{browser_obj} 中与拾取到的元素 @{element_data} 相似的元素,并将相似元素数组输出至 @{get_similar_ele}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择相似元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"相似元素拾取\",\"name\":\"element_data\",\"tip\":\"在网页上拾取不同位置的两个相似元素\",\"required\":true,\"noInput\":true},{\"types\":\"ElementGetAttributeHasSelfTypeFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"get_type\",\"title\":\"元素操作\",\"name\":\"get_type\",\"tip\":\"\",\"options\":[{\"label\":\"获取元素对象\",\"value\":\"getElement\"},{\"label\":\"获取元素文本内容\",\"value\":\"getText\"},{\"label\":\"获取元素源代码\",\"value\":\"getHtml\"},{\"label\":\"获取元素值\",\"value\":\"getValue\"},{\"label\":\"获取元素链接地址\",\"value\":\"getLink\"},{\"label\":\"获取元素属性\",\"value\":\"getAttribute\"},{\"label\":\"获取元素位置\",\"value\":\"getPosition\"},{\"label\":\"获取元素选中状态\",\"value\":\"getSelection\"}],\"default\":\"getElement\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"attribute_name\",\"title\":\"属性名称\",\"name\":\"attribute_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.attribute_name.show\",\"expression\":\"return $this.get_type.value == \'getAttribute\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_similar_ele\",\"title\":\"元素信息\",\"tip\":\"\"}],\"icon\":\"get-similar-elements-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(35,'web','BrowserElement.loop_similar','{\"key\":\"BrowserElement.loop_similar\",\"title\":\"循环相似元素列表(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().loop_similar\",\"comment\":\"获取浏览器对象 @{browser_obj} 中与拾取到的元素 @{element_data} 相似的元素,从起始项@{start}到结束项@{end}进行循环操作,输出列表循环至@{item}, 是否输出循环项位置为@{index}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择相似元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"相似元素拾取\",\"name\":\"element_data\",\"tip\":\"在网页上拾取不同位置的两个相似元素\",\"required\":true,\"noInput\":true},{\"types\":\"ElementGetAttributeHasSelfTypeFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"get_type\",\"title\":\"元素操作\",\"name\":\"get_type\",\"tip\":\"\",\"options\":[{\"label\":\"获取元素对象\",\"value\":\"getElement\"},{\"label\":\"获取元素文本内容\",\"value\":\"getText\"},{\"label\":\"获取元素源代码\",\"value\":\"getHtml\"},{\"label\":\"获取元素值\",\"value\":\"getValue\"},{\"label\":\"获取元素链接地址\",\"value\":\"getLink\"},{\"label\":\"获取元素属性\",\"value\":\"getAttribute\"},{\"label\":\"获取元素位置\",\"value\":\"getPosition\"},{\"label\":\"获取元素选中状态\",\"value\":\"getSelection\"}],\"default\":\"getElement\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start\",\"title\":\"起始位置\",\"name\":\"start\",\"tip\":\"下标位置从0开始\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end\",\"title\":\"结束位置\",\"name\":\"end\",\"tip\":\"下标位置从0开始,-1代表循环至最后一个元素\",\"default\":-1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"attribute_name\",\"title\":\"属性名称\",\"name\":\"attribute_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.attribute_name.show\",\"expression\":\"return $this.get_type.value == \'getAttribute\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"index\",\"title\":\"循环项位置\",\"tip\":\"默认变量可修改,用于遍历列表的变量索引数值\"},{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"item\",\"title\":\"循环项\",\"tip\":\"默认变量可修改,用于遍历列表的变量\"}],\"icon\":\"loop-similar-elements-web\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(36,'web','BrowserElement.element_text','{\"key\":\"BrowserElement.element_text\",\"title\":\"获取元素文本内容(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().element_text\",\"comment\":\"获取浏览器对象 @{browser_obj} 网页中拾取的元素 @{element_data} 文本内容,并将结果输出至 @{data_pick}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要获取文本的元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"元素拾取\",\"name\":\"element_data\",\"tip\":\"选择要获取文本的元素位置\",\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"data_pick\",\"title\":\"输出变量\",\"tip\":\"输出获取到的当前网页标题字符串\"}],\"icon\":\"get-element-text-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(37,'web','BrowserElement.slider_hover','{\"key\":\"BrowserElement.slider_hover\",\"title\":\"拾取滑块拖拽(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().slider_hover\",\"comment\":\"将滑块 @{element_slider} 从 @{drag_type} 向 @{drag_direction} 拖拽 @{percent_value} %\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"slider_element\",\"name\":\"slider_element\",\"required\":true,\"noInput\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"progress_element\",\"name\":\"progress_element\",\"required\":true,\"noInput\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"percent_value\",\"title\":\"滑块移动比例\",\"name\":\"percent_value\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ElementDragDirectionTypeFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"drag_direction\",\"title\":\"拖拽方向\",\"name\":\"drag_direction\",\"tip\":\"\",\"options\":[{\"label\":\"左\",\"value\":\"left\"},{\"label\":\"右\",\"value\":\"right\"},{\"label\":\"上\",\"value\":\"up\"},{\"label\":\"下\",\"value\":\"down\"}],\"default\":\"left\",\"required\":true},{\"types\":\"ElementDragTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"drag_type\",\"title\":\"拖拽类型\",\"name\":\"drag_type\",\"tip\":\"\",\"options\":[{\"label\":\"起始位置\",\"value\":\"start\"},{\"label\":\"当前位置\",\"value\":\"current\"}],\"default\":\"start\",\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"duration\",\"title\":\"拖动的持续时间\",\"name\":\"duration\",\"tip\":\"\",\"default\":0.25,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"pick-slider-drag-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(38,'web','BrowserElement.get_select','{\"key\":\"BrowserElement.get_select\",\"title\":\"获取下拉框(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().get_select\",\"comment\":\"在浏览器对象 @{browser_obj} 中获取下拉框 @{element_data}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取下拉框\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"current_content\",\"title\":\"获取当前选中内容\",\"name\":\"current_content\",\"tip\":\"选中内容或者所有内容\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_selected\",\"title\":\"下拉框选中内容\",\"tip\":\"\"}],\"icon\":\"get-dropdown-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(39,'web','BrowserElement.get_checked','{\"key\":\"BrowserElement.get_checked\",\"title\":\"获取复选框(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().get_checked\",\"comment\":\"在浏览器对象 @{browser_obj} 中获取复选框 @{element_data}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取复选框\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_checkbox_checked\",\"title\":\"复选框选中内容\",\"tip\":\"\"}],\"icon\":\"get-checkbox-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(40,'web','BrowserElement.set_select','{\"key\":\"BrowserElement.set_select\",\"title\":\"操作下拉框(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().set_select\",\"comment\":\"在浏览器对象 @{browser_obj} 中获取下拉框 @{element_data} ,通过 @{pattern} 选择 @{value}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取下拉框\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"SelectionPartner\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"pattern\",\"title\":\"匹配模式\",\"name\":\"pattern\",\"tip\":\"\",\"options\":[{\"label\":\"模糊匹配\",\"value\":\"contains\"},{\"label\":\"精准匹配\",\"value\":\"equal\"},{\"label\":\"顺序匹配\",\"value\":\"index\"}],\"default\":\"contains\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"匹配内容\",\"name\":\"value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.value.show\",\"expression\":\"return [\'contains\', \'equal\'].includes($this.pattern.value)\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"solution\",\"title\":\"顺序\",\"name\":\"solution\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.solution.show\",\"expression\":\"return $this.pattern.value == \'index\'\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"operate-dropdown-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(41,'web','BrowserElement.set_checked','{\"key\":\"BrowserElement.set_checked\",\"title\":\"操作复选框(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().set_checked\",\"comment\":\"在浏览器对象 @{browser_obj} 中获取复选框 @{element_data} 后 @{checked_type}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取复选框\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"ElementCheckedTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"checked_type\",\"title\":\"操作类型\",\"name\":\"checked_type\",\"tip\":\"\",\"options\":[{\"label\":\"勾选\",\"value\":\"checked\"},{\"label\":\"取消勾选\",\"value\":\"unchecked\"},{\"label\":\"反选\",\"value\":\"reversed\"}],\"default\":\"checked\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"operate-checkbox-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(42,'web','BrowserElement.element_operation','{\"key\":\"BrowserElement.element_operation\",\"title\":\"元素操作(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().element_operation\",\"comment\":\"在浏览器对象 @{browser_obj} 中获取 @{element_data} 并 @{operation_type}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取元素\",\"name\":\"element_data\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"ElementAttributeOpTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"operation_type\",\"title\":\"操作类型\",\"name\":\"operation_type\",\"tip\":\"设置-获取-删除信息\",\"options\":[{\"label\":\"获取属性\",\"value\":\"get\"},{\"label\":\"设置属性\",\"value\":\"set\"},{\"label\":\"删除属性\",\"value\":\"del\"}],\"default\":\"get\",\"required\":true},{\"types\":\"ElementGetAttributeTypeFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"get_type\",\"title\":\"信息类型\",\"name\":\"get_type\",\"tip\":\"\",\"options\":[{\"label\":\"获取元素文本内容\",\"value\":\"getText\"},{\"label\":\"获取元素源代码\",\"value\":\"getHtml\"},{\"label\":\"获取元素值\",\"value\":\"getValue\"},{\"label\":\"获取元素链接地址\",\"value\":\"getLink\"},{\"label\":\"获取元素属性\",\"value\":\"getAttribute\"},{\"label\":\"获取元素位置\",\"value\":\"getPosition\"},{\"label\":\"获取元素选中状态\",\"value\":\"getSelection\"}],\"default\":\"getText\",\"dynamics\":[{\"key\":\"$this.get_type.show\",\"expression\":\"return $this.operation_type.value == \'get\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"attribute_name\",\"title\":\"属性名称\",\"name\":\"attribute_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.attribute_name.show\",\"expression\":\"return $this.get_type.value == \'getAttribute\' || [\'set\', \'del\'].includes($this.operation_type.value)\"}],\"required\":true},{\"types\":\"RelativePosition\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"position\",\"title\":\"相对位置\",\"name\":\"position\",\"tip\":\"\",\"options\":[{\"label\":\"屏幕左上\",\"value\":\"screenLeft\"},{\"label\":\"页面左上\",\"value\":\"webPageLeft\"}],\"default\":\"screenLeft\",\"dynamics\":[{\"key\":\"$this.position.show\",\"expression\":\"return $this.get_type.value == \'getPosition\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"attribute_value\",\"title\":\"属性值\",\"name\":\"attribute_value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.attribute_value.show\",\"expression\":\"return $this.operation_type.value == \'set\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_attr\",\"title\":\"元素属性\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_attr.show\",\"expression\":\"return $this.get_type.value == \'getAttribute\'\"}]},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_value\",\"title\":\"元素值\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_value.show\",\"expression\":\"return $this.get_type.value == \'getValue\'\"}]},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_html\",\"title\":\"元素源代码\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_html.show\",\"expression\":\"return $this.get_type.value == \'getHtml\'\"}]},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_link\",\"title\":\"元素链接地址\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_link.show\",\"expression\":\"return $this.get_type.value == \'getLink\'\"}]},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_text\",\"title\":\"元素文本内容\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_text.show\",\"expression\":\"return $this.get_type.value == \'getText\'\"}]},{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_position\",\"title\":\"元素位置\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_position.show\",\"expression\":\"return $this.get_type.value == \'getPosition\'\"}]},{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ele_selected\",\"title\":\"元素选中状态\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.get_ele_selected.show\",\"expression\":\"return $this.get_type.value == \'getSelection\'\"}]}],\"icon\":\"element-operation-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(43,'web','BrowserElement.get_table','{\"key\":\"BrowserElement.get_table\",\"title\":\"获取表格数据(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().get_table\",\"comment\":\"获取浏览器对象 @{browser_obj} 中的表格 @{element_data} ,将结果输出为字典对象 @{table_pick}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取表格\",\"name\":\"element_data\",\"tip\":\"拾取网页表格中任一单元格元素,无须拾取整个表格\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"to_excel\",\"title\":\"存储到表格文档\",\"name\":\"to_excel\",\"tip\":\"可直接存储为excel文档\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\"}},\"key\":\"excel_path\",\"title\":\"表格文档路径\",\"name\":\"excel_path\",\"tip\":\"请选择文档存储路径\",\"dynamics\":[{\"key\":\"$this.excel_path.show\",\"expression\":\"return $this.to_excel.value == true\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"table_pick\",\"title\":\"表格对象\",\"tip\":\"输出获取的表格对象,数据类型:字典\"}],\"icon\":\"get-table-data-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(44,'web','BrowserElement.data_batch','{\"key\":\"BrowserElement.data_batch\",\"title\":\"数据抓取(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().data_batch\",\"comment\":\"在指定的浏览器对象 @{browser_obj} 中抓取 @{batch_data} ,将结果输出为字典对象 @{table_pick}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"BATCH\"}},\"key\":\"batch_data\",\"title\":\"抓取对象\",\"name\":\"batch_data\",\"tip\":\"拾取需要抓取的元素\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"multi_page\",\"title\":\"是否抓取多页\",\"name\":\"multi_page\",\"tip\":\"选择需要是否抓取多页,默认抓取当前页\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_count\",\"title\":\"抓取页数\",\"name\":\"page_count\",\"tip\":\"填写需要抓取的总页数,例如:抓取10页,则填写10\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_count.show\",\"expression\":\"return $this.multi_page.value == true\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_interval\",\"title\":\"翻页间隔时间,单位:秒\",\"name\":\"page_interval\",\"tip\":\"填写翻页的间隔时间,若间隔时间过短导致页面加载不完全,可适当增加翻页间隔时间\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_interval.show\",\"expression\":\"return $this.multi_page.value == true\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"翻页按钮\",\"name\":\"element_data\",\"tip\":\"拾取需要翻页的元素\",\"dynamics\":[{\"key\":\"$this.element_data.show\",\"expression\":\"return $this.multi_page.value == true\"}],\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"simulate_flag\",\"title\":\"模拟人工输入\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工输入是模拟人为操作方式输入,否则将根据元素的自动化接口进行输入\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.simulate_flag.show\",\"expression\":\"return $this.multi_page.value == true\"}],\"required\":false},{\"types\":\"ButtonForClickTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"button_type\",\"title\":\"点击键位\",\"name\":\"button_type\",\"tip\":\"选择模拟鼠标点击的方式\",\"options\":[{\"label\":\"左击\",\"value\":\"click\"},{\"label\":\"双击\",\"value\":\"dbclick\"},{\"label\":\"右击\",\"value\":\"right\"}],\"default\":\"click\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"to_excel\",\"title\":\"存储到表格文档\",\"name\":\"to_excel\",\"tip\":\"可直接存储为excel文档\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\"],\"defaultPath\":\"default.xlsx\"}},\"key\":\"excel_path\",\"title\":\"表格文档路径\",\"name\":\"excel_path\",\"tip\":\"请选择文档存储路径\",\"dynamics\":[{\"key\":\"$this.excel_path.show\",\"expression\":\"return $this.to_excel.value == true\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"TablePickType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出类型\",\"name\":\"output_type\",\"tip\":\"选择表格输出类型,默认输出为行\",\"options\":[{\"label\":\"按行输出\",\"value\":\"row\"},{\"label\":\"按列输出\",\"value\":\"column\"}],\"default\":\"row\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"output_head\",\"title\":\"是否输出表头\",\"name\":\"output_head\",\"tip\":\"选择是否输出表头,默认输出表头\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"table_pick\",\"title\":\"表格对象\",\"tip\":\"输出获取的表格对象,数据类型:字典\"},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"table_path\",\"title\":\"表格路径\",\"tip\":\"输出保存的表格路径\",\"dynamics\":[{\"key\":\"$this.table_path.show\",\"expression\":\"return $this.to_excel.value == true\"}]}],\"icon\":\"data-scraping-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(45,'web','BrowserElement.create_element','{\"key\":\"BrowserElement.create_element\",\"title\":\"获取元素对象(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().create_element\",\"comment\":\"在指定的浏览器对象 @{browser_obj} 中根据 @{locate_type}获取元素对象 ,将结果输出为元素对象 @{element_obj}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"LocateType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"locate_type\",\"title\":\"定位方式\",\"name\":\"locate_type\",\"tip\":\"选择Xpath或者CssSelector定位方式\",\"options\":[{\"label\":\"xpath\",\"value\":\"xpath\"},{\"label\":\"css选择器\",\"value\":\"cssSelector\"}],\"default\":\"xpath\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"locate_value\",\"title\":\"Xpath/CssSelector\",\"name\":\"locate_value\",\"tip\":\"输入Xpath或者CssSelector\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"element_obj\",\"title\":\"元素对象\",\"tip\":\"输出元素对象,结果为单个对象或列表\"}],\"icon\":\"get-element-object-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(46,'web','BrowserElement.get_relative_element','{\"key\":\"BrowserElement.get_relative_element\",\"title\":\"获取关联元素(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().get_relative_element\",\"comment\":\"在指定的浏览器对象 @{browser_obj} 中获取 @{element_data} 关联的 @{relative_type},将结果输出为元素对象 @{element_obj}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"元素对象\",\"name\":\"element_data\",\"tip\":\"作为锚点的元素,只能是单个元素对象,不能是列表\",\"required\":true,\"noInput\":true},{\"types\":\"RelativeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"relative_type\",\"title\":\"关联类型\",\"name\":\"relative_type\",\"tip\":\"选择关联类型,例如:兄弟元素、父级元素、子级元素等\",\"options\":[{\"label\":\"子元素\",\"value\":\"child\"},{\"label\":\"父元素\",\"value\":\"parent\"},{\"label\":\"兄弟元素\",\"value\":\"sibling\"}],\"default\":\"child\",\"required\":true},{\"types\":\"ChildElementType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"child_element_type\",\"title\":\"子元素类型\",\"name\":\"child_element_type\",\"tip\":\"选择获取子元素的类型\",\"options\":[{\"label\":\"所有子元素\",\"value\":\"all\"},{\"label\":\"第n个子元素\",\"value\":\"index\"},{\"label\":\"子元素xpath\",\"value\":\"xpath\"},{\"label\":\"最后一个子元素\",\"value\":\"last\"}],\"default\":\"all\",\"dynamics\":[{\"key\":\"$this.child_element_type.show\",\"expression\":\"return $this.relative_type.value == \'child\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"child_element_xpath\",\"title\":\"子元素xpath\",\"name\":\"child_element_xpath\",\"tip\":\"填写子元素的xpath,仅获取单个元素\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.child_element_xpath.show\",\"expression\":\"return $this.child_element_type.value == \'xpath\' && $this.relative_type.value == \'child\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"child_element_index\",\"title\":\"子元素位置\",\"name\":\"child_element_index\",\"tip\":\"填写子元素位置,例如:0表示第一个子元素\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.child_element_index.show\",\"expression\":\"return $this.child_element_type.value == \'index\' && $this.relative_type.value == \'child\'\"}],\"required\":true},{\"types\":\"SiblingElementType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"sibling_element_type\",\"title\":\"兄弟元素类型\",\"name\":\"sibling_element_type\",\"tip\":\"选择获取兄弟元素的类型\",\"options\":[{\"label\":\"所有兄弟元素\",\"value\":\"all\"},{\"label\":\"下一个兄弟元素\",\"value\":\"next\"},{\"label\":\"上一个兄弟元素\",\"value\":\"prev\"}],\"default\":\"all\",\"dynamics\":[{\"key\":\"$this.sibling_element_type.show\",\"expression\":\"return $this.relative_type.value == \'sibling\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"element_timeout\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"element_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"element_obj\",\"title\":\"元素对象\",\"tip\":\"输出元素对象,结果为单个对象或列表\"}],\"icon\":\"get-related-elements-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(47,'web','BrowserElement.element_exist','{\"key\":\"BrowserElement.element_exist\",\"title\":\"元素是否存在(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_element.BrowserElement().element_exist\",\"comment\":\"浏览器对象 @{browser_obj} 中元素 @{element_data} 是否存在\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择指定的网页元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"元素拾取\",\"name\":\"element_data\",\"tip\":\"拾取需要等待的网页元素\",\"required\":true,\"noInput\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"element_exist\",\"title\":\"元素存在/不存在\",\"tip\":\"输出元素是否存在,存在为true,不存在为false\"}],\"icon\":\"wait-element-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(48,'script','BrowserScript.js_run','{\"key\":\"BrowserScript.js_run\",\"title\":\"Js脚本\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_script.BrowserScript().js_run\",\"comment\":\"通过 @{input_type:在线编辑/外部导入方式} 编辑脚本内容 @{content||file_path} ,执行JavaScript,脚本执行结果保存至 @{program_script}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择js运行的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"InputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"input_type\",\"title\":\"写入方式\",\"name\":\"input_type\",\"tip\":\"\",\"options\":[{\"label\":\"在线编辑\",\"value\":\"content\"},{\"label\":\"外部导入\",\"value\":\"file\"}],\"default\":\"content\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_PYTHON_TEXTAREAMODAL_VARIABLE\"},\"key\":\"content\",\"title\":\"脚本内容\",\"name\":\"content\",\"tip\":\"编辑要执行的自定义脚本\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.content.show\",\"expression\":\"return $this.input_type.value == \'content\'\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".js\"]}},\"key\":\"file_path\",\"title\":\"脚本路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.input_type.value == \'file\'\"}],\"required\":true},{\"types\":\"List\",\"formType\":{\"type\":\"SCRIPTPARAMS\"},\"key\":\"params\",\"title\":\"参数管理\",\"name\":\"params\",\"tip\":\"输入脚本相关的参数管理,注意参数会被序列化,一些不支持序列化的将会报错\",\"need_parse\":\"json_str\",\"required\":false},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"iframe元素对象\",\"name\":\"element_data\",\"tip\":\"对于iframe中执行有问题的,可以获取iframe的元素对象来辅助iframe的脚本执行\",\"required\":false,\"noInput\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"iframe_url\",\"title\":\"iframe地址\",\"name\":\"iframe_url\",\"tip\":\"对于iframe中执行有问题,可以获取iframe的src字段填入此处\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"program_script\",\"title\":\"执行结果\",\"tip\":\"\"}],\"icon\":\"js-script\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(49,'web','BrowserSoftware.browser_open','{\"key\":\"BrowserSoftware.browser_open\",\"title\":\"打开浏览器\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().browser_open\",\"comment\":\"打开 @{browser_type:浏览器} 并进入初始网址 @{url:网址} ,将结果输出为浏览器对象 @{web_open}\",\"inputList\":[{\"types\":\"URL\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"初始网址\",\"name\":\"url\",\"tip\":\"初始网址\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CommonForBrowserType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"browser_type\",\"title\":\"浏览器类型\",\"name\":\"browser_type\",\"tip\":\"选择浏览器类型,需安装星火数字员工插件实现网页自动化,路径:设置-插件安装\",\"options\":[{\"label\":\"Chrome\",\"value\":\"chrome\"},{\"label\":\"Edge\",\"value\":\"edge\"},{\"label\":\"360安全浏览器\",\"value\":\"360se\"},{\"label\":\"360极速浏览器X\",\"value\":\"360ChromeX\"},{\"label\":\"Firefox\",\"value\":\"firefox\"},{\"label\":\"内置浏览器\",\"value\":\"chromium\"}],\"default\":\"chrome\",\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\"}},\"key\":\"browser_abs_path\",\"title\":\"浏览器路径\",\"name\":\"browser_abs_path\",\"tip\":\"浏览器软件安装路径\",\"default\":\"\",\"level\":\"normal\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"open_args\",\"title\":\"浏览器启动参数\",\"name\":\"open_args\",\"tip\":\"浏览器启动参数,比如--incognito\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"CHECKBOX\"},\"key\":\"open_with_incognito\",\"title\":\"使用隐私模式\",\"name\":\"open_with_incognito\",\"tip\":\"请提前将浏览器中“星火数字员工插件”详情设置为\\\"在无痕模式下启用\\\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"wait_load_success\",\"title\":\"等待网页加载完成\",\"name\":\"wait_load_success\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"level\":\"normal\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"timeout\",\"title\":\"加载延时时间(秒)\",\"name\":\"timeout\",\"tip\":\"\",\"default\":20,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"normal\",\"dynamics\":[{\"key\":\"$this.timeout.show\",\"expression\":\"return $this.wait_load_success.value == true\"}],\"required\":true},{\"types\":\"CommonForTimeoutHandleType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"timeout_handle_type\",\"title\":\"延时超时后执行\",\"name\":\"timeout_handle_type\",\"tip\":\"延时处理方式,终止即停止网页加载,跳过即跳过等待加载,不影响网页加载\",\"options\":[{\"label\":\"终止\",\"value\":\"execError\"},{\"label\":\"跳过\",\"value\":\"stopLoad\"}],\"default\":\"execError\",\"level\":\"normal\",\"dynamics\":[{\"key\":\"$this.timeout_handle_type.show\",\"expression\":\"return $this.wait_load_success.value == true\"}],\"required\":true}],\"outputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"web_open\",\"title\":\"浏览器对象\",\"tip\":\"输出打开的浏览器对象,使用此网页对象可实现网页自动化\"}],\"icon\":\"open-browser\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(50,'web','BrowserSoftware.browser_close','{\"key\":\"BrowserSoftware.browser_close\",\"title\":\"关闭浏览器\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().browser_close\",\"comment\":\"关闭浏览器对象 @{browser_obj}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要关闭的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"close-browser\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(51,'web/web.cookie','BrowserSoftware.set_cookies','{\"key\":\"BrowserSoftware.set_cookies\",\"title\":\"设置Cookie\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().set_cookies\",\"comment\":\"设置浏览器对象 @{browser_obj} 的Cookie值为 @{cookie_input}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要设置Cookies的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"URL\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"目标url\",\"name\":\"url\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cookie_name\",\"title\":\"cookie名称\",\"name\":\"cookie_name\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cookie_val\",\"title\":\"cookie值\",\"name\":\"cookie_val\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_timeout\",\"title\":\"等待页面加载时间(秒)\",\"name\":\"page_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"cookie_input\",\"title\":\"cookie值\",\"tip\":\"\"}],\"icon\":\"set-cookie\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(52,'web/web.cookie','BrowserSoftware.get_cookies','{\"key\":\"BrowserSoftware.get_cookies\",\"title\":\"获取Cookie\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().get_cookies\",\"comment\":\"获取浏览器对象 @{browser_obj} 的Cookie值,输出至 @{get_cookie}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要获取cookie值的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"URL\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"目标url\",\"name\":\"url\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cookie_name\",\"title\":\"cookie名称\",\"name\":\"cookie_name\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_timeout\",\"title\":\"等待页面加载时间(秒)\",\"name\":\"page_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_cookie\",\"title\":\"cookie值\",\"tip\":\"\"}],\"icon\":\"get-cookie\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(53,'web/web.page','BrowserSoftware.web_open','{\"key\":\"BrowserSoftware.web_open\",\"title\":\"打开新网页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().web_open\",\"comment\":\"打开浏览器对象 @{browser_obj} 并进入网址 @{new_tab_url:新标签页地址} ,将结果输出为浏览器对象(open_new_tab_1)\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"URL\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_tab_url\",\"title\":\"新标签页网址\",\"name\":\"new_tab_url\",\"tip\":\"新打开标签页所在的网址\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"wait_page\",\"title\":\"等待网页加载完成\",\"name\":\"wait_page\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"web_new_page\",\"title\":\"浏览器对象\",\"tip\":\"\"}],\"icon\":\"open-new-webpage\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(54,'web/web.page','BrowserSoftware.web_switch','{\"key\":\"BrowserSoftware.web_switch\",\"title\":\"切换到已存在标签页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().web_switch\",\"comment\":\"通过 @{switch_type} 的匹配方式切换到浏览器对象 @{browser_obj} 中的指定标签页\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebSwitchType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"switch_type\",\"title\":\"匹配方式\",\"name\":\"switch_type\",\"tip\":\"选择网址/标题/标签页ID的匹配方式\",\"options\":[{\"label\":\"网址\",\"value\":\"url\"},{\"label\":\"标题\",\"value\":\"title\"},{\"label\":\"标签页ID\",\"value\":\"tabId\"}],\"default\":\"url\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"tab_url\",\"title\":\"网址\",\"name\":\"tab_url\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.tab_url.show\",\"expression\":\"return $this.switch_type.value == \'url\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"tab_title\",\"title\":\"标题\",\"name\":\"tab_title\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.tab_title.show\",\"expression\":\"return $this.switch_type.value == \'title\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"tab_id\",\"title\":\"标签页ID\",\"name\":\"tab_id\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.tab_id.show\",\"expression\":\"return $this.switch_type.value == \'tabId\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"toggle_tab\",\"title\":\"切换到的标签页\",\"tip\":\"\"}],\"icon\":\"switch-existing-tab\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(55,'web/web.page','BrowserSoftware.wait_web_load','{\"key\":\"BrowserSoftware.wait_web_load\",\"title\":\"等待页面加载完成\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().wait_web_load\",\"comment\":\"等待浏览器对象 @{browser_obj} 中页面加载\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要等待页面所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"timeout\",\"title\":\"超时时间(秒)\",\"name\":\"timeout\",\"tip\":\"超过该时间停止等待\",\"default\":20,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"wait-page-load\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(56,'web/web.page','BrowserSoftware.stop_web_load','{\"key\":\"BrowserSoftware.stop_web_load\",\"title\":\"停止加载网页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().stop_web_load\",\"comment\":\"停止加载网页 @{browser_obj}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要停止加载的网页所在浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"stop-loading-page\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(57,'web/web.page','BrowserSoftware.web_refresh','{\"key\":\"BrowserSoftware.web_refresh\",\"title\":\"刷新当前网页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().web_refresh\",\"comment\":\"刷新当前网页 @{browser_obj}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要刷新当前网页所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"refresh-current-page\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(58,'web/web.page','BrowserSoftware.web_close','{\"key\":\"BrowserSoftware.web_close\",\"title\":\"关闭网页\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().web_close\",\"comment\":\"关闭浏览器对象 @{browser_obj} 的当前网页\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要关闭的标签页所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"url\",\"name\":\"url\",\"tip\":\"不填写则关闭当前标签页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[],\"icon\":\"close-webpage\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(59,'web/web.page','BrowserSoftware.screenshot','{\"key\":\"BrowserSoftware.screenshot\",\"title\":\"网页截图\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().screenshot\",\"comment\":\"将浏览器对象 @{browser_obj} 截图,并保存至路径 @{web_screen}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要截图网页所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ScreenShotForShotRangeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"shot_range\",\"title\":\"截图区域\",\"name\":\"shot_range\",\"tip\":\"选择截图的区域类型,若选择全部区域,会生成整个页面的长图\",\"options\":[{\"label\":\"可视区域\",\"value\":\"visual\"},{\"label\":\"全网页区域\",\"value\":\"all\"}],\"default\":\"visual\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"image_path\",\"title\":\"截图保存路径\",\"name\":\"image_path\",\"tip\":\"截图保存的本地电脑文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"image_name\",\"title\":\"图片名称\",\"name\":\"image_name\",\"tip\":\"填写图片名,可以带或不带扩展名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_timeout\",\"title\":\"等待页面加载时间(秒)\",\"name\":\"page_timeout\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"web_screen\",\"title\":\"文件路径\",\"tip\":\"\"}],\"icon\":\"webpage-screenshot\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(60,'web/web.page','BrowserSoftware.browser_forward','{\"key\":\"BrowserSoftware.browser_forward\",\"title\":\"网页前进\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().browser_forward\",\"comment\":\"浏览器对象 @{browser_obj} 的当前网页前进\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要网页前进所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"webpage-forward\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(61,'web/web.page','BrowserSoftware.browser_back','{\"key\":\"BrowserSoftware.browser_back\",\"title\":\"网页后退\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().browser_back\",\"comment\":\"浏览器对象 @{browser_obj} 的当前网页后退\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要网页后退所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"webpage-backward\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(62,'web','BrowserSoftware.get_current_obj','{\"key\":\"BrowserSoftware.get_current_obj\",\"title\":\"获取已打开的浏览器对象\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().get_current_obj\",\"comment\":\"获取已经打开的浏览器软件 @{browser_type} 的浏览器对象,并将结果输出至 @{browser_obj}\",\"inputList\":[{\"types\":\"CommonForBrowserType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"browser_type\",\"title\":\"浏览器类型\",\"name\":\"browser_type\",\"tip\":\"选择该浏览器对象所在的浏览器类型\",\"options\":[{\"label\":\"Chrome\",\"value\":\"chrome\"},{\"label\":\"Edge\",\"value\":\"edge\"},{\"label\":\"360安全浏览器\",\"value\":\"360se\"},{\"label\":\"360极速浏览器X\",\"value\":\"360ChromeX\"},{\"label\":\"Firefox\",\"value\":\"firefox\"},{\"label\":\"内置浏览器\",\"value\":\"chromium\"}],\"default\":\"chrome\",\"required\":true}],\"outputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"tip\":\"保存获取的浏览器对象,使用此对象进行网页自动化操作\"}],\"icon\":\"get-open-browser-objects\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(63,'web/web.page','BrowserSoftware.get_current_url','{\"key\":\"BrowserSoftware.get_current_url\",\"title\":\"获取网页URL\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().get_current_url\",\"comment\":\"获取浏览器对象 @{browser_obj} 的URL值,并将结果输出至 @{get_url}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择获取页面URL所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_url\",\"title\":\"网页url\",\"tip\":\"\"}],\"icon\":\"get-webpage-url\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(64,'web/web.page','BrowserSoftware.get_current_title','{\"key\":\"BrowserSoftware.get_current_title\",\"title\":\"获取网页标题\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().get_current_title\",\"comment\":\"获取浏览器对象 @{browser_obj} 的标题,并将结果输出至 @{get_page_title}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择获取页面标题所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_page_title\",\"title\":\"网页标题\",\"tip\":\"\"}],\"icon\":\"get-webpage-title\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(65,'web/web.page','BrowserSoftware.get_current_tab_id','{\"key\":\"BrowserSoftware.get_current_tab_id\",\"title\":\"获取当前标签页ID\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().get_current_tab_id\",\"comment\":\"获取浏览器对象 @{browser_obj}当前标签页的唯一ID,并将结果输出至 @{get_tab_id}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择获取标签页ID所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_tab_id\",\"title\":\"标签页ID\",\"tip\":\"\"}],\"icon\":\"get-current-tab-id\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(66,'web/web.file','BrowserSoftware.download_web_file','{\"key\":\"BrowserSoftware.download_web_file\",\"title\":\"文件下载(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().download_web_file\",\"comment\":\"在网页中点击 @{element_data||link_str} 下载,将其保存至 @{save_path}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要点击下载的网页元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取点击目标\",\"name\":\"element_data\",\"tip\":\"选择要点击下载的网页元素\",\"dynamics\":[{\"key\":\"$this.element_data.show\",\"expression\":\"return $this.download_mode.value == \'click\'\"}],\"required\":true,\"noInput\":true},{\"types\":\"DownloadModeForFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"download_mode\",\"title\":\"下载场景\",\"name\":\"download_mode\",\"tip\":\"\",\"options\":[{\"label\":\"点击下载\",\"value\":\"click\"},{\"label\":\"链接下载\",\"value\":\"link\"}],\"default\":\"click\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"link_str\",\"title\":\"下载链接地址\",\"name\":\"link_str\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.link_str.show\",\"expression\":\"return $this.download_mode.value == \'link\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"save_path\",\"title\":\"保存路径\",\"name\":\"save_path\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"custom_flag\",\"title\":\"自定义命名\",\"name\":\"custom_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"自定义文件名\",\"name\":\"file_name\",\"tip\":\"不需要输入扩展名,系统会自动添加扩展名【系统建议打开显示扩展名】\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.custom_flag.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"simulate_flag\",\"title\":\"模拟人工点击\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工点击是模拟人为操作方式点击,否则将根据拾取元素的自动化接口进行点击\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"dynamics\":[{\"key\":\"$this.simulate_flag.show\",\"expression\":\"return $this.download_mode.value == \'click\'\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_wait\",\"title\":\"同步等待\",\"name\":\"is_wait\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"time_out\",\"title\":\"最长等待时间\",\"name\":\"time_out\",\"tip\":\"超过最长等待时间(秒)则停止等待\",\"default\":60,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.time_out.show\",\"expression\":\"return $this.is_wait.value == true\"}],\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"load_file\",\"title\":\"文档路径\",\"tip\":\"\"}],\"icon\":\"file-download-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(67,'web/web.file','BrowserSoftware.upload_web_file','{\"key\":\"BrowserSoftware.upload_web_file\",\"title\":\"文件上传(web)\",\"version\":\"1.0.0\",\"src\":\"astronverse.browser.browser_software.BrowserSoftware().upload_web_file\",\"comment\":\"在网页中点击 @{element_data} ,在弹出的文件选择对话框中输入要上传的文件路径 @{upload_path}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"选择要点击上传的网页元素所在的浏览器对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"element_data\",\"title\":\"拾取点击目标\",\"name\":\"element_data\",\"tip\":\"选择要点击上传的网页元素\",\"required\":true,\"noInput\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\"}},\"key\":\"upload_path\",\"title\":\"上传文件路径\",\"name\":\"upload_path\",\"tip\":\"待上传文件完整路径,若要选择多个文件,切换到Python模式,输入文件列表,例:[\\\"文件1路径\\\", \\\"文件2路径\\\"]\",\"default\":\"\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"simulate_flag\",\"title\":\"模拟人工点击\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工点击是模拟人为操作方式点击,否则将根据拾取元素的自动化接口进行点击\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"download_file\",\"title\":\"文档路径\",\"tip\":\"\"}],\"icon\":\"file-upload-web\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(68,'code','DataProcess.set_variable_value','{\"key\":\"DataProcess.set_variable_value\",\"title\":\"设置变量值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.data.DataProcess().set_variable_value\",\"comment\":\"赋值 @{value} 给变量 @{variable_var}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"变量值\",\"name\":\"value\",\"tip\":\"输入需要设置的变量值\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"VariableType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"variable_type\",\"title\":\"变量类型\",\"name\":\"variable_type\",\"tip\":\"选择需要设置的变量类型\",\"options\":[{\"label\":\"字符串\",\"value\":\"str\"},{\"label\":\"整数\",\"value\":\"int\"},{\"label\":\"浮点数\",\"value\":\"float\"},{\"label\":\"布尔值\",\"value\":\"bool\"},{\"label\":\"列表\",\"value\":\"list\"},{\"label\":\"字典\",\"value\":\"dict\"},{\"label\":\"JSON\",\"value\":\"json\"},{\"label\":\"元组\",\"value\":\"tuple\"},{\"label\":\"其他\",\"value\":\"other\"}],\"default\":\"int\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"variable_var\",\"title\":\"设置后的变量值\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.variable_var.types\",\"expression\":\"return [\'int\',\'str\', \'float\', \'bool\', \'list\', \'dict\'].includes($this.variable_type.value) ? $this.variable_type.value[0].toUpperCase() + $this.variable_type.value.slice(1) : \'Any\'\"}]}],\"icon\":\"set-variable-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(69,'data/data.String','DataConvertProcess.json_convertor','{\"key\":\"DataConvertProcess.json_convertor\",\"title\":\"JSON字符串互转\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dataconvert.DataConvertProcess().json_convertor\",\"comment\":\"将输入文本 @{input_data} 从JSON格式转换为字符串或从字符串格式转换为JSON格式,返回转换后的文本 @{json_convert_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"input_data\",\"title\":\"输入内容\",\"name\":\"input_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"JSONConvertType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"convert_type\",\"title\":\"转换类型\",\"name\":\"convert_type\",\"tip\":\"\",\"options\":[{\"label\":\"JSON转字符串\",\"value\":\"json_to_str\"},{\"label\":\"字符串转JSON\",\"value\":\"str_to_json\"}],\"default\":\"json_to_str\",\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"json_convert_data\",\"title\":\"转换结果\",\"tip\":\"\"}],\"icon\":\"json-string-convert\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(70,'data/data.String','DataConvertProcess.other_to_str','{\"key\":\"DataConvertProcess.other_to_str\",\"title\":\"其他格式转文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dataconvert.DataConvertProcess().other_to_str\",\"comment\":\"将输入内容 @{input_data} 转换为文本(字符串)格式,返回转换后的结果 @{other_convert_str}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"input_data\",\"title\":\"输入内容\",\"name\":\"input_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"other_convert_str\",\"title\":\"转换结果\",\"tip\":\"\"}],\"icon\":\"format-to-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(71,'data/data.String','DataConvertProcess.str_to_other','{\"key\":\"DataConvertProcess.str_to_other\",\"title\":\"文本转其他格式\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dataconvert.DataConvertProcess().str_to_other\",\"comment\":\"将输入文本(字符串) @{input_data} 转换为其他格式,返回转换后的结果 @{str_convert_other}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"input_data\",\"title\":\"输入内容\",\"name\":\"input_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"StringConvertType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"convert_type\",\"title\":\"转换类型\",\"name\":\"convert_type\",\"tip\":\"\",\"options\":[{\"label\":\"字符串转列表\",\"value\":\"str_to_list\"},{\"label\":\"字符串转字典\",\"value\":\"str_to_dict\"},{\"label\":\"字符串转元组\",\"value\":\"str_to_tuple\"},{\"label\":\"字符串转布尔值\",\"value\":\"str_to_bool\"},{\"label\":\"字符串转整数\",\"value\":\"str_to_int\"},{\"label\":\"字符串转浮点数\",\"value\":\"str_to_float\"}],\"default\":\"str_to_int\",\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"str_convert_other\",\"title\":\"转换结果\",\"tip\":\"\"}],\"icon\":\"text-convert-format\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(72,'data/data.Dict','DictProcess.create_new_dict','{\"key\":\"DictProcess.create_new_dict\",\"title\":\"创建新字典\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().create_new_dict\",\"comment\":\"创建一个字典,返回创建的字典 @{created_new_dict_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"created_new_dict_data\",\"title\":\"新创建的字典\",\"tip\":\"\"}],\"icon\":\"create-new-dict\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(73,'data/data.Dict','DictProcess.set_value_to_dict','{\"key\":\"DictProcess.set_value_to_dict\",\"title\":\"字典设置值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().set_value_to_dict\",\"comment\":\"设置字典 @{dict_data} 的键为 @{dict_key} ,值为 @{value} ,返回设置后的字典 @{inserted_dict_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_key\",\"title\":\"键(key)\",\"name\":\"dict_key\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"值(value)\",\"name\":\"value\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"inserted_dict_data\",\"title\":\"设置后的字典\",\"tip\":\"\"}],\"icon\":\"dict-set-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(74,'data/data.Dict','DictProcess.delete_value_from_dict','{\"key\":\"DictProcess.delete_value_from_dict\",\"title\":\"字典删除值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().delete_value_from_dict\",\"comment\":\"从字典 @{dict_data} 中删除键为 @{dict_key} 的值,返回删除后的字典 @{deleted_dict_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_key\",\"title\":\"键(key)\",\"name\":\"dict_key\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"deleted_dict_data\",\"title\":\"删除后的字典\",\"tip\":\"\"}],\"icon\":\"dict-delete-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(75,'data/data.Dict','DictProcess.get_value_from_dict','{\"key\":\"DictProcess.get_value_from_dict\",\"title\":\"字典获取值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().get_value_from_dict\",\"comment\":\"获取字典 @{dict_data} 的键为 @{dict_key} 的值 @{get_dict_value}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_key\",\"title\":\"键(key)\",\"name\":\"dict_key\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"NoKeyOptionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"fail_option\",\"title\":\"键不存在时处理方式\",\"name\":\"fail_option\",\"tip\":\"键不存在时是否抛出异常或返回默认值\",\"options\":[{\"label\":\"抛出异常\",\"value\":\"raise_error\"},{\"label\":\"返回默认值\",\"value\":\"return_default\"}],\"default\":\"raise_error\",\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"default_value\",\"title\":\"默认值\",\"name\":\"default_value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.default_value.show\",\"expression\":\"return $this.fail_option.value == \'return_default\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_dict_value\",\"title\":\"字典值\",\"tip\":\"\"}],\"icon\":\"dict-get-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(76,'data/data.Dict','DictProcess.get_keys_from_dict','{\"key\":\"DictProcess.get_keys_from_dict\",\"title\":\"获取字典所有键\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().get_keys_from_dict\",\"comment\":\"获取字典 @{dict_data} 的所有键 @{get_dict_keys}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_dict_keys\",\"title\":\"键列表\",\"tip\":\"\"}],\"icon\":\"get-dict-all-keys\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(77,'data/data.Dict','DictProcess.get_values_from_dict','{\"key\":\"DictProcess.get_values_from_dict\",\"title\":\"获取字典所有值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.dict.DictProcess().get_values_from_dict\",\"comment\":\"获取字典 @{dict_data} 的所有值 @{get_dict_values}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dict_data\",\"title\":\"字典数据\",\"name\":\"dict_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_dict_values\",\"title\":\"值列表\",\"tip\":\"\"}],\"icon\":\"get-dict-all-values\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(78,'data/data.List','ListProcess.create_new_list','{\"key\":\"ListProcess.create_new_list\",\"title\":\"创建新列表\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().create_new_list\",\"comment\":\"创建一个 @{list_type} 类型的列表,返回创建的列表 @{created_list_data}\",\"inputList\":[{\"types\":\"ListType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"list_type\",\"title\":\"列表类型\",\"name\":\"list_type\",\"tip\":\"\",\"options\":[{\"label\":\"空列表\",\"value\":\"empty\"},{\"label\":\"相同元素列表\",\"value\":\"same_data\"},{\"label\":\"用户自定义列表\",\"value\":\"user_defined\"}],\"default\":\"empty\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"size\",\"title\":\"列表长度\",\"name\":\"size\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.size.show\",\"expression\":\"return $this.list_type.value == \'same_data\'\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"初始值\",\"name\":\"value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.value.show\",\"expression\":\"return [\'same_data\', \'user_defined\'].includes($this.list_type.value)\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"created_list_data\",\"title\":\"创建的列表\",\"tip\":\"\"}],\"icon\":\"create-new-list\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(79,'data/data.List','ListProcess.clear_list','{\"key\":\"ListProcess.clear_list\",\"title\":\"清空列表\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().clear_list\",\"comment\":\"清空列表 @{list_data} ,返回清空后的列表 @{cleared_list_data}\",\"inputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"cleared_list_data\",\"title\":\"清空后的列表\",\"tip\":\"\"}],\"icon\":\"list-clear\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(80,'data/data.List','ListProcess.insert_value_to_list','{\"key\":\"ListProcess.insert_value_to_list\",\"title\":\"列表插入值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().insert_value_to_list\",\"comment\":\"将值 @{value} 插入到列表 @{list_data} 的 @{insert_method} 位置 @{index} ,返回插入后的列表 @{inserted_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"插入值\",\"name\":\"value\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"InsertMethodType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"insert_method\",\"title\":\"插入方式\",\"name\":\"insert_method\",\"tip\":\"\",\"options\":[{\"label\":\"追加\",\"value\":\"append\"},{\"label\":\"指定位置插入\",\"value\":\"index\"}],\"default\":\"append\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"index\",\"title\":\"插入位置\",\"name\":\"index\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.index.show\",\"expression\":\"return $this.insert_method.value == \'index\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"inserted_list_data\",\"title\":\"插入后的列表\",\"tip\":\"\"}],\"icon\":\"list-insert-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(81,'data/data.List','ListProcess.change_value_in_list','{\"key\":\"ListProcess.change_value_in_list\",\"title\":\"列表修改值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().change_value_in_list\",\"comment\":\"将列表 @{list_data} 的 @{index} 位置的值修改为 @{new_value} ,返回修改后的列表 @{changed_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"index\",\"title\":\"位置索引\",\"name\":\"index\",\"tip\":\"填写需要修改值的位置索引,从0开始,修改第一个值索引为0,修改第二个值索引为1,以此类推\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_value\",\"title\":\"新值\",\"name\":\"new_value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"changed_list_data\",\"title\":\"修改后的列表\",\"tip\":\"\"}],\"icon\":\"list-modify-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(82,'data/data.List','ListProcess.get_list_position','{\"key\":\"ListProcess.get_list_position\",\"title\":\"获取值在列表位置\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().get_list_position\",\"comment\":\"获取值 @{value} 在列表 @{list_data} 的位置 @{get_list_position}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"值\",\"name\":\"value\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_list_position\",\"title\":\"位置索引\",\"tip\":\"\"}],\"icon\":\"get-value-position\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(83,'data/data.List','ListProcess.remove_value_from_list','{\"key\":\"ListProcess.remove_value_from_list\",\"title\":\"列表删除值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().remove_value_from_list\",\"comment\":\"以 @{del_mode} 删除@{list_data}@{del_value||del_pos}的内容,返回删除后的列表 @{removed_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"DeleteMethodType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"del_mode\",\"title\":\"删除方式\",\"name\":\"del_mode\",\"tip\":\"\",\"options\":[{\"label\":\"指定位置删除\",\"value\":\"index\"},{\"label\":\"指定值删除\",\"value\":\"value\"}],\"default\":\"index\",\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"del_value\",\"title\":\"删除值\",\"name\":\"del_value\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.del_value.show\",\"expression\":\"return $this.del_mode.value == \'value\'\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"del_pos\",\"title\":\"删除位置\",\"name\":\"del_pos\",\"tip\":\"可指定单个或多个位置数据,多个位置索引之间用逗号隔开,如:1,3,5\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.del_pos.show\",\"expression\":\"return $this.del_mode.value == \'index\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"removed_list_data\",\"title\":\"删除后的列表\",\"tip\":\"\"}],\"icon\":\"list-remove-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(84,'data/data.List','ListProcess.sort_list','{\"key\":\"ListProcess.sort_list\",\"title\":\"列表排序\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().sort_list\",\"comment\":\"对列表 @{list_data} 进行 @{sort_method} 排序,返回排序后的列表 @{sorted_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SortMethodType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sort_method\",\"title\":\"排序方式\",\"name\":\"sort_method\",\"tip\":\"\",\"options\":[{\"label\":\"升序\",\"value\":\"asc\"},{\"label\":\"降序\",\"value\":\"desc\"}],\"default\":\"desc\",\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"sorted_list_data\",\"title\":\"排序后的列表\",\"tip\":\"\"}],\"icon\":\"list-sort\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(85,'data/data.List','ListProcess.random_shuffle_list','{\"key\":\"ListProcess.random_shuffle_list\",\"title\":\"列表随机打乱顺序\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().random_shuffle_list\",\"comment\":\"对列表 @{list_data} 进行随机打乱顺序,返回打乱顺序后的列表 @{shuffled_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"shuffled_list_data\",\"title\":\"打乱顺序后的列表\",\"tip\":\"\"}],\"icon\":\"list-shuffle\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(86,'data/data.List','ListProcess.filter_elements_from_list','{\"key\":\"ListProcess.filter_elements_from_list\",\"title\":\"剔除列表中的多项\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().filter_elements_from_list\",\"comment\":\"从列表 @{list_data_1} 中剔除列表 @{list_data_2} 中的元素,返回剔除后的列表 @{filter_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_1\",\"title\":\"待处理列表\",\"name\":\"list_data_1\",\"tip\":\"填写需要处理的列表\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_2\",\"title\":\"剔除元素列表\",\"name\":\"list_data_2\",\"tip\":\"填写需要剔除的元素列表,如果待处理列表中有剔除元素列表中的元素,则剔除\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"filter_list_data\",\"title\":\"剔除后的列表\",\"tip\":\"返回剔除后的列表\"}],\"icon\":\"list-remove-multiple\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(87,'data/data.List','ListProcess.reverse_list','{\"key\":\"ListProcess.reverse_list\",\"title\":\"列表反转\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().reverse_list\",\"comment\":\"将列表 @{list_data} 反转,返回反转后的列表 @{reversed_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"reversed_list_data\",\"title\":\"反转后的列表\",\"tip\":\"\"}],\"icon\":\"list-reverse\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(88,'data/data.List','ListProcess.merge_list','{\"key\":\"ListProcess.merge_list\",\"title\":\"列表合并\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().merge_list\",\"comment\":\"将列表 @{list_data_1} 和列表 @{list_data_2} 合并为一个列表 @{merged_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_1\",\"title\":\"第一个列表\",\"name\":\"list_data_1\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_2\",\"title\":\"第二个列表\",\"name\":\"list_data_2\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"merged_list_data\",\"title\":\"合并后的列表\",\"tip\":\"\"}],\"icon\":\"list-merge\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(89,'data/data.List','ListProcess.get_unique_list','{\"key\":\"ListProcess.get_unique_list\",\"title\":\"列表去重\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().get_unique_list\",\"comment\":\"将列表 @{list_data} 去重,返回去重后的列表 @{unique_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"unique_list_data\",\"title\":\"去重后的列表\",\"tip\":\"\"}],\"icon\":\"list-remove-duplicates\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(90,'data/data.List','ListProcess.get_common_elements_from_list','{\"key\":\"ListProcess.get_common_elements_from_list\",\"title\":\"获取两个列表的重复项\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().get_common_elements_from_list\",\"comment\":\"获取列表 @{list_data_1} 和列表 @{list_data_2} 的重复项,返回重复项列表 @{common_list_data}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_1\",\"title\":\"第一个列表\",\"name\":\"list_data_1\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data_2\",\"title\":\"第二个列表\",\"name\":\"list_data_2\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"common_list_data\",\"title\":\"重复项列表\",\"tip\":\"\"}],\"icon\":\"get-list-duplicates\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(91,'data/data.List','ListProcess.get_value_from_list','{\"key\":\"ListProcess.get_value_from_list\",\"title\":\"根据索引获取列表值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().get_value_from_list\",\"comment\":\"根据索引 @{index} 获取列表 @{list_data} 的值 @{get_list_value}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"index\",\"title\":\"索引\",\"name\":\"index\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_list_value\",\"title\":\"值\",\"tip\":\"\"}],\"icon\":\"get-list-value-by-index\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(92,'data/data.List','ListProcess.get_length_of_list','{\"key\":\"ListProcess.get_length_of_list\",\"title\":\"获取列表长度\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.list.ListProcess().get_length_of_list\",\"comment\":\"获取列表 @{list_data} 的长度 @{get_list_length}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_list_length\",\"title\":\"长度\",\"tip\":\"\"}],\"icon\":\"get-list-length\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(93,'data/data.Math','MathProcess.generate_random_number','{\"key\":\"MathProcess.generate_random_number\",\"title\":\"生成随机数\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.math.MathProcess().generate_random_number\",\"comment\":\"生成 @{number_type} 类型 @{size} 个随机数,范围为 @{start} 到 @{end} ,返回生成的随机数 @{generated_random_numbers}\",\"inputList\":[{\"types\":\"NumberType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"number_type\",\"title\":\"随机数类型\",\"name\":\"number_type\",\"tip\":\"\",\"options\":[{\"label\":\"整数\",\"value\":\"integer\"},{\"label\":\"浮点数\",\"value\":\"float\"}],\"default\":\"integer\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"size\",\"title\":\"随机数个数\",\"name\":\"size\",\"tip\":\"\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start\",\"title\":\"开始范围\",\"name\":\"start\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end\",\"title\":\"结束范围\",\"name\":\"end\",\"tip\":\"\",\"default\":101,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"generated_random_numbers\",\"title\":\"生成的随机数\",\"tip\":\"\"}],\"icon\":\"generate-random-number\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(94,'data/data.Math','MathProcess.get_rounding_number','{\"key\":\"MathProcess.get_rounding_number\",\"title\":\"四舍五入\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.math.MathProcess().get_rounding_number\",\"comment\":\"将数字 @{number} 四舍五入 @{precision} 位,返回四舍五入后的数字 @{rounding_number}\",\"inputList\":[{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"number\",\"title\":\"原有数据\",\"name\":\"number\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"precision\",\"title\":\"精度\",\"name\":\"precision\",\"tip\":\"可填写小数位数,如填写2,则四舍五入到小数点后2位;也支持填写负数和0,如填写-2,则四舍五入到小数点前2位。\",\"default\":2,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"rounding_number\",\"title\":\"四舍五入后的数字\",\"tip\":\"\"}],\"icon\":\"round-number\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(95,'data/data.Math','MathProcess.self_calculation_number','{\"key\":\"MathProcess.self_calculation_number\",\"title\":\"自增自减\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.math.MathProcess().self_calculation_number\",\"comment\":\"对数字 @{number} 自增或自减 @{add_sub_number} ,返回计算后的结果 @{self_calculation_number}\",\"inputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"number\",\"title\":\"原数据\",\"name\":\"number\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"AddSubType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"add_sub\",\"title\":\"自增/自减\",\"name\":\"add_sub\",\"tip\":\"选择是自增还是自减\",\"options\":[{\"label\":\"加\",\"value\":\"add\"},{\"label\":\"减\",\"value\":\"sub\"}],\"default\":\"add\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"add_sub_number\",\"title\":\"自增或自减的数字\",\"name\":\"add_sub_number\",\"tip\":\"\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"self_calculation_number\",\"title\":\"自增自减后的结果\",\"tip\":\"\"}],\"icon\":\"increment-decrement\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(96,'data/data.Math','MathProcess.get_absolute_number','{\"key\":\"MathProcess.get_absolute_number\",\"title\":\"获取绝对值\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.math.MathProcess().get_absolute_number\",\"comment\":\"获取数字 @{raw_number} 的绝对值 @{absolute_number}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"raw_number\",\"title\":\"原数据\",\"name\":\"raw_number\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"absolute_number\",\"title\":\"原数据绝对值\",\"tip\":\"\"}],\"icon\":\"get-absolute-value\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(97,'data/data.Math','MathProcess.calculate_expression','{\"key\":\"MathProcess.calculate_expression\",\"title\":\"数学计算\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.math.MathProcess().calculate_expression\",\"comment\":\"进行基本的数学计算 @{left} @{operator} @{right} ,返回计算结果 @{calculation_number}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"left\",\"title\":\"运算符左侧值\",\"name\":\"left\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MathOperatorType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"operator\",\"title\":\"运算符\",\"name\":\"operator\",\"tip\":\"\",\"options\":[{\"label\":\"加\",\"value\":\"+\"},{\"label\":\"减\",\"value\":\"-\"},{\"label\":\"乘\",\"value\":\"*\"},{\"label\":\"除\",\"value\":\"/\"}],\"default\":\"+\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"right\",\"title\":\"运算符右侧值\",\"name\":\"right\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MathRoundType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"handle_method\",\"title\":\"返回值处理方式\",\"name\":\"handle_method\",\"tip\":\"\",\"options\":[{\"label\":\"四舍五入\",\"value\":\"round\"},{\"label\":\"向上取整\",\"value\":\"ceil\"},{\"label\":\"向下取整\",\"value\":\"floor\"},{\"label\":\"不做操作\",\"value\":\"none\"}],\"default\":\"none\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"precision\",\"title\":\"四舍五入保留位数\",\"name\":\"precision\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.precision.show\",\"expression\":\"return $this.handle_method.value == \'round\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"calculation_number\",\"title\":\"计算结果\",\"tip\":\"\"}],\"icon\":\"math-calculation\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(98,'data/data.String','StringProcess.extract_content_from_string','{\"key\":\"StringProcess.extract_content_from_string\",\"title\":\"文本提取内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().extract_content_from_string\",\"comment\":\"从文本 @{text} 中提取 @{extract_type} 类型的内容\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"原文本\",\"name\":\"text\",\"tip\":\"输入需要提取内容的文本\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ExtractType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"extract_type\",\"title\":\"提取类型\",\"name\":\"extract_type\",\"tip\":\"选择需要提取的类型\",\"options\":[{\"label\":\"中国大陆手机号码\",\"value\":\"phone_number\"},{\"label\":\"邮箱地址\",\"value\":\"email\"},{\"label\":\"网址\",\"value\":\"url\"},{\"label\":\"数字\",\"value\":\"digit\"},{\"label\":\"中国大陆身份证号码\",\"value\":\"id_number\"},{\"label\":\"正则表达式\",\"value\":\"regex\"}],\"default\":\"digit\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"regex_formula\",\"title\":\"正则表达式\",\"name\":\"regex_formula\",\"tip\":\"输入正则表达式,例如[\\\\u4e00-\\\\u9fff]用于提取中文,\\\\d用于提取数字,[a-zA-Z]用于提取英文等\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.regex_formula.show\",\"expression\":\"return $this.extract_type.value == \'regex\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"first_flag\",\"title\":\"仅提取第一项匹配内容\",\"name\":\"first_flag\",\"tip\":\"是否仅提取第一项匹配内容\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"extract_from_string\",\"title\":\"提取内容\",\"tip\":\"返回提取的内容\"}],\"icon\":\"text-extract-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(99,'data/data.String','StringProcess.replace_content_in_string','{\"key\":\"StringProcess.replace_content_in_string\",\"title\":\"文本替换内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().replace_content_in_string\",\"comment\":\"将文本 @{text} 中的 @{replace_type} 类型内容替换为 @{new_value}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"原文本\",\"name\":\"text\",\"tip\":\"输入需要替换内容的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ReplaceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"replace_type\",\"title\":\"替换类型\",\"name\":\"replace_type\",\"tip\":\"选择需要替换的类型\",\"options\":[{\"label\":\"字符串\",\"value\":\"string\"},{\"label\":\"中国大陆手机号码\",\"value\":\"phone_number\"},{\"label\":\"邮箱地址\",\"value\":\"email\"},{\"label\":\"网址\",\"value\":\"url\"},{\"label\":\"数字\",\"value\":\"digit\"},{\"label\":\"中国大陆身份证号码\",\"value\":\"id_number\"},{\"label\":\"正则表达式\",\"value\":\"regex\"}],\"default\":\"string\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"replaced_string\",\"title\":\"需要替换的字符串\",\"name\":\"replaced_string\",\"tip\":\"输入需要替换的字符串\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.replaced_string.show\",\"expression\":\"return $this.replace_type.value == \'string\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"regex_formula\",\"title\":\"用正则表达式替换\",\"name\":\"regex_formula\",\"tip\":\"输入正则表达式用于替换内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.regex_formula.show\",\"expression\":\"return $this.replace_type.value == \'regex\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_value\",\"title\":\"替换为\",\"name\":\"new_value\",\"tip\":\"输入新值\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"first_flag\",\"title\":\"仅替换第一项匹配内容\",\"name\":\"first_flag\",\"tip\":\"是否仅替换第一项匹配内容\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"ignore_case_flag\",\"title\":\"忽略大小写\",\"name\":\"ignore_case_flag\",\"tip\":\"是否忽略大小写\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"replaced_content_string\",\"title\":\"替换后的文本\",\"tip\":\"返回替换后的文本\"}],\"icon\":\"text-replace-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(100,'data/data.String','StringProcess.merge_list_to_string','{\"key\":\"StringProcess.merge_list_to_string\",\"title\":\"列表聚合为文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().merge_list_to_string\",\"comment\":\"将列表 @{list_data} 中的内容聚合为文本 @{merged_string_from_list}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list_data\",\"title\":\"列表数据\",\"name\":\"list_data\",\"tip\":\"输入需要聚合的列表数据\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"separator\",\"title\":\"分隔符\",\"name\":\"separator\",\"tip\":\"输入分隔符\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"merged_string_from_list\",\"title\":\"聚合后的文本\",\"tip\":\"返回聚合后的文本\"}],\"icon\":\"list-to-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(101,'data/data.String','StringProcess.split_string_to_list','{\"key\":\"StringProcess.split_string_to_list\",\"title\":\"文本分割为列表\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().split_string_to_list\",\"comment\":\"将文本 @{string_data} 按 @{separator} 分割为列表 @{split_list_from_string}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"文本数据\",\"name\":\"string_data\",\"tip\":\"输入需要分割的文本数据\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"separator\",\"title\":\"分隔符\",\"name\":\"separator\",\"tip\":\"输入分隔符\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"split_list_from_string\",\"title\":\"分割后的列表\",\"tip\":\"返回分割后的列表\"}],\"icon\":\"text-split-to-list\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(102,'data/data.String','StringProcess.concatenate_string','{\"key\":\"StringProcess.concatenate_string\",\"title\":\"文本合并\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().concatenate_string\",\"comment\":\"将 @{string_data_1} 和 @{string_data_2} 合并为 @{concat_string}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data_1\",\"title\":\"第一个文本\",\"name\":\"string_data_1\",\"tip\":\"输入需要合并的第一个文本\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data_2\",\"title\":\"第二个文本\",\"name\":\"string_data_2\",\"tip\":\"输入需要合并的第二个文本\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ConcatStringType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"concat_type\",\"title\":\"合并类型\",\"name\":\"concat_type\",\"tip\":\"选择需要合并的类型,可以自定义分隔符\",\"options\":[{\"label\":\"不使用分隔符\",\"value\":\"none\"},{\"label\":\"换行符\",\"value\":\"linebreak\"},{\"label\":\"空格( )\",\"value\":\"space\"},{\"label\":\"连字符(-)\",\"value\":\"hyphen\"},{\"label\":\"下划线(_)\",\"value\":\"underline\"},{\"label\":\"自定义\",\"value\":\"other\"}],\"default\":\"none\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"separator\",\"title\":\"分隔符\",\"name\":\"separator\",\"tip\":\"输入分隔符\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.separator.show\",\"expression\":\"return $this.concat_type.value == \'other\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"concat_string\",\"title\":\"合并后的文本\",\"tip\":\"返回合并后的文本\"}],\"icon\":\"text-merge\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(103,'data/data.String','StringProcess.fill_string_to_length','{\"key\":\"StringProcess.fill_string_to_length\",\"title\":\"文本补齐至固定长度\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().fill_string_to_length\",\"comment\":\"将文本 @{string_data} 补齐至 @{total_length} 长度,补齐方式为 @{fill_type}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"文本数据\",\"name\":\"string_data\",\"tip\":\"输入需要补齐的文本数据\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"add_str\",\"title\":\"补齐字符\",\"name\":\"add_str\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"total_length\",\"title\":\"总长度\",\"name\":\"total_length\",\"tip\":\"输入需要补齐的总长度\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FillStringType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"fill_type\",\"title\":\"补齐方式\",\"name\":\"fill_type\",\"tip\":\"选择补齐方式,可以选择左补齐、右补齐\",\"options\":[{\"label\":\"右补齐\",\"value\":\"right\"},{\"label\":\"左补齐\",\"value\":\"left\"}],\"default\":\"right\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"complete_string\",\"title\":\"补齐后的文本\",\"tip\":\"\"}],\"icon\":\"text-pad-to-length\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(104,'data/data.String','StringProcess.strip_string','{\"key\":\"StringProcess.strip_string\",\"title\":\"文本去除两侧空格\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().strip_string\",\"comment\":\"将文本 @{string_data} 两侧的空格去除 @{stripped_string}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"文本数据\",\"name\":\"string_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"StripStringType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"strip_method\",\"title\":\"去除方式\",\"name\":\"strip_method\",\"tip\":\"\",\"options\":[{\"label\":\"左侧\",\"value\":\"left\"},{\"label\":\"右侧\",\"value\":\"right\"},{\"label\":\"两侧\",\"value\":\"both\"}],\"default\":\"both\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"stripped_string\",\"title\":\"去除两侧空格后的文本\",\"tip\":\"\"}],\"icon\":\"text-trim-spaces\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(105,'data/data.String','StringProcess.cut_string_to_length','{\"key\":\"StringProcess.cut_string_to_length\",\"title\":\"截取固定长度文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().cut_string_to_length\",\"comment\":\"将文本 @{string_data} 截取 @{length} 长度,截取方式为 @{cut_type} ,返回截取后的文本 @{cut_string}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"截取文本\",\"name\":\"string_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"length\",\"title\":\"截取长度\",\"name\":\"length\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CutStringType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"cut_type\",\"title\":\"截取类型\",\"name\":\"cut_type\",\"tip\":\"\",\"options\":[{\"label\":\"从第一个字符开始截取\",\"value\":\"first\"},{\"label\":\"从指定位置开始截取\",\"value\":\"index\"},{\"label\":\"从指定字符串开始截取\",\"value\":\"string\"}],\"default\":\"first\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"index\",\"title\":\"从第几个字符开始截取\",\"name\":\"index\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.index.show\",\"expression\":\"return $this.cut_type.value == \'index\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"find_str\",\"title\":\"需要检索的字符串\",\"name\":\"find_str\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.find_str.show\",\"expression\":\"return $this.cut_type.value == \'string\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"cut_string\",\"title\":\"截取后的文本\",\"tip\":\"\"}],\"icon\":\"screenshot-fixed-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(106,'data/data.String','StringProcess.change_case_of_string','{\"key\":\"StringProcess.change_case_of_string\",\"title\":\"更改文本大小写\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().change_case_of_string\",\"comment\":\"将文本 @{string_data} 的大小写更改为 @{case_type} ,返回更改后的文本 @{change_case_string}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"文本数据\",\"name\":\"string_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CaseChangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"case_type\",\"title\":\"大小写类型\",\"name\":\"case_type\",\"tip\":\"\",\"options\":[{\"label\":\"全部大写\",\"value\":\"upper\"},{\"label\":\"全部小写\",\"value\":\"lower\"},{\"label\":\"首字母大写\",\"value\":\"caps\"}],\"default\":\"lower\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"change_case_string\",\"title\":\"更改后的文本\",\"tip\":\"\"}],\"icon\":\"change-text-case\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(107,'data/data.String','StringProcess.get_string_length','{\"key\":\"StringProcess.get_string_length\",\"title\":\"获取文本长度\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.string.StringProcess().get_string_length\",\"comment\":\"获取文本 @{string_data} 的长度 @{string_length}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"文本数据\",\"name\":\"string_data\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"string_length\",\"title\":\"文本长度\",\"tip\":\"\"}],\"icon\":\"get-text-length\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(108,'data/data.Time','TimeProcess.get_current_time','{\"key\":\"TimeProcess.get_current_time\",\"title\":\"获取当前时间\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().get_current_time\",\"comment\":\"获取当前时间,格式为 @{time_format},返回当前时间对象 @{current_time}\",\"inputList\":[{\"types\":\"TimeFormatType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"time_format\",\"title\":\"时间格式\",\"name\":\"time_format\",\"tip\":\"\",\"options\":[{\"label\":\"年-月-日\",\"value\":\"%Y-%m-%d\"},{\"label\":\"年-月-日 时:分:秒\",\"value\":\"%Y-%m-%d %H:%M:%S\"},{\"label\":\"年-月-日 时:分\",\"value\":\"%Y-%m-%d %H:%M\"},{\"label\":\"年/月/日\",\"value\":\"%Y/%m/%d\"},{\"label\":\"年/月/日 时:分\",\"value\":\"%Y/%m/%d %H:%M\"},{\"label\":\"年/月/日 时:分:秒\",\"value\":\"%Y/%m/%d %H:%M:%S\"},{\"label\":\"年月日\",\"value\":\"%Y%m%d\"},{\"label\":\"时:分\",\"value\":\"%H:%M\"},{\"label\":\"时:分:秒\",\"value\":\"%H:%M:%S\"},{\"label\":\"一周的第几天\",\"value\":\"%w\"},{\"label\":\"一年的第几天\",\"value\":\"%j\"},{\"label\":\"一年的第几周\",\"value\":\"%W\"},{\"label\":\"XXXX年XX月XX日\",\"value\":\"%Y年%m月%d日\"},{\"label\":\"XXXX年XX月XX日 XX:XX\",\"value\":\"%Y年%m月%d日 %H:%M\"},{\"label\":\"XXXX年XX月XX日 XX:XX:XX\",\"value\":\"%Y年%m月%d日 %H:%M:%S\"}],\"default\":\"%Y-%m-%d %H:%M:%S\",\"required\":true}],\"outputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"current_time\",\"title\":\"当前时间对象\",\"tip\":\"返回值为时间对象,可通过快捷函数或转化原子能力输出字符串形式\"}],\"icon\":\"get-current-time\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(109,'data/data.Time','TimeProcess.set_time','{\"key\":\"TimeProcess.set_time\",\"title\":\"设置时间\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().set_time\",\"comment\":\"设置时间 @{time},设置方式为 @{change_type},返回设置后的时间对象 @{set_time}\",\"inputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_DATETIME\"},\"key\":\"time\",\"title\":\"时间对象\",\"name\":\"time\",\"tip\":\"\",\"required\":true},{\"types\":\"TimeChangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"change_type\",\"title\":\"日期调整方式\",\"name\":\"change_type\",\"tip\":\"\",\"options\":[{\"label\":\"保持不变\",\"value\":\"maintain\"},{\"label\":\"增加时间\",\"value\":\"add\"},{\"label\":\"减少时间\",\"value\":\"sub\"}],\"default\":\"maintain\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"seconds\",\"title\":\"秒\",\"name\":\"seconds\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.seconds.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"minutes\",\"title\":\"分\",\"name\":\"minutes\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.minutes.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"hours\",\"title\":\"时\",\"name\":\"hours\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.hours.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"days\",\"title\":\"天\",\"name\":\"days\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.days.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"months\",\"title\":\"月\",\"name\":\"months\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.months.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"years\",\"title\":\"年\",\"name\":\"years\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.years.show\",\"expression\":\"return $this.change_type.value != \'maintain\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"set_time\",\"title\":\"设置时间对象\",\"tip\":\"\"}],\"icon\":\"set-time\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(110,'data/data.Time','TimeProcess.time_to_timestamp','{\"key\":\"TimeProcess.time_to_timestamp\",\"title\":\"时间对象转时间戳\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().time_to_timestamp\",\"comment\":\"将时间对象 @{time} 转换为 @{timestamp_unit} 精度的时间戳,返回转换后的时间戳 @{converted_timestamp}\",\"inputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_DATETIME\"},\"key\":\"time\",\"title\":\"时间对象\",\"name\":\"time\",\"tip\":\"\",\"required\":true},{\"types\":\"TimestampUnitType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"timestamp_unit\",\"title\":\"时间戳精度\",\"name\":\"timestamp_unit\",\"tip\":\"\",\"options\":[{\"label\":\"秒\",\"value\":\"second\"},{\"label\":\"毫秒\",\"value\":\"millisecond\"},{\"label\":\"微秒\",\"value\":\"microsecond\"}],\"default\":\"second\",\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"converted_timestamp\",\"title\":\"时间戳\",\"tip\":\"\"}],\"icon\":\"datetime-to-timestamp\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(111,'data/data.Time','TimeProcess.timestamp_to_time','{\"key\":\"TimeProcess.timestamp_to_time\",\"title\":\"时间戳转时间对象\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().timestamp_to_time\",\"comment\":\"将时间戳 @{timestamp} 转换为时间对象 @{converted_time}\",\"inputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"timestamp\",\"title\":\"时间戳\",\"name\":\"timestamp\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"TimeZoneType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"time_zone\",\"title\":\"选择时区\",\"name\":\"time_zone\",\"tip\":\"\",\"options\":[{\"label\":\"UTC标准时间\",\"value\":\"UTC\"},{\"label\":\"本地时间\",\"value\":\"local\"}],\"default\":\"local\",\"required\":true}],\"outputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"converted_time\",\"title\":\"时间对象\",\"tip\":\"\"}],\"icon\":\"timestamp-to-datetime\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(112,'data/data.Time','TimeProcess.get_time_difference','{\"key\":\"TimeProcess.get_time_difference\",\"title\":\"获取时间差\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().get_time_difference\",\"comment\":\"获取 @{time_1} 和 @{time_2} 之间的时间差,单位为 @{time_unit},返回时间差 @{time_difference}\",\"inputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_DATETIME\"},\"key\":\"time_1\",\"title\":\"时间对象1\",\"name\":\"time_1\",\"tip\":\"\",\"required\":true},{\"types\":\"Date\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_DATETIME\"},\"key\":\"time_2\",\"title\":\"时间对象2\",\"name\":\"time_2\",\"tip\":\"\",\"required\":true},{\"types\":\"TimeUnitType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"time_unit\",\"title\":\"时间单位\",\"name\":\"time_unit\",\"tip\":\"\",\"options\":[{\"label\":\"秒\",\"value\":\"second\"},{\"label\":\"分\",\"value\":\"minute\"},{\"label\":\"时\",\"value\":\"hour\"},{\"label\":\"天\",\"value\":\"day\"},{\"label\":\"月\",\"value\":\"month\"},{\"label\":\"年\",\"value\":\"year\"}],\"default\":\"second\",\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"time_difference\",\"title\":\"时间差\",\"tip\":\"\"}],\"icon\":\"get-time-difference\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(113,'data/data.Time','TimeProcess.format_datetime','{\"key\":\"TimeProcess.format_datetime\",\"title\":\"输出指定格式时间文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.dataprocess.time.TimeProcess().format_datetime\",\"comment\":\"将时间对象 @{time} 按照指定格式 @{format_type} 输出文本 @{format_datetime}\",\"inputList\":[{\"types\":\"Date\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_DATETIME\"},\"key\":\"time\",\"title\":\"时间对象\",\"name\":\"time\",\"tip\":\"\",\"required\":true},{\"types\":\"TimeFormatType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"format_type\",\"title\":\"时间格式\",\"name\":\"format_type\",\"tip\":\"\",\"options\":[{\"label\":\"年-月-日\",\"value\":\"%Y-%m-%d\"},{\"label\":\"年-月-日 时:分:秒\",\"value\":\"%Y-%m-%d %H:%M:%S\"},{\"label\":\"年-月-日 时:分\",\"value\":\"%Y-%m-%d %H:%M\"},{\"label\":\"年/月/日\",\"value\":\"%Y/%m/%d\"},{\"label\":\"年/月/日 时:分\",\"value\":\"%Y/%m/%d %H:%M\"},{\"label\":\"年/月/日 时:分:秒\",\"value\":\"%Y/%m/%d %H:%M:%S\"},{\"label\":\"年月日\",\"value\":\"%Y%m%d\"},{\"label\":\"时:分\",\"value\":\"%H:%M\"},{\"label\":\"时:分:秒\",\"value\":\"%H:%M:%S\"},{\"label\":\"一周的第几天\",\"value\":\"%w\"},{\"label\":\"一年的第几天\",\"value\":\"%j\"},{\"label\":\"一年的第几周\",\"value\":\"%W\"},{\"label\":\"XXXX年XX月XX日\",\"value\":\"%Y年%m月%d日\"},{\"label\":\"XXXX年XX月XX日 XX:XX\",\"value\":\"%Y年%m月%d日 %H:%M\"},{\"label\":\"XXXX年XX月XX日 XX:XX:XX\",\"value\":\"%Y年%m月%d日 %H:%M:%S\"}],\"default\":\"%Y-%m-%d %H:%M:%S\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"format_datetime\",\"title\":\"时间文本\",\"tip\":\"\"}],\"icon\":\"output-formatted-time\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(114,'dialog','Dialog.message_box','{\"key\":\"Dialog.message_box\",\"title\":\"消息提示框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().message_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title\",\"title\":\"对话框标题\",\"name\":\"box_title\",\"tip\":\"用户自定义的对话框上方的显示标题,限制标题内容字符数量为50个字符。不输入则默认标题为“消息提示框”\",\"default\":\"消息提示框\",\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"MessageType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"message_type\",\"title\":\"消息类型\",\"name\":\"message_type\",\"tip\":\"可选消息类型\",\"options\":[{\"label\":\"信息\",\"value\":\"message\"},{\"label\":\"警告\",\"value\":\"warning\"},{\"label\":\"问题\",\"value\":\"question\"},{\"label\":\"错误\",\"value\":\"error\"}],\"default\":\"message\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"message_content\",\"title\":\"消息内容\",\"name\":\"message_content\",\"tip\":\"用户自定义的对话框内的显示内容,限制内容字符数量为120个字符\",\"default\":\"\",\"required\":true,\"limitLength\":[-1,120]},{\"types\":\"ButtonType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"button_type\",\"title\":\"展示按钮\",\"name\":\"button_type\",\"tip\":\"消息框可展示给用户进行选择的按钮,只允许切换,不允许空选状态\",\"options\":[{\"label\":\"确认\",\"value\":\"confirm\"},{\"label\":\"确认-取消\",\"value\":\"confirm_cancel\"},{\"label\":\"是-否\",\"value\":\"yes_no\"},{\"label\":\"是-否-取消\",\"value\":\"yes_no_cancel\"}],\"default\":\"confirm\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\"},\"key\":\"auto_check\",\"title\":\"无操作自动关闭对话框\",\"name\":\"auto_check\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"无操作超时等待(秒)\",\"name\":\"wait_time\",\"tip\":\"设置超时等待时间,未识别到用户鼠标移动行为超过该时间则选中默认按钮,默认为60秒\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.wait_time.show\",\"expression\":\"return $this.auto_check.value == true\"}],\"required\":false},{\"types\":\"DefaultButtonC\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"default_button_c\",\"title\":\"超时默认选中按钮\",\"name\":\"default_button_c\",\"tip\":\"\",\"options\":[{\"label\":\"确认\",\"value\":\"confirm\"}],\"default\":\"confirm\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.default_button_c.show\",\"expression\":\"return $this.auto_check.value == true && $this.button_type.value == \'confirm\'\"}],\"required\":true},{\"types\":\"DefaultButtonCN\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"default_button_cn\",\"title\":\"超时默认选中按钮\",\"name\":\"default_button_cn\",\"tip\":\"\",\"options\":[{\"label\":\"确认\",\"value\":\"confirm\"},{\"label\":\"取消\",\"value\":\"cancel\"}],\"default\":\"confirm\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.default_button_cn.show\",\"expression\":\"return $this.auto_check.value == true && $this.button_type.value == \'confirm_cancel\'\"}],\"required\":true},{\"types\":\"DefaultButtonY\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"default_button_y\",\"title\":\"超时默认选中按钮\",\"name\":\"default_button_y\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":\"yes\"},{\"label\":\"否\",\"value\":\"no\"}],\"default\":\"yes\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.default_button_y.show\",\"expression\":\"return $this.auto_check.value == true && $this.button_type.value == \'yes_no\'\"}],\"required\":true},{\"types\":\"DefaultButtonYN\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"default_button_yn\",\"title\":\"超时默认选中按钮\",\"name\":\"default_button_yn\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":\"yes\"},{\"label\":\"否\",\"value\":\"no\"},{\"label\":\"取消\",\"value\":\"cancel\"}],\"default\":\"yes\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.default_button_yn.show\",\"expression\":\"return $this.auto_check.value == true && $this.button_type.value == \'yes_no_cancel\'\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"preview_button\",\"title\":\"预览\",\"name\":\"preview_button\",\"tip\":\"\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"result_button\",\"title\":\"保存用户选择的按钮至\",\"tip\":\"\"}],\"icon\":\"message-dialog\",\"helpManual\":\"打开消息提示对话框,可设定消息提示的内容和标题。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(115,'dialog','Dialog.input_box','{\"key\":\"Dialog.input_box\",\"title\":\"输入对话框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().input_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title\",\"title\":\"对话框标题\",\"name\":\"box_title\",\"tip\":\"限制标题内容字符数量为50个字符,不输入则默认标题为“输入对话框”\",\"default\":\"输入对话框\",\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"InputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"input_type\",\"title\":\"输入框类型\",\"name\":\"input_type\",\"tip\":\"\",\"options\":[{\"label\":\"文本框\",\"value\":\"text\"},{\"label\":\"密码框\",\"value\":\"password\"}],\"default\":\"text\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"input_title\",\"title\":\"输入框标题\",\"name\":\"input_title\",\"tip\":\"\",\"default\":\"输入框标题\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false,\"limitLength\":[-1,60]},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"default_input_text\",\"title\":\"输入框内容默认值\",\"name\":\"default_input_text\",\"tip\":\"填写则在对话框中展示默认文本作为文本提示符,限制内容字符数量为120个字符\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.default_input_text.show\",\"expression\":\"return $this.input_type.value == \'text\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"DEFAULTPASSWORD\"},\"key\":\"default_input_pwd\",\"title\":\"输入框内容默认值\",\"name\":\"default_input_pwd\",\"tip\":\"填写则在对话框中展示默认密码,密码长度在4-16位之间\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.default_input_pwd.show\",\"expression\":\"return $this.input_type.value == \'password\'\"}],\"required\":false,\"limitLength\":[4,16]},{\"types\":\"Any\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"preview_button\",\"title\":\"预览\",\"name\":\"preview_button\",\"tip\":\"\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"input_content\",\"title\":\"保存用户输入内容至\",\"tip\":\"\"}],\"icon\":\"input-dialog\",\"helpManual\":\"打开输入对话框,需用户进行信息输入并确定提交,返回用户在对话框中输入的内容。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(116,'dialog','Dialog.select_box','{\"key\":\"Dialog.select_box\",\"title\":\"选择对话框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().select_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title\",\"title\":\"对话框标题\",\"name\":\"box_title\",\"tip\":\"\",\"default\":\"选择对话框\",\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"SelectType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_type\",\"title\":\"选择模式\",\"name\":\"select_type\",\"tip\":\"\",\"options\":[{\"label\":\"单选\",\"value\":\"single\"},{\"label\":\"多选\",\"value\":\"multi\"}],\"default\":\"single\",\"required\":true},{\"types\":\"List\",\"formType\":{\"type\":\"OPTIONSLIST\"},\"key\":\"options\",\"title\":\"选项\",\"name\":\"options\",\"tip\":\"\",\"default\":[],\"need_parse\":\"str\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"options_title\",\"title\":\"选择框标题\",\"name\":\"options_title\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"preview_button\",\"title\":\"预览\",\"name\":\"preview_button\",\"tip\":\"\",\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"select_result\",\"title\":\"保存选择结果至\",\"tip\":\"\"}],\"icon\":\"select-dialog\",\"helpManual\":\"打开选择对话框,保存用户从选择列表中选择的一个或多个选项\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(117,'dialog','Dialog.select_time_box','{\"key\":\"Dialog.select_time_box\",\"title\":\"日期时间选择框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().select_time_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title\",\"title\":\"对话框标题\",\"name\":\"box_title\",\"tip\":\"\",\"default\":\"日期时间选择框\",\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"TimeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"time_type\",\"title\":\"时间类型\",\"name\":\"time_type\",\"tip\":\"\",\"options\":[{\"label\":\"时间\",\"value\":\"time\"},{\"label\":\"时间段\",\"value\":\"time_range\"}],\"default\":\"time\",\"required\":false},{\"types\":\"TimeFormat\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"time_format\",\"title\":\"时间格式\",\"name\":\"time_format\",\"tip\":\"\",\"options\":[{\"label\":\"年-月-日\",\"value\":\"YYYY-MM-DD\"},{\"label\":\"年-月-日 时:分\",\"value\":\"YYYY-MM-DD HH:mm\"},{\"label\":\"年-月-日 时:分:秒\",\"value\":\"YYYY-MM-DD HH:mm:ss\"},{\"label\":\"年/月/日\",\"value\":\"YYYY/MM/DD\"},{\"label\":\"年/月/日 时:分\",\"value\":\"YYYY/MM/DD HH:mm\"},{\"label\":\"年/月/日 时:分:秒\",\"value\":\"YYYY/MM/DD HH:mm:ss\"}],\"default\":\"YYYY-MM-DD\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"DEFAULTDATEPICKER\",\"params\":{\"format\":\"YYYY-MM-DD\"}},\"key\":\"default_time\",\"title\":\"默认时间\",\"name\":\"default_time\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.default_time.show\",\"expression\":\"return $this.time_type.value == \'time\'\"},{\"key\":\"$this.default_time.formType.params.format\",\"expression\":\"return $this.time_format.value\"}],\"required\":false},{\"types\":\"List\",\"formType\":{\"type\":\"RANGEDATEPICKER\",\"params\":{\"format\":\"YYYY-MM-DD\"}},\"key\":\"default_time_range\",\"title\":\"默认时间\",\"name\":\"default_time_range\",\"tip\":\"\",\"default\":[\"\",\"\"],\"dynamics\":[{\"key\":\"$this.default_time_range.show\",\"expression\":\"return $this.time_type.value == \'time_range\'\"},{\"key\":\"$this.default_time.formType.params.format\",\"expression\":\"return $this.time_format.value\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"input_title\",\"title\":\"输入框标题\",\"name\":\"input_title\",\"tip\":\"\",\"default\":\"输入框标题\",\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"preview_button\",\"title\":\"预览\",\"name\":\"preview_button\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"select_time\",\"title\":\"保存时间选择结果至\",\"tip\":\"\"}],\"icon\":\"datetime-picker\",\"helpManual\":\"打开日期时间选择框,保存用户选择的日期或时间\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(118,'dialog','Dialog.select_file_box','{\"key\":\"Dialog.select_file_box\",\"title\":\"文件选择对话框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().select_file_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title_file\",\"title\":\"对话框标题\",\"name\":\"box_title_file\",\"tip\":\"\",\"default\":\"文件选择框\",\"dynamics\":[{\"key\":\"$this.box_title_file.show\",\"expression\":\"return $this.open_type.value == \'file\'\"}],\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title_folder\",\"title\":\"对话框标题\",\"name\":\"box_title_folder\",\"tip\":\"\",\"default\":\"文件夹选择框\",\"dynamics\":[{\"key\":\"$this.box_title_folder.show\",\"expression\":\"return $this.open_type.value == \'folder\'\"}],\"required\":false,\"limitLength\":[-1,50]},{\"types\":\"OpenType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"open_type\",\"title\":\"打开类型\",\"name\":\"open_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false},{\"types\":\"FileType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"file_type\",\"title\":\"文件类型\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"所有文件\",\"value\":\"*\"},{\"label\":\"Excel文件\",\"value\":\".xls,.xlsx\"},{\"label\":\"Word文件\",\"value\":\".doc,.docx\"},{\"label\":\"文本文件\",\"value\":\".txt\"},{\"label\":\"图像文件\",\"value\":\".png,.jpg,.jpeg,.bmp,.gif\"},{\"label\":\"PPT文件\",\"value\":\".ppt,.pptx\"},{\"label\":\"压缩文件\",\"value\":\".zip,.rar\"}],\"default\":\"*\",\"dynamics\":[{\"key\":\"$this.file_type.show\",\"expression\":\"return $this.open_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\"},\"key\":\"multiple_choice\",\"title\":\"是否允许多选\",\"name\":\"multiple_choice\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"dynamics\":[{\"key\":\"$this.multiple_choice.show\",\"expression\":\"return $this.open_type.value == \'file\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"select_title\",\"title\":\"选择框标题\",\"name\":\"select_title\",\"tip\":\"\",\"default\":\"\",\"required\":false,\"limitLength\":[-1,60]},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"default_path\",\"title\":\"默认文件夹\",\"name\":\"default_path\",\"tip\":\"当配置了该参数,则在打开文件选择框时默认解析在该文件夹路径进行下一步选择\",\"default\":\"\",\"level\":\"advanced\",\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"preview_button\",\"title\":\"预览\",\"name\":\"preview_button\",\"tip\":\"\",\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"select_file\",\"title\":\"保存文件选择结果至\",\"tip\":\"保存用户的文件选择结果,如果用户点击取消按钮则返回为空值\"}],\"icon\":\"file-select-dialog\",\"helpManual\":\"打开系统选择文件对话框,保存用户选择的对应的文件/文件夹路径\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(119,'dialog','Dialog.custom_box','{\"key\":\"Dialog.custom_box\",\"title\":\"自定义对话框\",\"version\":\"1.0.0\",\"src\":\"astronverse.dialog.dialog.Dialog().custom_box\",\"comment\":\"\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT\"},\"key\":\"box_title\",\"title\":\"对话框标题\",\"name\":\"box_title\",\"tip\":\"\",\"default\":\"自定义对话框\",\"required\":true,\"limitLength\":[-1,50]},{\"types\":\"Str\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"design_interface\",\"title\":\"设计对话框界面\",\"name\":\"design_interface\",\"tip\":\"点击按钮则进入对话框设计弹窗界面\",\"need_parse\":\"json_str\",\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\"},\"key\":\"auto_check\",\"title\":\"无操作自动关闭对话框\",\"name\":\"auto_check\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"无操作超时等待(秒)\",\"name\":\"wait_time\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.wait_time.show\",\"expression\":\"return $this.auto_check.value == true\"}],\"required\":false},{\"types\":\"DefaultButtonCN\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"default_button\",\"title\":\"超时默认选中按钮\",\"name\":\"default_button\",\"tip\":\"\",\"options\":[{\"label\":\"确认\",\"value\":\"confirm\"},{\"label\":\"取消\",\"value\":\"cancel\"}],\"default\":\"confirm\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.default_button.show\",\"expression\":\"return $this.auto_check.value == true\"}],\"required\":true}],\"outputList\":[{\"types\":\"DialogResult\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"dialog_result\",\"title\":\"自定义对话框结果\",\"tip\":\"存储所有表单控件的输出结果以及点击的按钮结果\"}],\"icon\":\"custom-dialog\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(120,'network/email','Email.send_email','{\"key\":\"Email.send_email\",\"title\":\"发送邮件\",\"version\":\"1.0.0\",\"src\":\"astronverse.email.email.Email().send_email\",\"comment\":\"给指定邮箱 @{receiver} 发送邮件\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"receiver\",\"title\":\"收件人邮箱\",\"name\":\"receiver\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cc\",\"title\":\"抄送邮箱\",\"name\":\"cc\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"subject\",\"title\":\"主题\",\"name\":\"subject\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_html\",\"title\":\"是否为HTML格式\",\"name\":\"is_html\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"CONTENTPASTE\"},\"key\":\"content\",\"title\":\"内容\",\"name\":\"content\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"files\"}},\"key\":\"attachment_path\",\"title\":\"附件路径\",\"name\":\"attachment_path\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"EmailServerType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"mail_server\",\"title\":\"邮件服务器\",\"name\":\"mail_server\",\"tip\":\"\",\"options\":[{\"label\":\"其他邮箱\",\"value\":\"other\"},{\"label\":\"126\",\"value\":\"126\"},{\"label\":\"163\",\"value\":\"163\"},{\"label\":\"QQ\",\"value\":\"qq\"},{\"label\":\"讯飞邮箱\",\"value\":\"iflytek\"}],\"default\":\"qq\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"other_mail_server\",\"title\":\"其他邮件服务器\",\"name\":\"other_mail_server\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.other_mail_server.show\",\"expression\":\"return $this.mail_server.value == \'other\'\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"mail_port\",\"title\":\"邮件服务器端口\",\"name\":\"mail_port\",\"tip\":\"\",\"default\":465,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"use_ssl\",\"title\":\"是否使用SSL加密\",\"name\":\"use_ssl\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sender_mail\",\"title\":\"发件人邮箱\",\"name\":\"sender_mail\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"send_name\",\"title\":\"发件人名称\",\"name\":\"send_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"密码/授权码\",\"name\":\"password\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"bcc\",\"title\":\"密送邮箱\",\"name\":\"bcc\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"MODALBUTTON\"},\"key\":\"replace_table\",\"title\":\"智能填充表\",\"name\":\"replace_table\",\"tip\":\"\",\"default\":\"\",\"need_parse\":\"json_str\",\"required\":false}],\"outputList\":[],\"icon\":\"send-email\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(121,'network/email','Email.receive_email','{\"key\":\"Email.receive_email\",\"title\":\"接收邮件\",\"version\":\"1.0.0\",\"src\":\"astronverse.email.email.Email().receive_email\",\"comment\":\"从邮箱(@{user_mail})接收邮件信息\",\"inputList\":[{\"types\":\"EmailServerType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"mail_server\",\"title\":\"邮件服务器地址\",\"name\":\"mail_server\",\"tip\":\"选择一个邮箱类型\",\"options\":[{\"label\":\"其他邮箱\",\"value\":\"other\"},{\"label\":\"126\",\"value\":\"126\"},{\"label\":\"163\",\"value\":\"163\"},{\"label\":\"QQ\",\"value\":\"qq\"},{\"label\":\"讯飞邮箱\",\"value\":\"iflytek\"}],\"default\":\"qq\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"custom_mail_server\",\"title\":\"IMAP服务器地址\",\"name\":\"custom_mail_server\",\"tip\":\"输入指定的IMAP服务器地址\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"normal\",\"dynamics\":[{\"key\":\"$this.custom_mail_server.show\",\"expression\":\"return $this.mail_server.value == \'other\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"custom_mail_port\",\"title\":\"IMAP服务器端口\",\"name\":\"custom_mail_port\",\"tip\":\"输入指定的IMAP服务器端口\",\"default\":993,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"normal\",\"dynamics\":[{\"key\":\"$this.custom_mail_port.show\",\"expression\":\"return $this.mail_server.value == \'other\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"user_mail\",\"title\":\"用户邮件账号\",\"name\":\"user_mail\",\"tip\":\"IMAP服务器身份验证的用户名,通常是邮箱账号,以具体邮件服务商的规范为标准\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"normal\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"user_password\",\"title\":\"授权码\",\"name\":\"user_password\",\"tip\":\"IMAP服务器验证身份的授权码,一般需要短信认证开通,部分邮箱为账号密码,以具体邮件服务商的规范为标准\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"normal\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"max_return_num\",\"title\":\"邮件最大返回数量\",\"name\":\"max_return_num\",\"tip\":\"返回的最大邮件数量\",\"default\":5,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"CHECKBOX\"},\"key\":\"unseen_flag\",\"title\":\"仅未读邮件\",\"name\":\"unseen_flag\",\"tip\":\"仅获取未读邮件或全部邮件\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"normal\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"CHECKBOX\"},\"key\":\"save_attachment_flag\",\"title\":\"保存附件\",\"name\":\"save_attachment_flag\",\"tip\":\"是否下载邮件附件\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"normal\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"files\"}},\"key\":\"save_attachment_path\",\"title\":\"保存目录\",\"name\":\"save_attachment_path\",\"tip\":\"附件的保存目录\",\"default\":\"\",\"level\":\"normal\",\"dynamics\":[{\"key\":\"$this.save_attachment_path.show\",\"expression\":\"return $this.save_attachment_flag.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"folder_name\",\"title\":\"文件夹名称\",\"name\":\"folder_name\",\"tip\":\"输入要接收邮件的邮件箱名称,默认为INBOX\",\"default\":\"INBOX\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"CHECKBOX\"},\"key\":\"mask_as_read_flag\",\"title\":\"标记为已读\",\"name\":\"mask_as_read_flag\",\"tip\":\"获取邮件后,将邮件标记为已读状态\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sender_text\",\"title\":\"发件人中包含的内容\",\"name\":\"sender_text\",\"tip\":\"通过发送者包含关键字进行过滤\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"receiver_text\",\"title\":\"收件人中包含的内容\",\"name\":\"receiver_text\",\"tip\":\"通过接收者包含关键字进行过滤\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"theme_text\",\"title\":\"主题中包含的内容\",\"name\":\"theme_text\",\"tip\":\"通过主题包含关键字进行过滤\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"content_text\",\"title\":\"正文中包含的内容\",\"name\":\"content_text\",\"tip\":\"通过内容包含关键字进行过滤\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"mail_list\",\"title\":\"保存邮件列表\",\"tip\":\"指定一个变量名称,将返回的邮件列表存储至该变量\"}],\"icon\":\"receive-email\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(122,'os/encrypt','Encrypt.md5_encrypt','{\"key\":\"Encrypt.md5_encrypt\",\"title\":\"MD5加密\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().md5_encrypt\",\"comment\":\"对字符串(@{source_str})进行MD5加密\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_str\",\"title\":\"需要加密的字符串\",\"name\":\"source_str\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MD5bitsType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"md5_method\",\"title\":\"加密位数\",\"name\":\"md5_method\",\"tip\":\"\",\"options\":[{\"label\":\"32位\",\"value\":\"32\"},{\"label\":\"16位\",\"value\":\"16\"}],\"default\":\"32\",\"required\":true},{\"types\":\"EncryptCaseType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"case_method\",\"title\":\"大小写选择\",\"name\":\"case_method\",\"tip\":\"\",\"options\":[{\"label\":\"小写字母\",\"value\":\"lower\"},{\"label\":\"大写字母\",\"value\":\"upper\"}],\"default\":\"lower\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"md5_encrypted_result\",\"title\":\"MD5加密结果\",\"tip\":\"\"}],\"icon\":\"md5-encrypt\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(123,'os/encrypt','Encrypt.sha_encrypt','{\"key\":\"Encrypt.sha_encrypt\",\"title\":\"SHA加密\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().sha_encrypt\",\"comment\":\"对字符串(@{source_str})进行SHA加密\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_str\",\"title\":\"需要加密的字符串\",\"name\":\"source_str\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SHAType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"sha_method\",\"title\":\"SHA加密方法\",\"name\":\"sha_method\",\"tip\":\"\",\"options\":[{\"label\":\"sha1\",\"value\":\"sha1\"},{\"label\":\"sha224\",\"value\":\"sha224\"},{\"label\":\"sha256\",\"value\":\"sha256\"},{\"label\":\"sha384\",\"value\":\"sha384\"},{\"label\":\"sha512\",\"value\":\"sha512\"},{\"label\":\"sha3_224\",\"value\":\"sha3_224\"},{\"label\":\"sha3_256\",\"value\":\"sha3_256\"},{\"label\":\"sha3_384\",\"value\":\"sha3_384\"},{\"label\":\"sha3_512\",\"value\":\"sha3_512\"}],\"default\":\"sha1\",\"required\":true},{\"types\":\"EncryptCaseType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"case_method\",\"title\":\"大小写选择\",\"name\":\"case_method\",\"tip\":\"\",\"options\":[{\"label\":\"小写字母\",\"value\":\"lower\"},{\"label\":\"大写字母\",\"value\":\"upper\"}],\"default\":\"lower\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"sha_encrypted_result\",\"title\":\"SHA加密结果\",\"tip\":\"\"}],\"icon\":\"sha-encrypt\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(124,'os/encrypt','Encrypt.symmetric_encrypt','{\"key\":\"Encrypt.symmetric_encrypt\",\"title\":\"对称加密\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().symmetric_encrypt\",\"comment\":\"对字符串(@{source_str})进行对称加密\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_str\",\"title\":\"需要加密的字符串\",\"name\":\"source_str\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"加密密钥\",\"name\":\"password\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"symmetric_encrypted_result\",\"title\":\"对称加密结果\",\"tip\":\"\"}],\"icon\":\"symmetric-encrypt\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(125,'os/encrypt','Encrypt.symmetric_decrypt','{\"key\":\"Encrypt.symmetric_decrypt\",\"title\":\"对称解密\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().symmetric_decrypt\",\"comment\":\"对字符串(@{source_str})进行对称解密\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_str\",\"title\":\"需要解密的字符串\",\"name\":\"source_str\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"解密密钥\",\"name\":\"password\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"symmetric_decrypted_result\",\"title\":\"对称解密结果\",\"tip\":\"\"}],\"icon\":\"symmetric-decrypt\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(126,'os/encrypt','Encrypt.base64_encoding','{\"key\":\"Encrypt.base64_encoding\",\"title\":\"Base64编码\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().base64_encoding\",\"comment\":\"读取字符串或图片文件编码为Base64字符串,返回编码后的字符串(@{encoded_string})\",\"inputList\":[{\"types\":\"Base64CodeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"encode_type\",\"title\":\"编码类型\",\"name\":\"encode_type\",\"tip\":\"\",\"options\":[{\"label\":\"字符串\",\"value\":\"string\"},{\"label\":\"图片\",\"value\":\"picture\"}],\"default\":\"string\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"字符串数据\",\"name\":\"string_data\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.string_data.show\",\"expression\":\"return $this.encode_type.value == \'string\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.encode_type.value == \'picture\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"encoded_string\",\"title\":\"编码后的字符串\",\"tip\":\"\"}],\"icon\":\"base64-encode\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(127,'os/encrypt','Encrypt.base64_decoding','{\"key\":\"Encrypt.base64_decoding\",\"title\":\"Base64解码\",\"version\":\"1.0.0\",\"src\":\"astronverse.encrypt.encrypt.Encrypt().base64_decoding\",\"comment\":\"读取Base64字符串(@{string_data}),解码为字符串或图片文件\",\"inputList\":[{\"types\":\"Base64CodeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"decode_type\",\"title\":\"解码类型\",\"name\":\"decode_type\",\"tip\":\"\",\"options\":[{\"label\":\"字符串\",\"value\":\"string\"},{\"label\":\"图片\",\"value\":\"picture\"}],\"default\":\"string\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"string_data\",\"title\":\"Base64字符串\",\"name\":\"string_data\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"图片保存文件夹\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.decode_type.value == \'picture\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"图片文件名\",\"name\":\"file_name\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.decode_type.value == \'picture\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return $this.decode_type.value == \'picture\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"decoded_string\",\"title\":\"解码后的字符串/图片绝对路径\",\"tip\":\"\"}],\"icon\":\"base64-decode\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(128,'remote','Enterprise.upload_to_sharefolder','{\"key\":\"Enterprise.upload_to_sharefolder\",\"title\":\"上传文件至共享文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.enterprise.enterprise.Enterprise().upload_to_sharefolder\",\"comment\":\"上传 @{file_path} 的文件,返回上传结果 @{upload_result}\",\"inputList\":[{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"上传文件路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"upload_result\",\"title\":\"上传文件结果\",\"tip\":\"\"}],\"icon\":\"\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(129,'remote','Enterprise.download_from_sharefolder','{\"key\":\"Enterprise.download_from_sharefolder\",\"title\":\"从共享文件夹下载文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.enterprise.enterprise.Enterprise().download_from_sharefolder\",\"comment\":\"下载 @{file_path} 的文件到 @{save_folder},返回下载结果 @{download_result}\",\"inputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"REMOTEFOLDERS\"},\"key\":\"file_path\",\"title\":\"共享文件夹文件\",\"name\":\"file_path\",\"tip\":\"\",\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_folder\",\"title\":\"文件保存路径\",\"name\":\"save_folder\",\"tip\":\"\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"download_result\",\"title\":\"下载结果\",\"tip\":\"\"}],\"icon\":\"\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(130,'remote','Enterprise.get_shared_variable','{\"key\":\"Enterprise.get_shared_variable\",\"title\":\"获取共享变量\",\"version\":\"1.0.0\",\"src\":\"astronverse.enterprise.enterprise.Enterprise().get_shared_variable\",\"comment\":\"获取共享变量 @{shared_variable} 给变量 @{variable_data}\",\"inputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"REMOTEPARAMS\"},\"key\":\"shared_variable\",\"title\":\"共享变量名\",\"name\":\"shared_variable\",\"tip\":\"选择需要的共享变量\",\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"variable_data\",\"title\":\"保存共享变量至\",\"tip\":\"\"}],\"icon\":\"get-shared-variable\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(131,'document/document.Excel','Excel.open_excel','{\"key\":\"Excel.open_excel\",\"title\":\"打开Excel文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().open_excel\",\"comment\":\"打开路径为 @{file_path} 的Excel文件,返回Excel对象 @{open_excel_obj}\",\"inputList\":[{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[\".xlsx\",\".xls\"],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"输入文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"ApplicationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"default_application\",\"title\":\"默认创建程序\",\"name\":\"default_application\",\"tip\":\"选择默认创建程序,可选Excel或WPS\",\"options\":[{\"label\":\"Excel\",\"value\":\"Excel\"},{\"label\":\"WPS\",\"value\":\"WPS\"},{\"label\":\"系统自动选择\",\"value\":\"Default\"}],\"default\":\"Default\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"visible_flag\",\"title\":\"是否可视化\",\"name\":\"visible_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"密码\",\"name\":\"password\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"update_links\",\"title\":\"自动更新外部链接\",\"name\":\"update_links\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"open_excel_obj\",\"title\":\"打开的Excel对象\",\"tip\":\"\"}],\"icon\":\"excel-open-file\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(132,'document/document.Excel','Excel.get_excel','{\"key\":\"Excel.get_excel\",\"title\":\"获取已打开的Excel对象\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel\",\"comment\":\"获取文件名为 @{file_name} 的Excel对象,返回Excel对象 @{get_excel_obj}\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文件名\",\"name\":\"file_name\",\"tip\":\"输入文件名,不需要输入前序打开Excel的变量\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_excel_obj\",\"title\":\"获取的Excel对象\",\"tip\":\"\"}],\"icon\":\"get-open-excel-objects\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(133,'document/document.Excel','Excel.create_excel','{\"key\":\"Excel.create_excel\",\"title\":\"创建Excel文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().create_excel\",\"comment\":\"在路径为 @{file_path} 下创建文件名为 @{file_name} 的Excel文件,返回Excel对象 @{create_excel_obj}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"保存文件夹路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"保存文件名\",\"name\":\"file_name\",\"tip\":\"输入文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ApplicationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"default_application\",\"title\":\"默认创建程序\",\"name\":\"default_application\",\"tip\":\"选择默认创建程序,可选Excel或WPS\",\"options\":[{\"label\":\"Excel\",\"value\":\"Excel\"},{\"label\":\"WPS\",\"value\":\"WPS\"},{\"label\":\"系统自动选择\",\"value\":\"Default\"}],\"default\":\"Excel\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"visible_flag\",\"title\":\"是否可视化\",\"name\":\"visible_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"文件名存在处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择文件存在处理方式,可选覆盖、重命名、取消保存\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"文件打开密码\",\"name\":\"password\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"create_excel_obj\",\"title\":\"创建的Excel对象\",\"tip\":\"\"},{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"excel_path\",\"title\":\"创建的Excel文件路径\",\"tip\":\"\"}],\"icon\":\"excel-create-file\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(134,'document/document.Excel','Excel.save_excel','{\"key\":\"Excel.save_excel\",\"title\":\"保存Excel文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().save_excel\",\"comment\":\"保存Excel对象 @{excel} ,保存方式为 @{save_type}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"输入Excel对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"保存类型\",\"name\":\"save_type\",\"tip\":\"选择保存类型,可选保存或另存为\",\"options\":[{\"label\":\"保存\",\"value\":\"save\"},{\"label\":\"另存为\",\"value\":\"save_as\"},{\"label\":\"不保存\",\"value\":\"abort\"}],\"default\":\"save\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"输入文件路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文件名\",\"name\":\"file_name\",\"tip\":\"输入文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"文件名存在处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择文件存在处理方式,可选覆盖、重命名、取消保存\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"close_flag\",\"title\":\"是否关闭文件\",\"name\":\"close_flag\",\"tip\":\"选择保存后是否关闭文件,可选关闭或不关闭\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[],\"icon\":\"excel-save-file\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(135,'document/document.Excel','Excel.close_excel','{\"key\":\"Excel.close_excel\",\"title\":\"关闭Excel文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().close_excel\",\"comment\":\"关闭 @{close_range_flag},当前Excel对象 @{excel},保存类型为 @{save_type_one||save_type_all}\",\"inputList\":[{\"types\":\"CloseRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"close_range_flag\",\"title\":\"关闭文档范围\",\"name\":\"close_range_flag\",\"tip\":\"选择关闭文档的范围,如关闭当前文档或关闭所有文档\",\"options\":[{\"label\":\"当前文档\",\"value\":\"one\"},{\"label\":\"所有文档\",\"value\":\"all\"}],\"default\":\"one\",\"required\":true},{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"输入Excel对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.excel.show\",\"expression\":\"return $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type_one\",\"title\":\"保存类型\",\"name\":\"save_type_one\",\"tip\":\"选择保存类型,可选保存,另存为或不保存\",\"options\":[{\"label\":\"保存\",\"value\":\"save\"},{\"label\":\"另存为\",\"value\":\"save_as\"},{\"label\":\"不保存\",\"value\":\"abort\"}],\"default\":\"save\",\"dynamics\":[{\"key\":\"$this.save_type_one.show\",\"expression\":\"return $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"SaveTypeAll\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type_all\",\"title\":\"保存类型\",\"name\":\"save_type_all\",\"tip\":\"选择保存类型,可选保存或不保存\",\"options\":[{\"label\":\"save\",\"value\":\"save\"},{\"label\":\"abort\",\"value\":\"abort\"}],\"default\":\"save\",\"dynamics\":[{\"key\":\"$this.save_type_all.show\",\"expression\":\"return $this.close_range_flag.value == \'all\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"输入文件路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.save_type_one.value == \'save_as\' && $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文件名\",\"name\":\"file_name\",\"tip\":\"输入文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.save_type_one.value == \'save_as\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"文件名存在处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择文件存在处理方式,可选覆盖、重命名、取消保存\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"pkill_flag\",\"title\":\"是否关闭进程\",\"name\":\"pkill_flag\",\"tip\":\"选择是否关闭进程,可选关闭或不关闭\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.pkill_flag.show\",\"expression\":\"return $this.close_range_flag.value == \'all\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-close-file\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(136,'document/document.Excel','Excel.edit_excel','{\"key\":\"Excel.edit_excel\",\"title\":\"写入Excel文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().edit_excel\",\"comment\":\"编辑Excel对象 @{excel} ,写入内容 @{value}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"输入Excel对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"EditRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"edit_range\",\"title\":\"编辑范围\",\"name\":\"edit_range\",\"tip\":\"选择编辑范围,可选行、列、区域\",\"options\":[{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"单元格\",\"value\":\"cell\"}],\"default\":\"row\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_col\",\"title\":\"起始列\",\"name\":\"start_col\",\"tip\":\"输入要编辑的起始列位置,支持字母(如\'A\')或数字(如1)格式。\",\"default\":\"A\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_row\",\"title\":\"起始行\",\"name\":\"start_row\",\"tip\":\"输入要编辑的起始行号,从1开始计数。当选择列(COLUMN)时可不填,当选择行(ROW)或区域(AREA)时必填\",\"default\":\"1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"value\",\"title\":\"插入内容\",\"name\":\"value\",\"tip\":\"修改的内容以列表方式输入,当选择行或列时输入列表如[1,2,3],表示在指定行依次写入1,2,3;当选择区域时输入列表如[[1,1],[2,2]],表示在指定区域按行写入数据\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-write-file\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(137,'document/document.Excel','Excel.read_excel','{\"key\":\"Excel.read_excel\",\"title\":\"读取Excel文件内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().read_excel\",\"comment\":\"读取Excel对象 @{excel} 中工作表 @{sheet_name} 的Excel文件内容\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"输入Excel对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"read_range\",\"title\":\"读取范围\",\"name\":\"read_range\",\"tip\":\"选择读取范围,可选单元格、行、列、区域、已编辑区域\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_col\",\"title\":\"起始列\",\"name\":\"start_col\",\"tip\":\"输入起始列\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_col.show\",\"expression\":\"return $this.read_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_col\",\"title\":\"结束列\",\"name\":\"end_col\",\"tip\":\"输入结束列\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_col.show\",\"expression\":\"return $this.read_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell\",\"title\":\"单元格\",\"name\":\"cell\",\"tip\":\"输入待读取单元格,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell.show\",\"expression\":\"return $this.read_range.value == \'cell\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.read_range.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"column\",\"title\":\"列\",\"name\":\"column\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.column.show\",\"expression\":\"return $this.read_range.value == \'column\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_row\",\"title\":\"起始行\",\"name\":\"start_row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_row.show\",\"expression\":\"return $this.read_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_row\",\"title\":\"结束行\",\"name\":\"end_row\",\"tip\":\"输入整数代表行号,-n代表倒数第n行\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_row.show\",\"expression\":\"return $this.read_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"read_display\",\"title\":\"读取单元格显示内容\",\"name\":\"read_display\",\"tip\":\"选择是否读取单元格显示的内容,可选是或否\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"trim_spaces\",\"title\":\"去除空格\",\"name\":\"trim_spaces\",\"tip\":\"选择是否去除空格,可选是或否\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"replace_none\",\"title\":\"替换空值\",\"name\":\"replace_none\",\"tip\":\"选择是否将空值(None)替换为空字符串,可选是或否\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"read_excel_contents\",\"title\":\"读取的Excel内容\",\"tip\":\"读取的Excel内容以列表方式返回\"}],\"icon\":\"excel-read-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(138,'document/document.Excel','Excel.design_cell_type','{\"key\":\"Excel.design_cell_type\",\"title\":\"设置单元格格式\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().design_cell_type\",\"comment\":\"设置 @{excel} 中工作表 @{sheet_name} 的单元格格式,字体大小为 @{font_size},字体名称为 @{font_name},字体颜色为 @{font_color},背景颜色 @{bg_color}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名称\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"design_type\",\"title\":\"设置范围\",\"name\":\"design_type\",\"tip\":\"选择设置范围,可选单元格、行、列、区域\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.design_type.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"range_position\",\"title\":\"范围位置\",\"name\":\"range_position\",\"tip\":\"输入单元格范围,如A1:B2\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.range_position.show\",\"expression\":\"return $this.design_type.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.design_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.design_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col_width\",\"title\":\"列宽\",\"name\":\"col_width\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_COLOR\"},\"key\":\"bg_color\",\"title\":\"背景颜色\",\"name\":\"bg_color\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_COLOR\"},\"key\":\"font_color\",\"title\":\"字体颜色\",\"name\":\"font_color\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"FontType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"font_type\",\"title\":\"字体类型\",\"name\":\"font_type\",\"tip\":\"\",\"options\":[{\"label\":\"维持原状\",\"value\":\"no_change\"},{\"label\":\"粗体\",\"value\":\"bold\"},{\"label\":\"斜体\",\"value\":\"italic\"},{\"label\":\"粗斜体\",\"value\":\"bold_italic\"},{\"label\":\"常规\",\"value\":\"normal\"}],\"default\":\"no_change\",\"required\":true},{\"types\":\"FontNameType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"font_name\",\"title\":\"字体名称\",\"name\":\"font_name\",\"tip\":\"\",\"options\":[{\"label\":\"维持原状\",\"value\":\"维持原状\"},{\"label\":\"黑体\",\"value\":\"黑体\"},{\"label\":\"仿宋\",\"value\":\"仿宋\"},{\"label\":\"宋体\",\"value\":\"宋体\"},{\"label\":\"微软雅黑\",\"value\":\"微软雅黑\"},{\"label\":\"微软雅黑 Light\",\"value\":\"微软雅黑 Light\"},{\"label\":\"华文中宋\",\"value\":\"华文中宋\"},{\"label\":\"华文仿宋\",\"value\":\"华文仿宋\"},{\"label\":\"华文宋体\",\"value\":\"华文宋体\"},{\"label\":\"华文彩云\",\"value\":\"华文彩云\"},{\"label\":\"华文新魏\",\"value\":\"华文新魏\"},{\"label\":\"华文楷体\",\"value\":\"华文楷体\"},{\"label\":\"华文琥珀\",\"value\":\"华文琥珀\"},{\"label\":\"华文细黑\",\"value\":\"华文细黑\"},{\"label\":\"华文行楷\",\"value\":\"华文行楷\"},{\"label\":\"华文隶书\",\"value\":\"华文隶书\"},{\"label\":\"幼圆\",\"value\":\"幼圆\"},{\"label\":\"隶书\",\"value\":\"隶书\"},{\"label\":\"方正姚体\",\"value\":\"方正姚体\"},{\"label\":\"方正舒体\",\"value\":\"方正舒体\"},{\"label\":\"新宋体\",\"value\":\"新宋体\"},{\"label\":\"微軟正黑體 Light\",\"value\":\"微軟正黑體 Light\"},{\"label\":\"微軟正黑體\",\"value\":\"微軟正黑體\"},{\"label\":\"細明體_HKSCS-ExtB\",\"value\":\"細明體_HKSCS-ExtB\"},{\"label\":\"等线\",\"value\":\"等线\"},{\"label\":\"等线 Light\",\"value\":\"等线 Light\"},{\"label\":\"楷体\",\"value\":\"楷体\"},{\"label\":\"細明置-ExtB\",\"value\":\"細明置-ExtB\"},{\"label\":\"新細明置-ExtB\",\"value\":\"新細明置-ExtB\"},{\"label\":\"Onyx\",\"value\":\"Onyx\"},{\"label\":\"Myanmar Text\",\"value\":\"Myanmar Text\"},{\"label\":\"Niagara Engraved\",\"value\":\"Niagara Engraved\"},{\"label\":\"Niagara Solid\",\"value\":\"Niagara Solid\"},{\"label\":\"Nirmala Ul\",\"value\":\"Nirmala Ul\"},{\"label\":\"Nirmala Ul Semilight\",\"value\":\"Nirmala Ul Semilight\"},{\"label\":\"OCR A Extended\",\"value\":\"OCR A Extended\"},{\"label\":\"Old English Text MT\",\"value\":\"Old English Text MT\"},{\"label\":\"Palace Script MT\",\"value\":\"Palace Script MT\"},{\"label\":\"Poor Richard\",\"value\":\"Poor Richard\"},{\"label\":\"Papyrus\",\"value\":\"Papyrus\"},{\"label\":\"Parchment\",\"value\":\"Parchment\"},{\"label\":\"Perpetua\",\"value\":\"Perpetua\"},{\"label\":\"Perpetua Tilting MT\",\"value\":\"Perpetua Tilting MT\"},{\"label\":\"Playbill\",\"value\":\"Playbill\"},{\"label\":\"MV Boli\",\"value\":\"MV Boli\"},{\"label\":\"Pristina\",\"value\":\"Pristina\"},{\"label\":\"Rage Italic\",\"value\":\"Rage Italic\"},{\"label\":\"Ravie\",\"value\":\"Ravie\"},{\"label\":\"Palatino Linotype\",\"value\":\"Palatino Linotype\"},{\"label\":\"MT Extra\",\"value\":\"MT Extra\"},{\"label\":\"MS Gothic\",\"value\":\"MS Gothic\"},{\"label\":\"MS Reference Specialty\",\"value\":\"MS Reference Specialty\"},{\"label\":\"Marlett\",\"value\":\"Marlett\"},{\"label\":\"Matura MT Script Capitals\",\"value\":\"Matura MT Script Capitals\"},{\"label\":\"Microsoft Himalaya\",\"value\":\"Microsoft Himalaya\"},{\"label\":\"Microsoft JhengHei UI\",\"value\":\"Microsoft JhengHei UI\"},{\"label\":\"Microsoft JhengHei UI Light\",\"value\":\"Microsoft JhengHei UI Light\"},{\"label\":\"Microsoft New Tai Lue\",\"value\":\"Microsoft New Tai Lue\"},{\"label\":\"Microsoft PhagsPa\",\"value\":\"Microsoft PhagsPa\"},{\"label\":\"Microsoft Sans Serif\",\"value\":\"Microsoft Sans Serif\"},{\"label\":\"Microsoft Tai Le\",\"value\":\"Microsoft Tai Le\"},{\"label\":\"Microsoft Uighur\",\"value\":\"Microsoft Uighur\"},{\"label\":\"Microsoft Yahei Ul\",\"value\":\"Microsoft Yahei Ul\"},{\"label\":\"Microsoft YaHei Ul Light\",\"value\":\"Microsoft YaHei Ul Light\"},{\"label\":\"Microsoft Yi Baiti\",\"value\":\"Microsoft Yi Baiti\"},{\"label\":\"Mistral\",\"value\":\"Mistral\"},{\"label\":\"Modern No.20\",\"value\":\"Modern No.20\"},{\"label\":\"Mogolian Baiti\",\"value\":\"Mogolian Baiti\"}],\"default\":\"维持原状\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"font_size\",\"title\":\"字体大小\",\"name\":\"font_size\",\"tip\":\"\",\"default\":11,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"NumberFormatType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"numberformat\",\"title\":\"数字格式\",\"name\":\"numberformat\",\"tip\":\"\",\"options\":[{\"label\":\"维持原状\",\"value\":\"no_change\"},{\"label\":\"常规\",\"value\":\"G/通用格式\"},{\"label\":\"数字\",\"value\":\"0.00\"},{\"label\":\"货币\",\"value\":\"¥#,##0.00\"},{\"label\":\"_(¥* #,##0.00_);_(¥* (#,##0.00);_(¥* -_0_0_);_(@_)\",\"value\":\"_(¥* #,##0.00_);_(¥* (#,##0.00);_(¥* -_0_0_);_(@_)\"},{\"label\":\"短日期\",\"value\":\"yyyy/m/d\"},{\"label\":\"长日期\",\"value\":\"yyyy年mm月dd日\"},{\"label\":\"时间\",\"value\":\"h:mm:ss AM/PM\"},{\"label\":\"百分比\",\"value\":\"0.00%\"},{\"label\":\"分数\",\"value\":\"# ?/?\"},{\"label\":\"科学记数\",\"value\":\"0.00E+00\"},{\"label\":\"@\",\"value\":\"@\"},{\"label\":\"自定义\",\"value\":\"other\"}],\"default\":\"no_change\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"numberformat_other\",\"title\":\"自定义数字格式\",\"name\":\"numberformat_other\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.numberformat_other.show\",\"expression\":\"return $this.numberformat.value == \'other\'\"}],\"required\":false},{\"types\":\"HorizontalAlign\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"horizontal_align\",\"title\":\"水平对齐\",\"name\":\"horizontal_align\",\"tip\":\"\",\"options\":[{\"label\":\"维持原状\",\"value\":\"no_change\"},{\"label\":\"默认常规\",\"value\":\"default\"},{\"label\":\"左对齐\",\"value\":\"left-aligned\"},{\"label\":\"右对齐\",\"value\":\"right-aligned\"},{\"label\":\"居中对齐\",\"value\":\"center\"},{\"label\":\"填充\",\"value\":\"padding\"},{\"label\":\"两端对齐\",\"value\":\"aligned_both_sides\"},{\"label\":\"跨列居中\",\"value\":\"center_cross_column\"},{\"label\":\"分散对齐\",\"value\":\"distributed_align\"}],\"default\":\"no_change\",\"required\":true},{\"types\":\"VerticalAlign\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"vertical_align\",\"title\":\"垂直对齐\",\"name\":\"vertical_align\",\"tip\":\"\",\"options\":[{\"label\":\"维持原状\",\"value\":\"no_change\"},{\"label\":\"靠上\",\"value\":\"up\"},{\"label\":\"居中\",\"value\":\"middle\"},{\"label\":\"靠下\",\"value\":\"down\"},{\"label\":\"两端对齐\",\"value\":\"aligned_both_sides\"},{\"label\":\"分散对齐\",\"value\":\"distributed_align\"}],\"default\":\"no_change\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"wrap_text\",\"title\":\"自动换行\",\"name\":\"wrap_text\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"auto_row_height\",\"title\":\"自动行高\",\"name\":\"auto_row_height\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.auto_row_height.show\",\"expression\":\"return $this.design_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"auto_column_width\",\"title\":\"自动列宽\",\"name\":\"auto_column_width\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.auto_column_width.show\",\"expression\":\"return $this.design_type.value == \'column\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-set-cell-format\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(139,'document/document.Excel','Excel.copy_excel','{\"key\":\"Excel.copy_excel\",\"title\":\"复制Excel单元格\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().copy_excel\",\"comment\":\"复制Excel对象 @{excel} 中工作表 @{sheet_name} 的单元格,返回复制内容的字符串格式 @{copy_excel_contents}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"copy_range_type\",\"title\":\"复制范围\",\"name\":\"copy_range_type\",\"tip\":\"\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"填写单元格位置,如A1\",\"default\":\"A1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.copy_range_type.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.copy_range_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.copy_range_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"range_position\",\"title\":\"单元格范围\",\"name\":\"range_position\",\"tip\":\"填写单元格范围,如A1:B2\",\"default\":\"A1:B5\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.range_position.show\",\"expression\":\"return $this.copy_range_type.value == \'area\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"copy_excel_contents\",\"title\":\"Excel复制内容\",\"tip\":\"\"}],\"icon\":\"excel-copy-cell\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(140,'document/document.Excel','Excel.paste_excel','{\"key\":\"Excel.paste_excel\",\"title\":\"粘贴Excel单元格\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().paste_excel\",\"comment\":\"向Excel对象 @{excel} 中工作表 @{sheet_name} 粘贴单元格\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"PasteType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"paste_type\",\"title\":\"粘贴类型\",\"name\":\"paste_type\",\"tip\":\"\",\"options\":[{\"label\":\"默认全部粘贴\",\"value\":\"all\"},{\"label\":\"值和数字格式\",\"value\":\"value_and_format\"},{\"label\":\"仅格式\",\"value\":\"format\"},{\"label\":\"边框除外\",\"value\":\"exclude_frame\"},{\"label\":\"仅列宽\",\"value\":\"col_width_only\"},{\"label\":\"仅公式\",\"value\":\"formula_only\"},{\"label\":\"公式和数字格式\",\"value\":\"formula_and_format\"},{\"label\":\"粘贴值\",\"value\":\"paste_value\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_location\",\"title\":\"起始位置\",\"name\":\"start_location\",\"tip\":\"输入起始单元格位置,如A1\",\"default\":\"A1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"skip_blanks\",\"title\":\"是否跳过空白行\",\"name\":\"skip_blanks\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"transpose\",\"title\":\"是否转置\",\"name\":\"transpose\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[],\"icon\":\"excel-paste-cell\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(141,'document/document.Excel','Excel.delete_excel_cell','{\"key\":\"Excel.delete_excel_cell\",\"title\":\"删除Excel单元格\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().delete_excel_cell\",\"comment\":\"删除Excel对象 @{excel} 中工作表 @{sheet_name} 的单元格\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"delete_range_excel\",\"title\":\"删除范围\",\"name\":\"delete_range_excel\",\"tip\":\"\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"coordinate\",\"title\":\"单元格位置\",\"name\":\"coordinate\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.coordinate.show\",\"expression\":\"return $this.delete_range_excel.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.delete_range_excel.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.delete_range_excel.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"data_region\",\"title\":\"单元格范围\",\"name\":\"data_region\",\"tip\":\"输入单元格范围,如A1:B2\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.data_region.show\",\"expression\":\"return $this.delete_range_excel.value == \'area\'\"}],\"required\":true},{\"types\":\"DeleteCellDirection\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"direction\",\"title\":\"剩余数据填充方向\",\"name\":\"direction\",\"tip\":\"\",\"options\":[{\"label\":\"下方单元格上移\",\"value\":\"lower_move_up\"},{\"label\":\"右侧单元格左移\",\"value\":\"right_move_left\"}],\"default\":\"lower_move_up\",\"dynamics\":[{\"key\":\"$this.direction.show\",\"expression\":\"return [\'cell\', \'area\'].includes($this.delete_range_excel.value)\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-delete-cell\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(142,'document/document.Excel','Excel.clear_excel_content','{\"key\":\"Excel.clear_excel_content\",\"title\":\"清除Excel区域内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().clear_excel_content\",\"comment\":\"清除Excel对象 @{excel} 中工作表 @{sheet_name} 的单元格内容\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"select_type\",\"title\":\"区域选择\",\"name\":\"select_type\",\"tip\":\"选择需要删除的区域内容\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_location\",\"title\":\"单元格位置\",\"name\":\"cell_location\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_location.show\",\"expression\":\"return $this.select_type.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.select_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.select_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"data_range\",\"title\":\"单元格范围\",\"name\":\"data_range\",\"tip\":\"输入连续的单元格范围,如A1:B2\",\"default\":\"A1:B5\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.data_range.show\",\"expression\":\"return $this.select_type.value == \'area\'\"}],\"required\":true},{\"types\":\"ClearType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"clear_type\",\"title\":\"清除类型\",\"name\":\"clear_type\",\"tip\":\"\",\"options\":[{\"label\":\"清除内容\",\"value\":\"content\"},{\"label\":\"清除格式\",\"value\":\"style\"},{\"label\":\"清除内容和格式\",\"value\":\"all\"}],\"default\":\"content\",\"required\":true}],\"outputList\":[],\"icon\":\"excel-clear-range\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(143,'document/document.Excel','Excel.insert_excel_row_or_column','{\"key\":\"Excel.insert_excel_row_or_column\",\"title\":\"插入Excel行或列\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().insert_excel_row_or_column\",\"comment\":\"向Excel对象 @{excel} 中工作表 @{sheet_name} 插入行或列\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"EnhancedInsertType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"insert_type\",\"title\":\"插入类型\",\"name\":\"insert_type\",\"tip\":\"\",\"options\":[{\"label\":\"指定行号插入\",\"value\":\"row\"},{\"label\":\"指定列号插入\",\"value\":\"column\"},{\"label\":\"在最后一行后插入\",\"value\":\"add_rows\"},{\"label\":\"在最后一列后插入\",\"value\":\"add_columns\"}],\"default\":\"row\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.insert_type.value == \'row\'\"}],\"required\":true},{\"types\":\"RowDirectionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"row_direction\",\"title\":\"插入行方向\",\"name\":\"row_direction\",\"tip\":\"\",\"options\":[{\"label\":\"向上插入\",\"value\":\"upper\"},{\"label\":\"向下插入\",\"value\":\"lower\"}],\"default\":\"lower\",\"dynamics\":[{\"key\":\"$this.row_direction.show\",\"expression\":\"return $this.insert_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.insert_type.value == \'column\'\"}],\"required\":true},{\"types\":\"ColumnDirectionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"col_direction\",\"title\":\"插入列方向\",\"name\":\"col_direction\",\"tip\":\"\",\"options\":[{\"label\":\"向左插入\",\"value\":\"left\"},{\"label\":\"向右插入\",\"value\":\"right\"}],\"default\":\"right\",\"dynamics\":[{\"key\":\"$this.col_direction.show\",\"expression\":\"return $this.insert_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"blank_rows\",\"title\":\"是否只插入空行/空列\",\"name\":\"blank_rows\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"insert_num\",\"title\":\"插入行数\",\"name\":\"insert_num\",\"tip\":\"\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.insert_num.show\",\"expression\":\"return $this.blank_rows.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"insert_content\",\"title\":\"插入内容\",\"name\":\"insert_content\",\"tip\":\"插入单行或多行时,插入内容需为多维列表,字符需使用单引号\'\',例:插入1行,写入内容:[[123,24,32]],插入2行,写入内容:[[123,123],[\'aaa\']],则实际写入excel内容为:第一行:123 123;第二行:aaa;写入内容:[[123,123],\'aaa\'],则实际写入excel内容为:第一行:123 123;第二行:a a a;如果插入的行或列超过数据长度,多余的单元格将保持为空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.insert_content.show\",\"expression\":\"return $this.blank_rows.value == false\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-insert-row-column\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(144,'document/document.Excel','Excel.get_excel_row_num','{\"key\":\"Excel.get_excel_row_num\",\"title\":\"获取Excel行数\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel_row_num\",\"comment\":\"获取Excel对象 @{excel} 中工作表 @{sheet_name} 的行数,返回行数 @{excel_row_num}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ColumnType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"get_col_type\",\"title\":\"获取类型\",\"name\":\"get_col_type\",\"tip\":\"\",\"options\":[{\"label\":\"所有列\",\"value\":\"all\"},{\"label\":\"单列\",\"value\":\"one_column\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.get_col_type.value == \'one_column\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"excel_row_num\",\"title\":\"Excel行数\",\"tip\":\"\"}],\"icon\":\"excel-get-row-count\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(145,'document/document.Excel','Excel.get_excel_col_num','{\"key\":\"Excel.get_excel_col_num\",\"title\":\"获取Excel列数\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel_col_num\",\"comment\":\"获取Excel对象 @{excel} 中工作表 @{sheet_name} 的列数,返回列数 @{excel_col_num}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"RowType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"get_row_type\",\"title\":\"获取类型\",\"name\":\"get_row_type\",\"tip\":\"\",\"options\":[{\"label\":\"所有行\",\"value\":\"all\"},{\"label\":\"单行\",\"value\":\"one_row\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.get_row_type.value == \'one_row\'\"}],\"required\":true},{\"types\":\"ColumnOutputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出格式\",\"name\":\"output_type\",\"tip\":\"\",\"options\":[{\"label\":\"字母列号\",\"value\":\"letter\"},{\"label\":\"数字列号\",\"value\":\"number\"}],\"default\":\"number\",\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"excel_col_num\",\"title\":\"Excel列数\",\"tip\":\"\"}],\"icon\":\"excel-get-column-count\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(146,'document/document.Excel','Excel.get_excel_first_available_row','{\"key\":\"Excel.get_excel_first_available_row\",\"title\":\"获取Excel第一个可用行\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel_first_available_row\",\"comment\":\"获取Excel对象 @{excel} 中工作表 @{sheet_name} 的第一个可用行,返回可用行 @{get_first_available_row}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_first_available_row\",\"title\":\"第一个可用行\",\"tip\":\"\"}],\"icon\":\"excel-get-first-available-row\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(147,'document/document.Excel','Excel.get_excel_first_available_col','{\"key\":\"Excel.get_excel_first_available_col\",\"title\":\"获取Excel第一个可用列\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel_first_available_col\",\"comment\":\"获取Excel对象 @{excel} 中工作表 @{sheet_name} 的第一个可用列,返回可用列 @{get_first_available_col}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ColumnOutputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出格式\",\"name\":\"output_type\",\"tip\":\"\",\"options\":[{\"label\":\"字母列号\",\"value\":\"letter\"},{\"label\":\"数字列号\",\"value\":\"number\"}],\"default\":\"letter\",\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_first_available_col\",\"title\":\"第一个可用列\",\"tip\":\"\"}],\"icon\":\"excel-get-first-available-column\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(148,'document/document.Excel','Excel.loop_excel_content','{\"key\":\"Excel.loop_excel_content\",\"title\":\"循环Excel内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().loop_excel_content\",\"comment\":\"循环Excel对象 @{excel} 中指定 @{select_type} 的内容,输出循环项位置至@{key} 输出循环项至@{value}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SearchRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"select_type\",\"title\":\"循环范围\",\"name\":\"select_type\",\"tip\":\"\",\"options\":[{\"label\":\"已编辑区域\",\"value\":\"all\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"}],\"default\":\"row\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_row\",\"title\":\"起始行\",\"name\":\"start_row\",\"tip\":\"输入起始行编号,从1开始\",\"default\":\"1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_row.show\",\"expression\":\"return [\'row\', \'area\'].includes($this.select_type.value)\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_row\",\"title\":\"结束行\",\"name\":\"end_row\",\"tip\":\"输入结束行编号,-n代表倒数第n行\",\"default\":\"-1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_row.show\",\"expression\":\"return [\'row\', \'area\'].includes($this.select_type.value)\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_col\",\"title\":\"起始列\",\"name\":\"start_col\",\"tip\":\"输入起始列编号,如A或1\",\"default\":\"A\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_col.show\",\"expression\":\"return [\'column\', \'area\'].includes($this.select_type.value)\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_col\",\"title\":\"结束列\",\"name\":\"end_col\",\"tip\":\"输入结束列编号,-n代表倒数第n列\",\"default\":\"-1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_col.show\",\"expression\":\"return [\'column\', \'area\'].includes($this.select_type.value)\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"real_text\",\"title\":\"是否获取可见值\",\"name\":\"real_text\",\"tip\":\"Excel的可见值为打开所见到的值,真实值可能是隐藏的公式等,选择否获取真实值\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"cell_strip\",\"title\":\"是否去除空格\",\"name\":\"cell_strip\",\"tip\":\"是否去除前后空格以及换行符\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"key\",\"title\":\"循环项位置\",\"tip\":\"例如 A, B\"},{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"value\",\"title\":\"循环项\",\"tip\":\"例如 [1.0, None]\"}],\"icon\":\"excel-loop-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(149,'document/document.Excel','Excel.excel_get_cell_color','{\"key\":\"Excel.excel_get_cell_color\",\"title\":\"获取Excel单元格颜色\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().excel_get_cell_color\",\"comment\":\"获取Excel对象 @{excel} 中工作表 @{sheet_name} 的单元格 @{coordinate} 的颜色,返回颜色 @{get_cell_color}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"coordinate\",\"title\":\"单元格位置\",\"name\":\"coordinate\",\"tip\":\"输入单元格位置,如A1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_cell_color\",\"title\":\"单元格颜色\",\"tip\":\"\"}],\"icon\":\"excel-get-cell-color\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(150,'document/document.Excel','Excel.merge_split_excel_cell','{\"key\":\"Excel.merge_split_excel_cell\",\"title\":\"合并或拆分Excel单元格\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().merge_split_excel_cell\",\"comment\":\"合并或拆分Excel对象 @{excel} 中工作表 @{sheet_name} 的单元格\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"MergeOrSplitType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"job_type\",\"title\":\"合并或拆分类型\",\"name\":\"job_type\",\"tip\":\"\",\"options\":[{\"label\":\"合并\",\"value\":\"merge\"},{\"label\":\"拆分\",\"value\":\"split\"}],\"default\":\"merge\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"merge_cell_range\",\"title\":\"合并单元格范围\",\"name\":\"merge_cell_range\",\"tip\":\"输入需合并的连续单元格范围,如A1:B2\",\"default\":\"A1:B2\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.merge_cell_range.show\",\"expression\":\"return $this.job_type.value == \'merge\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"split_cell_range\",\"title\":\"拆分单元格范围\",\"name\":\"split_cell_range\",\"tip\":\"输入需拆分的连续单元格范围,如A1:B2\",\"default\":\"A1:B2\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.split_cell_range.show\",\"expression\":\"return $this.job_type.value == \'split\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-split-cell\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(151,'document/document.Excel','Excel.add_excel_worksheet','{\"key\":\"Excel.add_excel_worksheet\",\"title\":\"添加Excel工作表\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().add_excel_worksheet\",\"comment\":\"向Excel对象 @{excel} 中添加工作表 @{sheet_name}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SheetInsertType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"insert_type\",\"title\":\"插入位置\",\"name\":\"insert_type\",\"tip\":\"\",\"options\":[{\"label\":\"新表成为第一个工作表\",\"value\":\"first\"},{\"label\":\"新表成为最后一个工作表\",\"value\":\"last\"},{\"label\":\"新表插入到...表之前\",\"value\":\"before\"},{\"label\":\"新表插入到...表之后\",\"value\":\"after\"}],\"default\":\"first\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"relative_sheet_name\",\"title\":\"在...表之前/之后插入\",\"name\":\"relative_sheet_name\",\"tip\":\"输入相对位置相关的工作表名称,如\'Sheet1\'\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.relative_sheet_name.show\",\"expression\":\"return $this.insert_type.value == \'before\' || $this.insert_type.value == \'after\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"add-excel-worksheet\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(152,'document/document.Excel','Excel.move_excel_worksheet','{\"key\":\"Excel.move_excel_worksheet\",\"title\":\"移动Excel工作表\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().move_excel_worksheet\",\"comment\":\"移动Excel对象 @{excel} 中工作表 @{move_sheet} ,移动方式为 @{move_type}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MoveSheetType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"move_type\",\"title\":\"移动方式\",\"name\":\"move_type\",\"tip\":\"\",\"options\":[{\"label\":\"移动到目标工作表之后\",\"value\":\"move_after\"},{\"label\":\"移动到目标工作表之前\",\"value\":\"move_before\"},{\"label\":\"移动到第一个工作表\",\"value\":\"move_to_first\"},{\"label\":\"移动到最后一个工作表\",\"value\":\"move_to_last\"}],\"default\":\"move_after\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"move_sheet\",\"title\":\"要移动的工作表\",\"name\":\"move_sheet\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"move_to_sheet\",\"title\":\"目标工作表\",\"name\":\"move_to_sheet\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.move_to_sheet.show\",\"expression\":\"return [\'move_after\', \'move_before\'].includes($this.move_type.value)\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-move-sheet\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(153,'document/document.Excel','Excel.delete_excel_worksheet','{\"key\":\"Excel.delete_excel_worksheet\",\"title\":\"删除Excel工作表\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().delete_excel_worksheet\",\"comment\":\"删除Excel对象 @{excel} 中工作表 @{del_sheet_name}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"del_sheet_name\",\"title\":\"删除工作表\",\"name\":\"del_sheet_name\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-delete-sheet\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(154,'document/document.Excel','Excel.rename_excel_worksheet','{\"key\":\"Excel.rename_excel_worksheet\",\"title\":\"重命名Excel工作表\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().rename_excel_worksheet\",\"comment\":\"重命名Excel对象 @{excel} 中工作表 @{source_sheet_name} 为 @{new_sheet_name}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_sheet_name\",\"title\":\"原工作表\",\"name\":\"source_sheet_name\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_sheet_name\",\"title\":\"新工作表名\",\"name\":\"new_sheet_name\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-rename-sheet\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(155,'document/document.Excel','Excel.copy_excel_worksheet','{\"key\":\"Excel.copy_excel_worksheet\",\"title\":\"复制Excel工作表\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().copy_excel_worksheet\",\"comment\":\"复制Excel对象 @{excel} 中工作表 @{source_sheet_name} ,复制类型为 @{copy_type}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"source_sheet_name\",\"title\":\"原工作表\",\"name\":\"source_sheet_name\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_sheet_name\",\"title\":\"新工作表名\",\"name\":\"new_sheet_name\",\"tip\":\"工作表可以填写名称或序号,如\'Sheet1\'或\'1\',序号从1开始\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CopySheetLocationType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"location\",\"title\":\"复制位置\",\"name\":\"location\",\"tip\":\"\",\"options\":[{\"label\":\"复制到当前工作表之前\",\"value\":\"before\"},{\"label\":\"复制到当前工作表之后\",\"value\":\"after\"},{\"label\":\"复制到第一个工作表\",\"value\":\"first\"},{\"label\":\"复制到最后一个工作表\",\"value\":\"last\"}],\"default\":\"last\",\"required\":true},{\"types\":\"CopySheetType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"copy_type\",\"title\":\"复制类型\",\"name\":\"copy_type\",\"tip\":\"\",\"options\":[{\"label\":\"当前工作簿\",\"value\":\"current_workbook\"},{\"label\":\"其他工作簿\",\"value\":\"other_workbook\"}],\"default\":\"current_workbook\",\"required\":true},{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"other_excel_obj\",\"title\":\"其他Excel对象\",\"name\":\"other_excel_obj\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.other_excel_obj.show\",\"expression\":\"return $this.copy_type.value == \'other_workbook\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_cover\",\"title\":\"是否覆盖\",\"name\":\"is_cover\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[],\"icon\":\"excel-copy-sheet\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(156,'document/document.Excel','Excel.get_excel_worksheet_names','{\"key\":\"Excel.get_excel_worksheet_names\",\"title\":\"获取Excel工作表名称\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().get_excel_worksheet_names\",\"comment\":\"获取Excel对象 @{excel} 中工作表名称,返回工作表名称列表 @{sheet_names}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SheetRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sheet_range\",\"title\":\"工作表范围\",\"name\":\"sheet_range\",\"tip\":\"\",\"options\":[{\"label\":\"当前激活工作表\",\"value\":\"activated\"},{\"label\":\"所有工作表\",\"value\":\"all\"}],\"default\":\"activated\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"sheet_names\",\"title\":\"工作表名称\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\"}],\"icon\":\"excel-get-sheet-names\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(157,'document/document.Excel','Excel.search_and_replace_excel_content','{\"key\":\"Excel.search_and_replace_excel_content\",\"title\":\"查找或替换Excel内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().search_and_replace_excel_content\",\"comment\":\"查找或替换Excel对象 @{excel} 中 @{search_range} 范围内 @{find_str} ,返回查找结果 @{search_excel_result}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"find_str\",\"title\":\"查找内容\",\"name\":\"find_str\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"replace_flag\",\"title\":\"是否替换\",\"name\":\"replace_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"replace_str\",\"title\":\"替换内容\",\"name\":\"replace_str\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.replace_str.show\",\"expression\":\"return $this.replace_flag.value == true\"}],\"required\":true},{\"types\":\"SearchSheetType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"lookup_range_excel\",\"title\":\"查找范围\",\"name\":\"lookup_range_excel\",\"tip\":\"\",\"options\":[{\"label\":\"全部工作表\",\"value\":\"all\"},{\"label\":\"单个工作表\",\"value\":\"one\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.sheet_name.show\",\"expression\":\"return $this.lookup_range_excel.value == \'one\'\"}],\"required\":false},{\"types\":\"SearchRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"search_range\",\"title\":\"工作表内查找范围\",\"name\":\"search_range\",\"tip\":\"\",\"options\":[{\"label\":\"已编辑区域\",\"value\":\"all\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.search_range.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.search_range.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_row\",\"title\":\"起始行\",\"name\":\"start_row\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_row.show\",\"expression\":\"return $this.search_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_row\",\"title\":\"结束行\",\"name\":\"end_row\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_row.show\",\"expression\":\"return $this.search_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_col\",\"title\":\"起始列\",\"name\":\"start_col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_col.show\",\"expression\":\"return $this.search_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_col\",\"title\":\"结束列\",\"name\":\"end_col\",\"tip\":\"输入列名,支持输入字符A或者整数1,-n代表倒数第n列\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_col.show\",\"expression\":\"return $this.search_range.value == \'area\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"exact_match\",\"title\":\"是否精确匹配\",\"name\":\"exact_match\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"case_flag\",\"title\":\"是否区分大小写\",\"name\":\"case_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"MatchCountType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"match_range\",\"title\":\"匹配数量\",\"name\":\"match_range\",\"tip\":\"\",\"options\":[{\"label\":\"所有结果\",\"value\":\"all\"},{\"label\":\"第一个结果\",\"value\":\"first\"}],\"default\":\"all\",\"required\":true},{\"types\":\"SearchResultType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出类型\",\"name\":\"output_type\",\"tip\":\"默认返回单元格位置,比如[\'A1\', \'B2\'],也可以选择分开返回行列号,比如[[\'A\', 1], [\'B\', 2]]\",\"options\":[{\"label\":\"返回单元格位置\",\"value\":\"cell\"},{\"label\":\"返回列号和行号\",\"value\":\"col_and_row\"}],\"default\":\"cell\",\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"search_excel_result\",\"title\":\"查找结果\",\"tip\":\"选择单工作表查找是返回单元格位置列表,比如[\'A1\', \'B2\'],选择多工作表查找是返回工作表名称和单元格位置列表字典,比如{\'Sheet1\': [\'A1\', \'B2\'], \'Sheet2\': [\'C3\', \'D4\']}\"}],\"icon\":\"excel-find-replace\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(158,'document/document.Excel','Excel.insert_pic','{\"key\":\"Excel.insert_pic\",\"title\":\"插入Excel图片\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().insert_pic\",\"comment\":\"向Excel对象 @{excel} 中工作表 @{sheet_name} 插入图片 @{pic_path} ,插入类型为 @{pic_size_type}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"insert_pos\",\"title\":\"插入位置\",\"name\":\"insert_pos\",\"tip\":\"可填写单元格位置,如\'A1\';也可填写范围位置,如\'A1:B2\'\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"pic_path\",\"title\":\"图片路径\",\"name\":\"pic_path\",\"tip\":\"\",\"required\":true},{\"types\":\"ImageSizeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"pic_size_type\",\"title\":\"图片大小控制\",\"name\":\"pic_size_type\",\"tip\":\"\",\"options\":[{\"label\":\"调整缩放比例\",\"value\":\"scale\"},{\"label\":\"调整高度和宽度数值\",\"value\":\"number\"},{\"label\":\"自动调整大小匹配范围\",\"value\":\"auto\"}],\"default\":\"auto\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pic_height\",\"title\":\"图片高度\",\"name\":\"pic_height\",\"tip\":\"\",\"default\":300,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.pic_height.show\",\"expression\":\"return $this.pic_size_type.value == \'number\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pic_width\",\"title\":\"图片宽度\",\"name\":\"pic_width\",\"tip\":\"\",\"default\":400,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.pic_width.show\",\"expression\":\"return $this.pic_size_type.value == \'number\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pic_scale\",\"title\":\"图片缩放比例\",\"name\":\"pic_scale\",\"tip\":\"\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.pic_scale.show\",\"expression\":\"return $this.pic_size_type.value == \'scale\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-insert-image\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(159,'document/document.Excel','Excel.insert_formula','{\"key\":\"Excel.insert_formula\",\"title\":\"插入Excel公式\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().insert_formula\",\"comment\":\"向Excel对象 @{excel} 中工作表 @{sheet_name} 插入公式 @{formula}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"InsertFormulaDirectionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"insert_direction\",\"title\":\"公式插入方向\",\"name\":\"insert_direction\",\"tip\":\"\",\"options\":[{\"label\":\"向下插入\",\"value\":\"down\"},{\"label\":\"向右插入\",\"value\":\"right\"}],\"default\":\"down\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.insert_direction.value == \'down\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_row\",\"title\":\"起始行\",\"name\":\"start_row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_row.show\",\"expression\":\"return $this.insert_direction.value == \'down\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_row\",\"title\":\"结束行\",\"name\":\"end_row\",\"tip\":\"输入整数代表行号,-n代表倒数第n行\",\"default\":\"-1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_row.show\",\"expression\":\"return $this.insert_direction.value == \'down\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.insert_direction.value == \'right\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start_col\",\"title\":\"起始列\",\"name\":\"start_col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"A\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start_col.show\",\"expression\":\"return $this.insert_direction.value == \'right\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end_col\",\"title\":\"结束列\",\"name\":\"end_col\",\"tip\":\"输入列名,支持输入字符A或者整数1,-n代表倒数第n列\",\"default\":\"-1\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end_col.show\",\"expression\":\"return $this.insert_direction.value == \'right\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"formula\",\"title\":\"插入公式\",\"name\":\"formula\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-insert-formula\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(160,'document/document.Excel','Excel.create_excel_comment','{\"key\":\"Excel.create_excel_comment\",\"title\":\"创建Excel批注\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().create_excel_comment\",\"comment\":\"向Excel对象 @{excel} 中插入批注 @{comment}\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CreateCommentType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"comment_type\",\"title\":\"批注插入方式\",\"name\":\"comment_type\",\"tip\":\"可以指定单元格插入,也可以搜索内容插入\",\"options\":[{\"label\":\"按照单元格位置插入\",\"value\":\"position\"},{\"label\":\"按照内容搜索插入\",\"value\":\"content\"}],\"default\":\"position\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment\",\"title\":\"批注内容\",\"name\":\"comment\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.sheet_name.show\",\"expression\":\"return $this.comment_range.value == \'one\' || $this.comment_type.value == \'position\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"SearchSheetType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"comment_range\",\"title\":\"搜索范围\",\"name\":\"comment_range\",\"tip\":\"\",\"options\":[{\"label\":\"全部工作表\",\"value\":\"all\"},{\"label\":\"单个工作表\",\"value\":\"one\"}],\"default\":\"one\",\"dynamics\":[{\"key\":\"$this.comment_range.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"find_str\",\"title\":\"搜索内容\",\"name\":\"find_str\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.find_str.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"comment_all\",\"title\":\"是否批注所有匹配内容\",\"name\":\"comment_all\",\"tip\":\"选择是将会对所有匹配内容进行批注,选择否只会对第一个匹配内容进行批注\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.comment_all.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-create-comment\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(161,'document/document.Excel','Excel.delete_excel_comment','{\"key\":\"Excel.delete_excel_comment\",\"title\":\"删除Excel批注\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().delete_excel_comment\",\"comment\":\"删除Excel对象 @{excel} 中批注\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel\",\"title\":\"Excel对象\",\"name\":\"excel\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"delete_all\",\"title\":\"是否删除所有批注\",\"name\":\"delete_all\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.delete_all.value == false\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-delete-comment\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(162,'document/document.Excel','Excel.excel_text_to_number','{\"key\":\"Excel.excel_text_to_number\",\"title\":\"Excel区域文本转数字\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().excel_text_to_number\",\"comment\":\"将Excel对象 @{excel_obj} 中工作表 @{sheet_name} 中的区域文本转化为数字格式\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_obj\",\"title\":\"Excel对象\",\"name\":\"excel_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"select_type\",\"title\":\"区域选择\",\"name\":\"select_type\",\"tip\":\"\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.select_type.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.select_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.select_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"range_location\",\"title\":\"单元格范围\",\"name\":\"range_location\",\"tip\":\"输入单元格范围,如A1:B2\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.range_location.show\",\"expression\":\"return $this.select_type.value == \'area\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-text-to-number\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(163,'document/document.Excel','Excel.excel_number_to_text','{\"key\":\"Excel.excel_number_to_text\",\"title\":\"Excel区域数字转文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().excel_number_to_text\",\"comment\":\"将Excel对象 @{excel_obj} 中工作表 @{sheet_name} 中的区域数字转化为文本格式\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_obj\",\"title\":\"Excel对象\",\"name\":\"excel_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"ReadRangeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"select_type\",\"title\":\"区域选择\",\"name\":\"select_type\",\"tip\":\"\",\"options\":[{\"label\":\"单元格\",\"value\":\"cell\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"列\",\"value\":\"column\"},{\"label\":\"区域\",\"value\":\"area\"},{\"label\":\"已编辑区域\",\"value\":\"all\"}],\"default\":\"cell\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cell_position\",\"title\":\"单元格位置\",\"name\":\"cell_position\",\"tip\":\"输入单元格位置,如A1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cell_position.show\",\"expression\":\"return $this.select_type.value == \'cell\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.row.show\",\"expression\":\"return $this.select_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.col.show\",\"expression\":\"return $this.select_type.value == \'column\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"range_location\",\"title\":\"单元格范围\",\"name\":\"range_location\",\"tip\":\"输入单元格范围,如A1:B2\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.range_location.show\",\"expression\":\"return $this.select_type.value == \'area\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-number-to-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(164,'document/document.Excel','Excel.excel_set_col_width','{\"key\":\"Excel.excel_set_col_width\",\"title\":\"设置Excel列宽\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().excel_set_col_width\",\"comment\":\"设置Excel对象 @{excel_obj} 工作表 @{sheet_name} 中 @{col} 的列宽\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_obj\",\"title\":\"Excel对象\",\"name\":\"excel_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SetType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"set_type\",\"title\":\"设置类型\",\"name\":\"set_type\",\"tip\":\"\",\"options\":[{\"label\":\"设置值\",\"value\":\"value\"},{\"label\":\"自动调整\",\"value\":\"auto\"}],\"default\":\"auto\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"col\",\"title\":\"列\",\"name\":\"col\",\"tip\":\"输入列名,支持输入字符A或者整数1\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"width\",\"title\":\"列宽\",\"name\":\"width\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.width.show\",\"expression\":\"return $this.set_type.value == \'value\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-set-column-width\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(165,'document/document.Excel','Excel.excel_set_row_height','{\"key\":\"Excel.excel_set_row_height\",\"title\":\"设置Excel行高\",\"version\":\"1.0.0\",\"src\":\"astronverse.excel.excel.Excel().excel_set_row_height\",\"comment\":\"设置Excel对象 @{excel_obj} 工作表 @{sheet_name} 中 @{row} 的行高\",\"inputList\":[{\"types\":\"ExcelObj\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_obj\",\"title\":\"Excel对象\",\"name\":\"excel_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"sheet_name\",\"title\":\"工作表名\",\"name\":\"sheet_name\",\"tip\":\"输入需编辑的工作表名称,如\'Sheet1\',为空默认使用Excel文件中的第一个工作表对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SetType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"set_type\",\"title\":\"设置类型\",\"name\":\"set_type\",\"tip\":\"\",\"options\":[{\"label\":\"设置值\",\"value\":\"value\"},{\"label\":\"自动调整\",\"value\":\"auto\"}],\"default\":\"auto\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"row\",\"title\":\"行\",\"name\":\"row\",\"tip\":\"输入整数代表行号,从1开始\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"height\",\"title\":\"行高\",\"name\":\"height\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.height.show\",\"expression\":\"return $this.set_type.value == \'value\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"excel-set-row-height\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(166,'keyboard','Gui.keyboard','{\"key\":\"Gui.keyboard\",\"title\":\"键盘输入\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_key.GuiKeyBoard().keyboard\",\"comment\":\"通过(@{keyboard_type})方式模拟键盘输入(@{message})\",\"inputList\":[{\"types\":\"KeyboardType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"keyboard_type\",\"title\":\"输入方式\",\"name\":\"keyboard_type\",\"tip\":\"普通输入:windows键盘消息的方式来模拟按键输入;剪切板输入:获取剪切板内容并粘贴输入;按键组合:使用插入键盘符号组合快捷输入;驱动输入:使用驱动来模拟按键输入,一般用于网银密码框输入场景或其它普通输入方式无法输入的场景\",\"options\":[{\"label\":\"普通输入\",\"value\":\"normal\"},{\"label\":\"驱动输入\",\"value\":\"driver\"},{\"label\":\"剪贴板输入\",\"value\":\"clip\"},{\"label\":\"ghost输入\",\"value\":\"gblid\"}],\"default\":\"normal\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"message\",\"title\":\"输入内容\",\"name\":\"message\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.message.show\",\"expression\":\"return [\'normal\', \'driver\', \'gblid\'].includes($this.keyboard_type.value)\"}],\"required\":true},{\"types\":\"Simulate_flag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"simulate_flag\",\"title\":\"模拟人工输入\",\"name\":\"simulate_flag\",\"tip\":\"模拟人工方式操作键盘逐个输入字符,默认为否\",\"options\":[{\"label\":\"是\",\"value\":\"yes\"},{\"label\":\"否\",\"value\":\"no\"}],\"default\":\"no\",\"dynamics\":[{\"key\":\"$this.simulate_flag.show\",\"expression\":\"return $this.keyboard_type.value == \'normal\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"interval\",\"title\":\"输入间隔\",\"name\":\"interval\",\"tip\":\"设置输入间隔,单位为秒\",\"default\":0.1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.interval.show\",\"expression\":\"return [\'normal\', \'driver\'].includes($this.keyboard_type.value)\"}],\"required\":true}],\"outputList\":[],\"icon\":\"keyboard-input\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(167,'keyboard','Gui.key_input','{\"key\":\"Gui.key_input\",\"title\":\"键盘模拟按键\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_key.GuiKeyBoard().key_input\",\"comment\":\"使用键盘模拟按键(@{keys_str})(@{key_model})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"KEYBOARD\"},\"key\":\"keys_str\",\"title\":\"按键组合\",\"name\":\"keys_str\",\"tip\":\"设置按键组合\",\"default\":\"\",\"required\":true},{\"types\":\"KeyModel\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"key_model\",\"title\":\"按键方式\",\"name\":\"key_model\",\"tip\":\"设置按键方式\",\"options\":[{\"label\":\"单击\",\"value\":\"click\"},{\"label\":\"按下\",\"value\":\"down\"},{\"label\":\"弹起\",\"value\":\"up\"}],\"default\":\"click\",\"required\":true}],\"outputList\":[],\"icon\":\"keyboard-simulate-key\",\"helpManual\":\"通过设置键盘按键进行输入\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(168,'keyboard','Gui.mouse','{\"key\":\"Gui.mouse\",\"title\":\"鼠标点击\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_mouse.GuiMouse().mouse\",\"comment\":\"模拟鼠标(@{btn_type:左键/右键/中键}) (@{btn_model:按下/弹起/单机/双击}))\",\"inputList\":[{\"types\":\"BtnType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"btn_type\",\"title\":\"鼠标按键\",\"name\":\"btn_type\",\"tip\":\"选择点击鼠标上的键位\",\"options\":[{\"label\":\"左键\",\"value\":\"left\"},{\"label\":\"中键\",\"value\":\"middle\"},{\"label\":\"右键\",\"value\":\"right\"}],\"default\":\"left\",\"required\":true},{\"types\":\"BtnModel\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"btn_model\",\"title\":\"点击方式\",\"name\":\"btn_model\",\"tip\":\"选择操作鼠标的点击方式\",\"options\":[{\"label\":\"单击\",\"value\":\"click\"},{\"label\":\"双击\",\"value\":\"double_click\"},{\"label\":\"按下\",\"value\":\"down\"},{\"label\":\"弹起\",\"value\":\"up\"}],\"default\":\"click\",\"required\":true},{\"types\":\"ControlType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"ctrl_type\",\"title\":\"键盘辅助按键\",\"name\":\"ctrl_type\",\"tip\":\"选择键盘辅助按键,默认为无\",\"options\":[{\"label\":\"无\",\"value\":\"none\"},{\"label\":\"ctrl\",\"value\":\"ctrl\"},{\"label\":\"alt\",\"value\":\"alt\"},{\"label\":\"shift\",\"value\":\"shift\"},{\"label\":\"win\",\"value\":\"win\"},{\"label\":\"space\",\"value\":\"space\"}],\"default\":\"none\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[],\"icon\":\"mouse-click\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(169,'keyboard','Gui.mouse_wheel','{\"key\":\"Gui.mouse_wheel\",\"title\":\"鼠标滚动\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_mouse.GuiMouse().mouse_wheel\",\"comment\":\"模拟鼠标(@{scroll_type})滚动(@{scroll_px||times}),滚动方向为:(@{direction})\",\"inputList\":[{\"types\":\"ScrollType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"scroll_type\",\"title\":\"滚动方式\",\"name\":\"scroll_type\",\"tip\":\"\",\"options\":[{\"label\":\"按次数\",\"value\":\"time\"},{\"label\":\"按像素\",\"value\":\"px\"}],\"default\":\"time\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"times\",\"title\":\"自定义次数\",\"name\":\"times\",\"tip\":\"输入鼠标滚动的次数,默认滚动一次距离为120个网页像素\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.times.show\",\"expression\":\"return $this.scroll_type.value == \'time\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"scroll_px\",\"title\":\"自定义像素\",\"name\":\"scroll_px\",\"tip\":\"输入自定义像素,单位为px,一般为0-9999之间的数值\",\"default\":120,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.scroll_px.show\",\"expression\":\"return $this.scroll_type.value == \'px\'\"}],\"required\":true},{\"types\":\"Direction\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"direction\",\"title\":\"滚动方向\",\"name\":\"direction\",\"tip\":\"\",\"options\":[{\"label\":\"向上\",\"value\":\"up\"},{\"label\":\"向下\",\"value\":\"down\"}],\"default\":\"down\",\"required\":true},{\"types\":\"ControlType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"ctrl_type\",\"title\":\"键盘辅助按键\",\"name\":\"ctrl_type\",\"tip\":\"选择键盘辅助按键,默认为无\",\"options\":[{\"label\":\"无\",\"value\":\"none\"},{\"label\":\"ctrl\",\"value\":\"ctrl\"},{\"label\":\"alt\",\"value\":\"alt\"},{\"label\":\"shift\",\"value\":\"shift\"},{\"label\":\"win\",\"value\":\"win\"},{\"label\":\"space\",\"value\":\"space\"}],\"default\":\"none\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.ctrl_type.show\",\"expression\":\"return [\'px\', \'time\'].includes($this.scroll_type.value)\"}],\"required\":true}],\"outputList\":[],\"icon\":\"mouse-scroll-webpage\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(170,'keyboard','Gui.mouse_move','{\"key\":\"Gui.mouse_move\",\"title\":\"鼠标移动\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_mouse.GuiMouse().mouse_move\",\"comment\":\"在(@{window_type})上模拟鼠标移动到(@{position_x}, @{position_y})\",\"inputList\":[{\"types\":\"WindowType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"window_type\",\"title\":\"窗口类型\",\"name\":\"window_type\",\"tip\":\"整个屏幕:以屏幕左上角为坐标相对位置;激活窗口:以激活窗口左上角为相对坐标位置\",\"options\":[{\"label\":\"整个屏幕\",\"value\":\"fullscreen\"},{\"label\":\"激活窗口\",\"value\":\"active_window\"}],\"default\":\"fullscreen\",\"required\":true},{\"types\":\"List\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"window_position\",\"title\":\"窗口左上角位置\",\"name\":\"window_position\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.window_position.show\",\"expression\":\"return $this.window_type.value == \'active_window\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"MOUSEPOSITION\",\"params\":{\"size\":\"middle\"}},\"key\":\"get_mouse_position\",\"title\":\"获取鼠标位置\",\"name\":\"get_mouse_position\",\"tip\":\"\",\"default\":\"\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"position_x\",\"title\":\"鼠标x位置\",\"name\":\"position_x\",\"tip\":\"\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"position_y\",\"title\":\"鼠标y位置\",\"name\":\"position_y\",\"tip\":\"\",\"required\":true},{\"types\":\"MoveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_type\",\"title\":\"鼠标移动方式\",\"name\":\"move_type\",\"tip\":\"选择鼠标移动方式\",\"options\":[{\"label\":\"匀速直线移动\",\"value\":\"linear\"},{\"label\":\"模拟人工方式\",\"value\":\"simulation\"},{\"label\":\"瞬时移动\",\"value\":\"teleportation\"}],\"default\":\"linear\",\"required\":true},{\"types\":\"Speed\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_speed\",\"title\":\"鼠标移动速度\",\"name\":\"move_speed\",\"tip\":\"\",\"options\":[{\"label\":\"慢速\",\"value\":\"slow\"},{\"label\":\"正常\",\"value\":\"normal\"},{\"label\":\"快速\",\"value\":\"fast\"}],\"default\":\"normal\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.move_speed.show\",\"expression\":\"return [\'linear\', \'simulation\'].includes($this.move_type.value)\"}],\"required\":true}],\"outputList\":[],\"icon\":\"mouse-move\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(171,'keyboard','Gui.mouse_drag','{\"key\":\"Gui.mouse_drag\",\"title\":\"鼠标拖拽\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_mouse.GuiMouse().mouse_drag\",\"comment\":\"模拟鼠标从起点位置(@{start_pos_x}, @{start_pos_y})拖拽到终点位置(@{end_pos_x}, @{end_pos_y})\",\"inputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"start_pos_x\",\"title\":\"鼠标起点x位置\",\"name\":\"start_pos_x\",\"tip\":\"\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"start_pos_y\",\"title\":\"鼠标起点y位置\",\"name\":\"start_pos_y\",\"tip\":\"\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"end_pos_x\",\"title\":\"鼠标终点x位置\",\"name\":\"end_pos_x\",\"tip\":\"\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"end_pos_y\",\"title\":\"鼠标终点y位置\",\"name\":\"end_pos_y\",\"tip\":\"\",\"required\":true},{\"types\":\"BtnType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"btn_type\",\"title\":\"鼠标按键\",\"name\":\"btn_type\",\"tip\":\"拖拽时按下的鼠标键位\",\"options\":[{\"label\":\"左键\",\"value\":\"left\"},{\"label\":\"中键\",\"value\":\"middle\"},{\"label\":\"右键\",\"value\":\"right\"}],\"default\":\"left\",\"required\":true},{\"types\":\"MoveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_type\",\"title\":\"移动方式\",\"name\":\"move_type\",\"tip\":\"选择鼠标移动方式\",\"options\":[{\"label\":\"匀速直线移动\",\"value\":\"linear\"},{\"label\":\"模拟人工方式\",\"value\":\"simulation\"},{\"label\":\"瞬时移动\",\"value\":\"teleportation\"}],\"default\":\"linear\",\"required\":true},{\"types\":\"Speed\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_speed\",\"title\":\"移动速度\",\"name\":\"move_speed\",\"tip\":\"选择鼠标移动速度\",\"options\":[{\"label\":\"慢速\",\"value\":\"slow\"},{\"label\":\"正常\",\"value\":\"normal\"},{\"label\":\"快速\",\"value\":\"fast\"}],\"default\":\"normal\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.move_speed.show\",\"expression\":\"return [\'linear\', \'simulation\'].includes($this.move_type.value)\"}],\"required\":true},{\"types\":\"ControlType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"ctrl_type\",\"title\":\"键盘辅助按键\",\"name\":\"ctrl_type\",\"tip\":\"选择鼠标拖拽时的键盘辅助按键,默认为无\",\"options\":[{\"label\":\"无\",\"value\":\"none\"},{\"label\":\"ctrl\",\"value\":\"ctrl\"},{\"label\":\"alt\",\"value\":\"alt\"},{\"label\":\"shift\",\"value\":\"shift\"},{\"label\":\"win\",\"value\":\"win\"},{\"label\":\"space\",\"value\":\"space\"}],\"default\":\"none\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[],\"icon\":\"mouse-drag\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(172,'keyboard','Gui.mouse_position','{\"key\":\"Gui.mouse_position\",\"title\":\"获取鼠标位置\",\"version\":\"1.0.0\",\"src\":\"astronverse.input.gui_mouse.GuiMouse().mouse_position\",\"comment\":\"获取当前鼠标位置并输出至(@{point_x}, @{point_y})\",\"inputList\":[],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"point_x\",\"title\":\"鼠标x位置\",\"tip\":\"\"},{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"point_y\",\"title\":\"鼠标y位置\",\"tip\":\"\"}],\"icon\":\"get-mouse-position\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(173,'network/ftp','Network.ftp_create','{\"key\":\"Network.ftp_create\",\"title\":\"创建FTP连接\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_create\",\"comment\":\"建立与地址(@{host}),端口(@{port})的FTP连接,输出为(@{ftp_instance})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"host\",\"title\":\"服务器地址\",\"name\":\"host\",\"tip\":\"FTP服务器地址\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"port\",\"title\":\"服务器端口号\",\"name\":\"port\",\"tip\":\"FTP服务器端口号\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"name\",\"title\":\"用户名\",\"name\":\"name\",\"tip\":\"文件服务器提供的可登录账号\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"用户密码\",\"name\":\"password\",\"tip\":\"文件服务器提供的可登录账号的密码\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"ftp_instance\",\"title\":\"保存FTP连接对象至\",\"tip\":\"将创建的FTP连接对象保存至指定变量中\"}],\"icon\":\"ftp-create-connection\",\"helpManual\":\"建立一个文件服务器的FTP连接,并返回连接对象\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(174,'network/ftp','Network.ftp_close','{\"key\":\"Network.ftp_close\",\"title\":\"关闭FTP连接\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_close\",\"comment\":\"断开与(@{ftp_instance})的FTP连接,并将结果输出至变量(@{close_ftp})\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP对象\",\"name\":\"ftp_instance\",\"tip\":\"待断开的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"close_ftp\",\"title\":\"断开结果保存至\",\"tip\":\"FTP连接是否成功断开的布尔值\"}],\"icon\":\"ftp-close-connection\",\"helpManual\":\"断开一个FTP连接,并返回断开结果\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(175,'network/ftp','Network.get_work_dir','{\"key\":\"Network.get_work_dir\",\"title\":\"获取工作目录(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().get_work_dir\",\"comment\":\"获取(@{ftp_instance})当前的工作目录,输出为(@{get_work_dir})\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"当前FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_work_dir\",\"title\":\"工作目录保存至\",\"tip\":\"输出获取到的工作目录\"}],\"icon\":\"get-work-directory\",\"helpManual\":\"获取FTP对象当前工作目录\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(176,'network/ftp','Network.change_working_dir','{\"key\":\"Network.change_working_dir\",\"title\":\"切换工作目录(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().change_working_dir\",\"comment\":\"切换FTP连接(@{ftp_instance})的工作目录为(@{new_work_dir}),并保存切换后工作目录到(@{change_work_dir})中\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"需要切换工作目录的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_work_dir\",\"title\":\"工作目录\",\"name\":\"new_work_dir\",\"tip\":\"需要切换到的新的工作目录\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"change_work_dir\",\"title\":\"切换后工作目录\",\"tip\":\"输出切换后当前工作目录\"}],\"icon\":\"change-work-directory\",\"helpManual\":\"切换FTP连接当前工作目录到指定路径\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(177,'network/ftp','Network.create_folder','{\"key\":\"Network.create_folder\",\"title\":\"创建文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().create_folder\",\"comment\":\"在指定FTP连接(@{ftp_instance})下创建文件夹(@{folder_name}),并保存新文件夹路径到(@{new_folder})中\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"需要创建文件夹的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"folder_name\",\"title\":\"新文件夹名称\",\"name\":\"folder_name\",\"tip\":\"需要创建的文件夹名称\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_type\",\"title\":\"文件夹存在时\",\"name\":\"exist_type\",\"tip\":\"当FTP连接下存在同名文件夹时需要执行的操作\",\"options\":[{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"new_folder\",\"title\":\"创建后文件夹路径\",\"tip\":\"将创建后的文件夹路径保存至变量\"}],\"icon\":\"create-folder\",\"helpManual\":\"在指定FTP连接下创建文件夹\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(178,'network/ftp','Network.get_ftp_list','{\"key\":\"Network.get_ftp_list\",\"title\":\"获取文件/文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().get_ftp_list\",\"comment\":\"获取指定FTP连接(@{ftp_instance})下的(@{file_type})列表,并保存到(@{get_ftp_list})中\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"需要获取文件和文件夹信息的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ListType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"获取对象\",\"name\":\"file_type\",\"tip\":\"选择需要获取的对象\",\"options\":[{\"label\":\"全部\",\"value\":\"all\"},{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_ftp_list\",\"title\":\"获取的文件/文件夹列表\",\"tip\":\"将FTP连接下的文件/文件夹列表输出至变量\"}],\"icon\":\"get-folder\",\"helpManual\":\"获取指定FTP连接下的全部文件/文件夹列表\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(179,'network/ftp','Network.ftp_rename','{\"key\":\"Network.ftp_rename\",\"title\":\"重命名文件/文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_rename\",\"comment\":\"重命名(@{ftp_instance})中(@{file_type})(@{cur_file_name||cur_folder_name})为新名称(@{new_file_name||new_folder_name}),并将重命名后路径保存至变量(@{rename_ftp_path})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"执行文件/文件夹重命名操作的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"重命名对象\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cur_file_name\",\"title\":\"原文件名称\",\"name\":\"cur_file_name\",\"tip\":\"输入待重命名文件\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cur_file_name.show\",\"expression\":\"return $this.file_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_file_name\",\"title\":\"新文件名称(不包含文件扩展名)\",\"name\":\"new_file_name\",\"tip\":\"输入新文件名称,不包含文件扩展名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.new_file_name.show\",\"expression\":\"return $this.file_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cur_folder_name\",\"title\":\"原文件夹名称\",\"name\":\"cur_folder_name\",\"tip\":\"输入待重命名文件夹\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.cur_folder_name.show\",\"expression\":\"return $this.file_type.value == \'folder\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_folder_name\",\"title\":\"新文件夹名称\",\"name\":\"new_folder_name\",\"tip\":\"输入新文件夹名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.new_folder_name.show\",\"expression\":\"return $this.file_type.value == \'folder\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_type\",\"title\":\"重命名对象存在时\",\"name\":\"exist_type\",\"tip\":\"重命名后对象已存在时执行的操作\",\"options\":[{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"rename_ftp_path\",\"title\":\"重命名后路径\",\"tip\":\"保存重命名后(@{file_type})路径至变量\"}],\"icon\":\"rename-folder\",\"helpManual\":\"重命名FTP服务器上文件/文件夹\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(180,'network/ftp','Network.ftp_upload','{\"key\":\"Network.ftp_upload\",\"title\":\"上传文件/文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_upload\",\"comment\":\"上传(@{file_path||folder_path})至FTP指定目录(@{ftp_pwd}),并将上传后路径结果保存到(@{upload_ftp_list})中\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"需要上传文件/文件夹的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"上传对象\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_pwd\",\"title\":\"远程工作目录\",\"name\":\"ftp_pwd\",\"tip\":\"文件/文件夹上传的远程工作目录,默认为空,上传至当前工作路径\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"files\"}},\"key\":\"file_path\",\"title\":\"待上传文件\",\"name\":\"file_path\",\"tip\":\"支持单个或多个文件上传,多个文件名之间用逗号隔开,如:test1.txt,test2.txt,test3.txt\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.file_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"待上传文件夹\",\"name\":\"folder_path\",\"tip\":\"支持单个或多个文件夹上传,多个文件夹之间用逗号隔开,如:folder1,folder2,folder3\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.folder_path.show\",\"expression\":\"return $this.file_type.value == \'folder\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_type\",\"title\":\"上传对象已存在时\",\"name\":\"exist_type\",\"tip\":\"要上传的文件/文件夹在FTP服务器上已存在时的处理方式\",\"options\":[{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"upload_ftp_list\",\"title\":\"上传列表\",\"tip\":\"输出已上传文件/文件夹在FTP服务器上的路径信息至变量,数据类型为列表\"}],\"icon\":\"upload-folder\",\"helpManual\":\"将本地一个或多个文件/文件夹上传到至FTP指定目录下\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(181,'network/ftp','Network.ftp_download','{\"key\":\"Network.ftp_download\",\"title\":\"下载文件/文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_download\",\"comment\":\"下载(@{download_file_name||download_folder_name})至本地目录(@{dst_path}),并将下载后路径保存到变量(@{download_ftp_path})\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"执行下载文件/文件夹的FTP服务器连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"下载对象\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"download_file_name\",\"title\":\"待下载文件\",\"name\":\"download_file_name\",\"tip\":\"支持单个或多个文件下载,多个文件名之间用逗号隔开,如:test1.txt,test2.txt,test3.txt\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.download_file_name.show\",\"expression\":\"return $this.file_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"download_folder_name\",\"title\":\"待下载文件夹\",\"name\":\"download_folder_name\",\"tip\":\"支持单个或多个文件夹下载,多个文件夹之间用逗号隔开,如:folder1,folder2,folder3\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.download_folder_name.show\",\"expression\":\"return $this.file_type.value == \'folder\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"dst_path\",\"title\":\"下载至\",\"name\":\"dst_path\",\"tip\":\"本地存储路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"本地路径不存在时\",\"name\":\"state_type\",\"tip\":\"当本地存储路径不存在时执行的操作\",\"options\":[{\"label\":\"新建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"create\",\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_type\",\"title\":\"下载对象存在时\",\"name\":\"exist_type\",\"tip\":\"下载文件/文件夹存在时执行的操作\",\"options\":[{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"download_ftp_path\",\"title\":\"下载列表\",\"tip\":\"输出已下载的文件/文件夹路径列表至变量,数据类型为列表\"}],\"icon\":\"download-folder\",\"helpManual\":\"将FTP服务器上的多个文件/文件夹下载至本地\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(182,'network/ftp','Network.ftp_delete','{\"key\":\"Network.ftp_delete\",\"title\":\"删除文件/文件夹(FTP)\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.ftp.FTP().ftp_delete\",\"comment\":\"删除(@{ftp_instance})上(@{delete_file_name||delete_folder_name}),并输出删除结果至变量(@{delete_ftp_result})\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"ftp_instance\",\"title\":\"FTP连接对象\",\"name\":\"ftp_instance\",\"tip\":\"执行文件/文件夹删除操作的FTP连接对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"删除对象\",\"name\":\"file_type\",\"tip\":\"\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"file\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_file_name\",\"title\":\"待删除文件\",\"name\":\"delete_file_name\",\"tip\":\"支持单个或多个文件删除,多个文件名之间使用逗号隔开,如:test1.txt,test2.txt,test3.txt\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_file_name.show\",\"expression\":\"return $this.file_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_folder_name\",\"title\":\"待删除文件夹\",\"name\":\"delete_folder_name\",\"tip\":\"支持单个或多个文件夹删除,多个文件夹之间用逗号隔开,如:folder1,folder2,folder3\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_folder_name.show\",\"expression\":\"return $this.file_type.value == \'folder\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"delete_ftp_result\",\"title\":\"删除结果\",\"tip\":\"输出删除结果到变量,数据类型为布尔值\"}],\"icon\":\"delete-folder-ftp\",\"helpManual\":\"删除FTP服务器中指定文件/文件夹\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(183,'network/http','Network.http_request','{\"key\":\"Network.http_request\",\"title\":\"HTTP请求\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.network.Network().http_request\",\"comment\":\"向URL(@{url})发送HTTP请求(@{request_type}), 请求头(@{headers}), 请求体(@{body}), 并保存响应结果到{@{http_response}}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"URL\",\"name\":\"url\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"RequestType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"request_type\",\"title\":\"请求类型\",\"name\":\"request_type\",\"tip\":\"\",\"options\":[{\"label\":\"post\",\"value\":\"post\"},{\"label\":\"get\",\"value\":\"get\"},{\"label\":\"connect\",\"value\":\"connect\"},{\"label\":\"put\",\"value\":\"put\"},{\"label\":\"patch\",\"value\":\"patch\"},{\"label\":\"delete\",\"value\":\"delete\"},{\"label\":\"options\",\"value\":\"options\"},{\"label\":\"head\",\"value\":\"head\"},{\"label\":\"trace\",\"value\":\"trace\"}],\"default\":\"post\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"headers\",\"title\":\"请求头\",\"name\":\"headers\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"body\",\"title\":\"请求体\",\"name\":\"body\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.body.show\",\"expression\":\"return [\'post\', \'put\'].includes($this.request_type.value)\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待上传文件路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.request_type.value == \'post\'\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"time_out\",\"title\":\"超时时间\",\"name\":\"time_out\",\"tip\":\"\",\"default\":60,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"响应结果保存到文件\",\"name\":\"save_type\",\"tip\":\"将响应结果保存到指定文本文件中\",\"options\":[{\"label\":\"保存\",\"value\":\"yes\"},{\"label\":\"删除\",\"value\":\"no\"}],\"default\":\"no\",\"level\":\"advanced\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_path\",\"title\":\"文档保存目录\",\"name\":\"save_path\",\"tip\":\"输入文件保存目录\",\"default\":\"\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.save_path.show\",\"expression\":\"return $this.save_type.value == \'yes\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"save_name\",\"title\":\"文件名称\",\"name\":\"save_name\",\"tip\":\"输入保存文件名称,默认为文本文件\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.save_name.show\",\"expression\":\"return $this.save_type.value == \'yes\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"http_response\",\"title\":\"保存响应结果到\",\"tip\":\"\"}],\"icon\":\"http-request\",\"helpManual\":\"向指定url发送HTTP请求\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(184,'network/http','Network.http_download','{\"key\":\"Network.http_download\",\"title\":\"HTTP下载\",\"version\":\"1.0.0\",\"src\":\"astronverse.network.network.Network().http_download\",\"comment\":\"HTTP下载\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"下载地址\",\"name\":\"url\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"dst_dir\",\"title\":\"文件保存目录\",\"name\":\"dst_dir\",\"tip\":\"\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"rename\",\"title\":\"指定文件名\",\"name\":\"rename\",\"tip\":\"不指定自动沿用下载路径中的默认文件名,没有默认文件名则报错\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.rename.show\",\"expression\":\"return $this.state_type.value == \'create\'\"}],\"required\":false},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"设置文件保存目录不存在时的处理方式\",\"options\":[{\"label\":\"新建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"create\",\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_type\",\"title\":\"文件存在时\",\"name\":\"exist_type\",\"tip\":\"设置目标文件已存在时的处理方式\",\"options\":[{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"http_download_path\",\"title\":\"下载文件保存到\",\"tip\":\"将下载文件路径保存到变量中\"}],\"icon\":\"http-download\",\"helpManual\":\"下载指定URL(@{url})的文件保存到指定目录(@{dst_dir})中,并保存下载路径到变量(@{http_download_path})\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(185,'ai/ocr','OpenApi.id_card','{\"key\":\"OpenApi.id_card\",\"title\":\"身份证识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().id_card\",\"comment\":\"身份证识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"]}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\",\".xls\",\".csv\"],\"defaultPath\":\"未命名.xls\"}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"id_card\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"id_card\",\"title\":\"身份证识别结果对象\",\"tip\":\"输出身份证识别结果\"}],\"icon\":\"id-card-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(186,'ai/ocr','OpenApi.business_license','{\"key\":\"OpenApi.business_license\",\"title\":\"营业执照识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().business_license\",\"comment\":\"营业执照识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"]}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\",\".xls\",\".csv\"],\"defaultPath\":\"未命名.xls\"}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"business_license\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"business_license\",\"title\":\"营业执照结果对象\",\"tip\":\"输出营业执照结果对象\"}],\"icon\":\"business-license-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(187,'ai/ocr','OpenApi.vat_invoice','{\"key\":\"OpenApi.vat_invoice\",\"title\":\"增值税发票识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().vat_invoice\",\"comment\":\"增值税发票识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"],\"defaultPath\":\"未命名.xls\"}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\",\".xls\",\".csv\"]}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"vat_invoice\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"vat_invoice\",\"title\":\"增值税发票识别结果对象\",\"tip\":\"输出增值税发票识别结果对象\"}],\"icon\":\"vat-invoice-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(188,'ai/ocr','OpenApi.train_ticket','{\"key\":\"OpenApi.train_ticket\",\"title\":\"火车票识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().train_ticket\",\"comment\":\"火车票识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"]}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\",\".xls\",\".csv\"],\"defaultPath\":\"未命名.xls\"}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"train_ticket\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"train_ticket\",\"title\":\"火车票识别结果对象\",\"tip\":\"火车票识别结果对象\"}],\"icon\":\"train-ticket-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(189,'ai/ocr','OpenApi.taxi_ticket','{\"key\":\"OpenApi.taxi_ticket\",\"title\":\"出租车发票识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().taxi_ticket\",\"comment\":\"出租车发票识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"]}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".xlsx\",\".xls\",\".csv\"],\"defaultPath\":\"未命名.xls\"}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"taxi_ticket\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"taxi_ticket\",\"title\":\"出租车发票识别结果对象\",\"tip\":\"出租车发票识别结果对象\"}],\"icon\":\"taxi-invoice-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(190,'ai/ocr','OpenApi.common_ocr','{\"key\":\"OpenApi.common_ocr\",\"title\":\"通用文字识别\",\"version\":\"1.0.0\",\"src\":\"astronverse.openapi.openapi.OpenApi().common_ocr\",\"comment\":\"通用文字识别\",\"inputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_multi\",\"title\":\"批量处理\",\"name\":\"is_multi\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\",\"filters\":[\".jpeg\",\".jpg\",\".png\",\".gif\",\".bmp\"]}},\"key\":\"src_file\",\"title\":\"图像文件\",\"name\":\"src_file\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_file.show\",\"expression\":\"return $this.is_multi.value == false\"}],\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"src_dir\",\"title\":\"图像文件夹\",\"name\":\"src_dir\",\"tip\":\"\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.src_dir.show\",\"expression\":\"return $this.is_multi.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"is_save\",\"title\":\"输出文档\",\"name\":\"is_save\",\"tip\":\"批量处理选择“否”则每次单个处理一份图片文件,选择“是”则默认按文件夹类文件顺序依次进行处理,默认保存为excel文档,不支持修改\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"folder\"}},\"key\":\"dst_file\",\"title\":\"文档输出路径\",\"name\":\"dst_file\",\"tip\":\"选择文档输出文件夹\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file.show\",\"expression\":\"return $this.is_save.value == true\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文档输出文件名\",\"name\":\"dst_file_name\",\"tip\":\"选择文档输出文件名\",\"default\":\"common_ocr\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"common_ocr\",\"title\":\"通用文字识别结果对象\",\"tip\":\"输出通用文字识别结果对象\"}],\"icon\":\"general-text-recognition\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(191,'document/document.PDF','PDF.get_pages_num','{\"key\":\"PDF.get_pages_num\",\"title\":\"获取PDF文档页数\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().get_pages_num\",\"comment\":\"获取路径为 @{file_path} 的PDF文档页数 @{pdf_pages_num}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"输入PDF文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"PDF文件密码\",\"name\":\"password\",\"tip\":\"输入PDF文件密码,如果没有密码则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"pdf_pages_num\",\"title\":\"PDF文档页数\",\"tip\":\"返回PDF文档页数\"}],\"icon\":\"pdf-get-page-count\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(192,'document/document.PDF','PDF.get_pdf_text','{\"key\":\"PDF.get_pdf_text\",\"title\":\"提取PDF文档文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().get_pdf_text\",\"comment\":\"提取路径为 @{file_path} 的PDF文档文本,返回为字符串或列表 @{pdf_text}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[]}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"输入PDF文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password\",\"title\":\"PDF文件密码\",\"name\":\"password\",\"tip\":\"输入PDF文件密码,如果没有密码则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"title\":\"选择范围\",\"name\":\"select_range\",\"tip\":\"选择范围,支持全部页面和指定页面范围\",\"options\":[{\"label\":\"所有页面\",\"value\":\"all\"},{\"label\":\"指定页面\",\"value\":\"part\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_range\",\"title\":\"指定页面范围\",\"name\":\"page_range\",\"tip\":\"输入页面范围,格式为1-3,5,7-9,11,表示从1到3页,5页,7到9页,11页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_range.show\",\"expression\":\"return $this.select_range.value == \'part\'\"}],\"required\":true},{\"types\":\"TextSaveType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"text_save_type\",\"title\":\"是否保存为文件\",\"name\":\"text_save_type\",\"tip\":\"选择是否保存提取的文本为文件,可保存为Word或文本文件格式\",\"options\":[{\"label\":\"不保存\",\"value\":\"none\"},{\"label\":\"Word文件\",\"value\":\"word\"},{\"label\":\"文本文件\",\"value\":\"txt\"},{\"label\":\"Word文件和文本文件\",\"value\":\"word_and_txt\"}],\"default\":\"none\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.save_dir.show\",\"expression\":\"return [\'txt\', \'word_and_txt\', \'word\'].includes($this.text_save_type.value)\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"save_file_name\",\"title\":\"保存文件名\",\"name\":\"save_file_name\",\"tip\":\"输入保存文件名,不输入则使用默认文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.save_file_name.show\",\"expression\":\"return [\'txt\', \'word_and_txt\', \'word\'].includes($this.text_save_type.value)\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return [\'txt\', \'word_and_txt\', \'word\'].includes($this.text_save_type.value)\"}],\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"pdf_text\",\"title\":\"提取的PDF文档文本\",\"tip\":\"提取的PDF文档文本,以列表形式存储返回\"}],\"icon\":\"pdf-extract-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(193,'document/document.PDF','PDF.get_pdf_images','{\"key\":\"PDF.get_pdf_images\",\"title\":\"提取PDF文档图片\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().get_pdf_images\",\"comment\":\"提取路径为 @{file_path} 的PDF文档图片,并保存到 @{save_dir} 文件夹\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"PDF文件密码\",\"name\":\"pwd\",\"tip\":\"如果PDF文件需要密码,请输入密码,否则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"title\":\"选择范围\",\"name\":\"select_range\",\"tip\":\"选择范围,支持全部页面和指定页面范围\",\"options\":[{\"label\":\"所有页面\",\"value\":\"all\"},{\"label\":\"指定页面\",\"value\":\"part\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_range\",\"title\":\"指定页面范围\",\"name\":\"page_range\",\"tip\":\"输入页面范围,格式为1-3,5,7-9,11,表示从1到3页,5页,7到9页,11页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_range.show\",\"expression\":\"return $this.select_range.value == \'part\'\"}],\"required\":false},{\"types\":\"PictureType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"image_type\",\"title\":\"图片格式\",\"name\":\"image_type\",\"tip\":\"选择图片格式,支持JPEG、PNG等格式\",\"options\":[{\"label\":\"PNG\",\"value\":\"png\"},{\"label\":\"JPEG\",\"value\":\"jpeg\"}],\"default\":\"png\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"prefix\",\"title\":\"图片文件名前缀\",\"name\":\"prefix\",\"tip\":\"输入图片文件名前缀,不输入则使用原文件名前缀\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[],\"icon\":\"pdf-extract-images\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(194,'document/document.PDF','PDF.merge_pdf_files','{\"key\":\"PDF.merge_pdf_files\",\"title\":\"合并PDF文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().merge_pdf_files\",\"comment\":\"合并PDF文件为一个PDF文件,并保存到 @{pdf_merge_file_path} 路径\",\"inputList\":[{\"types\":\"MergeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"merge_type\",\"title\":\"合并方式\",\"name\":\"merge_type\",\"tip\":\"选择合并方式,支持按文件夹合并或按文件合并\",\"options\":[{\"label\":\"按文件夹合并\",\"value\":\"folder\"},{\"label\":\"按文件合并\",\"value\":\"file\"}],\"default\":\"folder\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_folder_path\",\"title\":\"合并文件夹\",\"name\":\"file_folder_path\",\"tip\":\"将会合并文件夹中所有的PDF文件\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_folder_path.show\",\"expression\":\"return $this.merge_type.value == \'folder\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"files\"}},\"key\":\"files_path\",\"title\":\"合并文件\",\"name\":\"files_path\",\"tip\":\"将会合并所选的所有PDF文件\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.files_path.show\",\"expression\":\"return $this.merge_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_file_name\",\"title\":\"合并文件名\",\"name\":\"new_file_name\",\"tip\":\"输入合并文件名,不输入则使用默认文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"new_pwd_flag\",\"title\":\"是否给新PDF文件设置密码\",\"name\":\"new_pwd_flag\",\"tip\":\"选择是否设置密码\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_pwd\",\"title\":\"新PDF文件密码\",\"name\":\"new_pwd\",\"tip\":\"输入新PDF文件密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.new_pwd.show\",\"expression\":\"return $this.new_pwd_flag.value == true\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"pdf_merge_file_path\",\"title\":\"合并后的PDF文件路径\",\"tip\":\"合并后的PDF文件路径\"}],\"icon\":\"merge-pdf-files\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(195,'document/document.PDF','PDF.extract_pdf_file','{\"key\":\"PDF.extract_pdf_file\",\"title\":\"抽取PDF指定页\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().extract_pdf_file\",\"comment\":\"抽取路径为 @{file_path} 的PDF文档指定页,并保存到 @{extract_file_path} 文件夹\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"选择需要抽取的PDF文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"PDF文件密码\",\"name\":\"pwd\",\"tip\":\"如果PDF文件需要密码,请输入密码,否则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_range\",\"title\":\"指定页面范围\",\"name\":\"page_range\",\"tip\":\"输入页面范围,格式为1-3,5,7-9,11,表示从1到3页,5页,7到9页,11页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_file_name\",\"title\":\"抽取后新PDF文件名\",\"name\":\"new_file_name\",\"tip\":\"输入新文件名,不输入则使用默认文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"new_pwd_flag\",\"title\":\"是否给新PDF文件设置密码\",\"name\":\"new_pwd_flag\",\"tip\":\"选择是否设置密码\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_pwd\",\"title\":\"新PDF文件密码\",\"name\":\"new_pwd\",\"tip\":\"输入新PDF文件密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.new_pwd.show\",\"expression\":\"return $this.new_pwd_flag.value == true\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"extract_file_path\",\"title\":\"提取后的PDF文件路径\",\"tip\":\"提取后的PDF文件路径\"}],\"icon\":\"pdf-extract-page\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(196,'document/document.PDF','PDF.extract_forms_from_pdf','{\"key\":\"PDF.extract_forms_from_pdf\",\"title\":\"提取PDF表格到Excel\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().extract_forms_from_pdf\",\"comment\":\"提取路径为 @{file_path} 的PDF文档中的表格,并保存到 @{forms_file_path} 文件夹\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"选择需要提取表格的PDF文件路径\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"PDF文件密码\",\"name\":\"pwd\",\"tip\":\"如果PDF文件需要密码,请输入密码,否则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"title\":\"选择范围\",\"name\":\"select_range\",\"tip\":\"选择范围,支持全部页面和指定页面范围\",\"options\":[{\"label\":\"所有页面\",\"value\":\"all\"},{\"label\":\"指定页面\",\"value\":\"part\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_range\",\"title\":\"指定页面范围\",\"name\":\"page_range\",\"tip\":\"输入页面范围,格式为1-3,5,7-9,11,表示从1到3页,5页,7到9页,11页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_range.show\",\"expression\":\"return $this.select_range.value == \'part\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"combine_flag\",\"title\":\"是否需要合并多个表格\",\"name\":\"combine_flag\",\"tip\":\"当出现跨页的表格时,可以选择是否合并成一个表格,建议表头相同时选择合并\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_file_name\",\"title\":\"表格文件名\",\"name\":\"new_file_name\",\"tip\":\"输入表格文件名,不输入则使用默认文件名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"forms_file_path\",\"title\":\"提取后的Excel文件路径\",\"tip\":\"提取后的Excel文件路径\"}],\"icon\":\"pdf-extract-table-to-excel\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(197,'document/document.PDF','PDF.convert_pdf_to_img','{\"key\":\"PDF.convert_pdf_to_img\",\"title\":\"PDF页面转图片\",\"version\":\"1.0.0\",\"src\":\"astronverse.pdf.pdf.PDF().convert_pdf_to_img\",\"comment\":\"转换路径为 @{file_path} 的PDF文档为图片,并保存到 @{save_dir} 文件夹\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"PDF文件路径\",\"name\":\"file_path\",\"tip\":\"选择需要转换的PDF文件路径\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"PDF文件密码\",\"name\":\"pwd\",\"tip\":\"如果PDF文件需要密码,请输入密码,否则留空\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"PictureType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"image_type\",\"title\":\"图片格式\",\"name\":\"image_type\",\"tip\":\"选择图片格式,支持JPEG、PNG等格式\",\"options\":[{\"label\":\"PNG\",\"value\":\"png\"},{\"label\":\"JPEG\",\"value\":\"jpeg\"}],\"default\":\"png\",\"required\":true},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"title\":\"选择范围\",\"name\":\"select_range\",\"tip\":\"选择范围,支持全部页面和指定页面范围\",\"options\":[{\"label\":\"所有页面\",\"value\":\"all\"},{\"label\":\"指定页面\",\"value\":\"part\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_range\",\"title\":\"指定页面范围\",\"name\":\"page_range\",\"tip\":\"输入页面范围,格式为1-3,5,7-9,11,表示从1到3页,5页,7到9页,11页\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_range.show\",\"expression\":\"return $this.select_range.value == \'part\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"save_dir\",\"title\":\"保存文件路径\",\"name\":\"save_dir\",\"tip\":\"选择保存文件的文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"prefix\",\"title\":\"图片文件名前缀\",\"name\":\"prefix\",\"tip\":\"输入图片文件名前缀,不输入则使用原文件名前缀\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"level\":\"advanced\",\"required\":true}],\"outputList\":[],\"icon\":\"pdf-page-to-image\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(198,'code','Report.print','{\"key\":\"Report.print\",\"title\":\"日志打印\",\"version\":\"1.0.0\",\"src\":\"astronverse.report.report.Report().print\",\"comment\":\"将变量(@{msg})打印\",\"inputList\":[{\"types\":\"ReportLevelType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"report_type\",\"title\":\"日志类型\",\"name\":\"report_type\",\"options\":[{\"label\":\"信息\",\"value\":\"info\"},{\"label\":\"警告\",\"value\":\"warning\"},{\"label\":\"错误\",\"value\":\"error\"}],\"default\":\"info\",\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"msg\",\"title\":\"日志内容\",\"name\":\"msg\",\"tip\":\"打印运行过程中输出的流变量\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"log-print\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(199,'script','Script.module','{\"key\":\"Script.module\",\"title\":\"运行Python模块\",\"version\":\"1.0.0\",\"src\":\"astronverse.script.script.Script().module\",\"comment\":\"运行Python模块(@{content:Python模块})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"SELECT\",\"params\":{\"filters\":\"PyModule\"}},\"key\":\"content\",\"title\":\"选择Python模块\",\"name\":\"content\",\"tip\":\"\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"program_script\",\"title\":\"执行结果\",\"tip\":\"\"}],\"icon\":\"run-python-module\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(200,'desktop','Software.open','{\"key\":\"Software.open\",\"title\":\"打开程序\",\"version\":\"1.0.0\",\"src\":\"astronverse.software.software.Software().open\",\"comment\":\"打开应用程序路径(@{app_abs_path}),并设置运行参数为(@{app_args}),将结果输出为(@{software_open})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[]}},\"key\":\"app_absolute_path\",\"name\":\"app_absolute_path\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"app_arguments\",\"name\":\"app_arguments\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"software_open\",\"title\":\"应用程序路径\",\"tip\":\"输出该被打开的应用程序路径\"}],\"icon\":\"open-program\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(201,'desktop','Software.close','{\"key\":\"Software.close\",\"title\":\"关闭程序\",\"version\":\"1.0.0\",\"src\":\"astronverse.software.software.Software().close\",\"comment\":\"关闭应用程序路径为(@{app_abs_path})的程序\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[]}},\"key\":\"app_absolute_path\",\"name\":\"app_absolute_path\",\"required\":true}],\"outputList\":[],\"icon\":\"close-program\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(202,'','Software.cmd','{\"key\":\"Software.cmd\",\"title\":\"Cmd命令\",\"version\":\"1.0.0\",\"src\":\"astronverse.software.software.Software().cmd\",\"comment\":\"通过配置cmd字符串(@{cmd}),执行cmd命令,执行结果保存至(@{exec_cmd})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"cmd\",\"title\":\"cmd字符串\",\"name\":\"cmd\",\"tip\":\"输入cmd字符串\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"exec_cmd\",\"title\":\"执行结果\",\"tip\":\"输出自定义cmd命令运行后返回的对象结果\"}],\"icon\":\"cmd-command\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(203,'os/os.clipboard','System.copy_clip','{\"key\":\"System.copy_clip\",\"title\":\"复制到剪切板\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.clipboard.Clipboard().copy_clip\",\"comment\":\"将 @{content_type} @{message||file_path||folder_path} 复制到剪切板\",\"inputList\":[{\"types\":\"ContentType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"content_type\",\"title\":\"复制类型\",\"name\":\"content_type\",\"tip\":\"选择复制到剪贴板的内容类型\",\"options\":[{\"label\":\"文本内容\",\"value\":\"msg\"},{\"label\":\"HTML格式文本内容\",\"value\":\"html\"},{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"msg\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"message\",\"title\":\"待复制的文本内容\",\"name\":\"message\",\"tip\":\"输入文本内容\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.message.show\",\"expression\":\"return $this.content_type.value == \'msg\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待复制的文件路径\",\"name\":\"file_path\",\"tip\":\"输入或选择待复制文件\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.content_type.value == \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"待复制的文件夹路径\",\"name\":\"folder_path\",\"tip\":\"输入或选择待复制文件夹\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.folder_path.show\",\"expression\":\"return $this.content_type.value == \'folder\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"copy-to-clipboard\",\"helpManual\":\"支持将文本内容/文件/文件夹复制到剪切板\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(204,'os/os.clipboard','System.clear_clip','{\"key\":\"System.clear_clip\",\"title\":\"清空剪切板\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.clipboard.Clipboard().clear_clip\",\"comment\":\"清空剪切板中的内容\",\"inputList\":[],\"outputList\":[],\"icon\":\"clear-clipboard\",\"helpManual\":\"清空剪切板中的内容\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(205,'os/os.clipboard','System.paste_clip','{\"key\":\"System.paste_clip\",\"title\":\"获取剪切板\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.clipboard.Clipboard().paste_clip\",\"comment\":\"获取剪切板中的 @{content_type} ,并保存至变量 @{output_content}\",\"inputList\":[{\"types\":\"ContentType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"content_type\",\"title\":\"获取类型\",\"name\":\"content_type\",\"tip\":\"选择剪切板中要获取的内容类型\",\"options\":[{\"label\":\"文本内容\",\"value\":\"msg\"},{\"label\":\"HTML格式文本内容\",\"value\":\"html\"},{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"}],\"default\":\"msg\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"dst_path\",\"title\":\"保存路径\",\"name\":\"dst_path\",\"tip\":\"请输入或选择保存路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_path.show\",\"expression\":\"return [\'file\', \'folder\'].includes($this.content_type.value)\"}],\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"保存路径不存在时\",\"name\":\"state_type\",\"tip\":\"设置保存路径不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"dynamics\":[{\"key\":\"$this.state_type.show\",\"expression\":\"return [\'folder\', \'file\'].includes($this.content_type.value)\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_file_name\",\"title\":\"文件保存名称\",\"name\":\"dst_file_name\",\"tip\":\"输入保存的文件名,不需加文件后缀,为空自动使用原文件名\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.dst_file_name.show\",\"expression\":\"return $this.content_type.value == \'file\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dst_folder_name\",\"title\":\"文件夹保存名称\",\"name\":\"dst_folder_name\",\"tip\":\"输入保存的文件夹名称,为空自动使用原文件夹名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.dst_folder_name.show\",\"expression\":\"return $this.content_type.value == \'folder\'\"}],\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"output_content\",\"title\":\"输出剪切板内容至变量\",\"tip\":\"获取文本内容输出为文本变量,获取文件/文件夹输出为路径信息\"}],\"icon\":\"get-clipboard\",\"helpManual\":\"支持获取剪切板中的文本内容/文件/文件夹,并将文本内容/文件文件夹保存路径保存到指定路径中\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(206,'os/os.zip','System.compress','{\"key\":\"System.compress\",\"title\":\"压缩\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.compress.Compress().compress\",\"comment\":\"压缩 @{file_type} 到指定目录 @{compress_dir} 中,并保存压缩包路径至 @{compress_path}\",\"inputList\":[{\"types\":\"FileFolderType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_type\",\"title\":\"选择压缩方式\",\"name\":\"file_type\",\"tip\":\"选择压缩文件/文件夹/文件和文件夹\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"文件夹\",\"value\":\"folder\"},{\"label\":\"文件和文件夹\",\"value\":\"both\"}],\"default\":\"file\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"files\"}},\"key\":\"file_path\",\"title\":\"待压缩文件\",\"name\":\"file_path\",\"tip\":\"选择或输入待压缩文件路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.file_type.value != \'folder\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"待压缩文件夹\",\"name\":\"folder_path\",\"tip\":\"选择或输入待压缩文件夹路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.folder_path.show\",\"expression\":\"return $this.file_type.value != \'file\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"compress_dir\",\"title\":\"目标路径\",\"name\":\"compress_dir\",\"tip\":\"保存目标压缩文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目标路径不存在时\",\"name\":\"state_type\",\"tip\":\"选择目标路径不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"compress_name\",\"title\":\"压缩包名称\",\"name\":\"compress_name\",\"tip\":\"指定压缩包名称,为空则默认以压缩列表中第一个压缩文件名称命名\",\"default\":\"\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"压缩密码\",\"name\":\"pwd\",\"tip\":\"为压缩包设置解压缩密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"压缩后源文件是否保存\",\"name\":\"save_type\",\"tip\":\"\",\"options\":[{\"label\":\"删除\",\"value\":\"delete\"},{\"label\":\"保留\",\"value\":\"save\"}],\"default\":\"save\",\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"compress_path\",\"title\":\"输出压缩后压缩包路径\",\"tip\":\"\"}],\"icon\":\"compress\",\"helpManual\":\"压缩选择文件与文件夹到指定目录,支持单个或多个文件文件夹同时压缩为一个压缩包(压缩后文件格式为zip格式)\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(207,'os/os.zip','System.uncompress','{\"key\":\"System.uncompress\",\"title\":\"解压\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.compress.Compress().uncompress\",\"comment\":\"将压缩文件 @{source_path} 解压至指定目录 @{target_path} 下,并输出解压后文件所在路径 @{uncompress_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"source_path\",\"title\":\"压缩包路径\",\"name\":\"source_path\",\"tip\":\"请选择或输入压缩包路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_path\",\"title\":\"目标解压目录\",\"name\":\"target_path\",\"tip\":\"选择或输入解压后文件保存路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"status_type\",\"title\":\"目标目录不存在时\",\"name\":\"status_type\",\"tip\":\"选择目标目录不存在时执行的操作,默认直接创建\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pwd\",\"title\":\"解压密码\",\"name\":\"pwd\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"解压缩后源文件是否保留\",\"name\":\"save_type\",\"tip\":\"解压后对源文件的操作,默认保留\",\"options\":[{\"label\":\"删除\",\"value\":\"delete\"},{\"label\":\"保留\",\"value\":\"save\"}],\"default\":\"save\",\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"uncompress_path\",\"title\":\"输出解压后文件路径\",\"tip\":\"\"}],\"icon\":\"decompress\",\"helpManual\":\"将压缩文件解压至指定路径\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(208,'os/os.file','File.file_exist','{\"key\":\"File.file_exist\",\"title\":\"IF 文件存在\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_exist\",\"comment\":\"判断文件 @{file_path} 是否存在,存在就执行以下操作\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"输入或选择文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"ExistType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_type\",\"title\":\"判断类型\",\"name\":\"exist_type\",\"tip\":\"选择判断类型\",\"options\":[{\"label\":\"存在\",\"value\":\"exist\"},{\"label\":\"不存在\",\"value\":\"not_exist\"}],\"default\":\"exist\",\"required\":false}],\"outputList\":[],\"icon\":\"check-file-exists\",\"helpManual\":\"判断文件是否存在并将判断结果输出至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(209,'os/os.file','File.file_create','{\"key\":\"File.file_create\",\"title\":\"新建文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_create\",\"comment\":\"在指定目录 @{dst_path} 下创建文件 @{file_name} ,并保存新建文件路径到 @{new_file_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"dst_path\",\"title\":\"指定目录\",\"name\":\"dst_path\",\"tip\":\"输入或选择指定文件夹目录\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"file_name\",\"title\":\"文件名称\",\"name\":\"file_name\",\"tip\":\"输入文件名称及扩展名\",\"default\":\"\",\"required\":true},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件存在时\",\"name\":\"exist_options\",\"tip\":\"选择新建文件已存在时的操作;生成副本:将在文件名称后增加数字标识,如a(1).txt,如果副本文件同样存在,则数字递增;覆盖:删除已存在文件并重新创建;跳过:返回当前已存在文件路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"new_file_path\",\"title\":\"新建文件路径\",\"tip\":\"输出新建文件路径,并保存到变量中,数据类型为字符串\"}],\"icon\":\"create-new-file\",\"helpManual\":\"在指定目录下新建文件\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(210,'os/os.file','File.file_delete','{\"key\":\"File.file_delete\",\"title\":\"删除文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_delete\",\"comment\":\"删除文件 @{file_path} ,并将结果输出至变量 @{delete_file_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待删除文件\",\"name\":\"file_path\",\"tip\":\"选择或输入待删除文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"DeleteType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"delete_options\",\"title\":\"删除操作\",\"name\":\"delete_options\",\"tip\":\"选择将文件彻底删除或移入回收站\",\"options\":[{\"label\":\"彻底删除\",\"value\":\"delete\"},{\"label\":\"移入回收站\",\"value\":\"trash\"}],\"default\":\"delete\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"delete_file_result\",\"title\":\"输出删除结果至\",\"tip\":\"将文件删除结果保存至变量,数据类型为布尔值\"}],\"icon\":\"delete-file\",\"helpManual\":\"删除指定文件,并将执行结果输出至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(211,'os/os.file','File.file_copy','{\"key\":\"File.file_copy\",\"title\":\"复制文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_copy\",\"comment\":\"复制文件 @{file_path} 到指定目录 @{target_path} 下,并保存复制后的文件路径 @{copy_file_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待复制文件\",\"name\":\"file_path\",\"tip\":\"选择或输入待复制文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_path\",\"title\":\"指定目录\",\"name\":\"target_path\",\"tip\":\"选择或输入目标目录\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"设置指定目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"file_name\",\"title\":\"复制后文件名\",\"name\":\"file_name\",\"tip\":\"指定复制后文件名,无需输入文件扩展名,为空默认使用原文件名称\",\"default\":\"\",\"required\":false},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"copy_options\",\"title\":\"文件存在时\",\"name\":\"copy_options\",\"tip\":\"选择指定目录下文件已存在时的操作; 生成副本:将在文件名称后增加数字标识,如a(1).txt,如果副本文件同样存在,则数字递增; 覆盖:删除已存在文件并重新创建; 跳过:返回当前已存在文件路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"copy_file_path\",\"title\":\"复制后文件路径\",\"tip\":\"输出复制后文件路径,并保存至变量,数据类型为字符串\"}],\"icon\":\"copy-file\",\"helpManual\":\"复制目标文件到指定目录\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(212,'os/os.file','File.file_write','{\"key\":\"File.file_write\",\"title\":\"写入文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_write\",\"comment\":\"写入内容 @{msg} 到文件 @{file_path} 中,并保存写入后的文件路径 @{write_file_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入文件路径,支持写入的文件类型有:\\\".txt\\\", \\\".docx\\\", \\\".md\\\", \\\".py\\\", \\\".json\\\", \\\".csv\\\"\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"file_option\",\"title\":\"文件不存在时\",\"name\":\"file_option\",\"tip\":\"选择文件不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"msg\",\"title\":\"写入内容\",\"name\":\"msg\",\"tip\":\"输入要写入的内容\",\"default\":\"\",\"required\":true},{\"types\":\"WriteType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"write_type\",\"title\":\"写入方式\",\"name\":\"write_type\",\"tip\":\"指定写入方式\",\"options\":[{\"label\":\"覆盖写入\",\"value\":\"overwrite\"},{\"label\":\"追加写入\",\"value\":\"append\"}],\"default\":\"append\",\"required\":false},{\"types\":\"EncodeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"encode_type\",\"title\":\"编码方式\",\"name\":\"encode_type\",\"tip\":\"选择写入内容的编码方式,文件内容为空时需指定,默认选项时自动使用待写入文件的编码方式\",\"options\":[{\"label\":\"默认\",\"value\":\"default\"},{\"label\":\"ansi\",\"value\":\"ansi\"},{\"label\":\"utf-8\",\"value\":\"utf-8\"},{\"label\":\"utf-16\",\"value\":\"utf-16\"},{\"label\":\"utf-16 be\",\"value\":\"utf-16 be\"},{\"label\":\"gbk\",\"value\":\"gbk\"},{\"label\":\"gb2312\",\"value\":\"gb2312\"},{\"label\":\"gb18030\",\"value\":\"gb18030\"}],\"default\":\"default\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"write_file_path\",\"title\":\"文件路径\",\"tip\":\"输出写入后文件路径至变量,数据类型为字符串\"}],\"icon\":\"write-file\",\"helpManual\":\"向指定文件中写入内容,并输出写入后的文件路径至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(213,'os/os.file','File.get_file_encoding_type','{\"key\":\"File.get_file_encoding_type\",\"title\":\"获取文件编码类型\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().get_file_encoding_type\",\"comment\":\"获取文件 @{file_path} 的编码类型并保存至变量 @{file_encoding_type}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入待获取编码类型的文件路径\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"file_encoding_type\",\"title\":\"文件编码类型\",\"tip\":\"输出文件编码类型至变量,数据类型为字符串\"}],\"icon\":\"get-file-encoding\",\"helpManual\":\"获取指定文件的编码类型,并输出编码类型至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(214,'os/os.file','File.file_read','{\"key\":\"File.file_read\",\"title\":\"读取文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_read\",\"comment\":\"按照 @{read_type} 方式读取文件 @{file_path} 中的内容,并保存至变量 @{read_file_content}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待读取文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入待读取文件路径, 支持读取的文件类型有:\\\".txt\\\", \\\".docx\\\", \\\".md\\\", \\\".py\\\", \\\".json\\\", \\\".csv\\\"\",\"default\":\"\",\"required\":true},{\"types\":\"ReadType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"read_type\",\"title\":\"读取方式\",\"name\":\"read_type\",\"tip\":\"选择读取方式,全部读取:读取全部内容; 按行读取:按行读取文本内容; 二进制:以二进制方式读取文件内容\",\"options\":[{\"label\":\"读取全部内容\",\"value\":\"all\"},{\"label\":\"按行读取到列表中\",\"value\":\"list\"},{\"label\":\"二进制方式读取\",\"value\":\"byte\"}],\"default\":\"all\",\"required\":false},{\"types\":\"EncodeType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"encode_type\",\"title\":\"编码方式\",\"name\":\"encode_type\",\"tip\":\"选择读取内容的编码方式,默认选项时自动使用待读取文件的编码方式\",\"options\":[{\"label\":\"默认\",\"value\":\"default\"},{\"label\":\"ansi\",\"value\":\"ansi\"},{\"label\":\"utf-8\",\"value\":\"utf-8\"},{\"label\":\"utf-16\",\"value\":\"utf-16\"},{\"label\":\"utf-16 be\",\"value\":\"utf-16 be\"},{\"label\":\"gbk\",\"value\":\"gbk\"},{\"label\":\"gb2312\",\"value\":\"gb2312\"},{\"label\":\"gb18030\",\"value\":\"gb18030\"}],\"default\":\"default\",\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"read_file_content\",\"title\":\"读取内容为\",\"tip\":\"输出读取内容至变量,数据类型为字符串\"}],\"icon\":\"read-file\",\"helpManual\":\"读取指定文件内容,并将读取内容输出保存至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(215,'os/os.file','File.file_move','{\"key\":\"File.file_move\",\"title\":\"移动文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_move\",\"comment\":\"将文件 @{file_path} 移动至指定目录 @{target_folder} 下,并保存移动后的文件路径 @{move_file_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待移动文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入待移动文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_folder\",\"title\":\"指定目录\",\"name\":\"target_folder\",\"tip\":\"选择或输入目标目录, 指定目录不存在时默认自动创建\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"设置指定目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"移动后文件名\",\"name\":\"file_name\",\"tip\":\"输入移动后文件名(不需后缀名),默认为空使用原文件名\",\"default\":\"\",\"required\":false},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件存在时\",\"name\":\"exist_options\",\"tip\":\"选择文件存在时执行的操作(覆盖、跳过、生成副本)\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"move_file_path\",\"title\":\"移动后文件路径\",\"tip\":\"输出移动后文件路径至变量,数据类型为字符串\"}],\"icon\":\"move-file\",\"helpManual\":\"将指定文件移动到目标目录,并输出移动后的文件路径至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(216,'os/os.file','File.file_rename','{\"key\":\"File.file_rename\",\"title\":\"重命名文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_rename\",\"comment\":\"将文件 @{file_path} 重命名为 @{new_name} 并保存重命名后的文件路径 @{rename_file_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待重命名文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入待重命名文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_name\",\"title\":\"新文件名\",\"name\":\"new_name\",\"tip\":\"输入新文件名(不需后缀名)\",\"default\":\"\",\"required\":true},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件存在时\",\"name\":\"exist_options\",\"tip\":\"选择文件存在时执行的操作(覆盖、跳过、生成副本)\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"rename_file_path\",\"title\":\"重命名后文件路径\",\"tip\":\"输出重命名后文件路径至变量,数据类型为字符串\"}],\"icon\":\"rename-file\",\"helpManual\":\"将指定文件重命名,并输出重命名后的文件路径至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(217,'os/os.file','File.file_search','{\"key\":\"File.file_search\",\"title\":\"查找文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_search\",\"comment\":\"在 @{folder_path} 目录中查找文件名包含 @{search_pattern} 的文件,并保存匹配文件路径至变量 @{find_file_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"待查找文件所在目录\",\"name\":\"folder_path\",\"tip\":\"选择或输入待查找文件所在目录\",\"default\":\"\",\"required\":true},{\"types\":\"SearchType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"find_type\",\"title\":\"查找方式\",\"name\":\"find_type\",\"tip\":\"指定查找方式:精确匹配需输入完整文件名;模糊匹配输入部分文件名,需区分大小写;正则表达式匹配输入正则表达式\",\"options\":[{\"label\":\"精确匹配\",\"value\":\"exact\"},{\"label\":\"模糊匹配\",\"value\":\"fuzzy\"},{\"label\":\"正则表达式匹配\",\"value\":\"regex\"}],\"default\":\"fuzzy\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"search_pattern\",\"title\":\"查找文件名\",\"name\":\"search_pattern\",\"tip\":\"输入查找内容\",\"default\":\"\",\"required\":true},{\"types\":\"TraverseType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"traverse_subfolder\",\"title\":\"遍历子文件夹\",\"name\":\"traverse_subfolder\",\"tip\":\"选择是否遍历子文件夹\",\"options\":[{\"label\":\"遍历子目录\",\"value\":\"yes\"},{\"label\":\"不遍历子目录\",\"value\":\"no\"}],\"default\":\"no\",\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"find_file_result\",\"title\":\"保存查找结果至\",\"tip\":\"输出匹配文件路径列表,并保存到变量,数据类型为列表\"}],\"icon\":\"find-file\",\"helpManual\":\"在指定目录中查找文件名包含指定内容的文件,并输出文件列表至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(218,'os/os.file','File.file_wait_status','{\"key\":\"File.file_wait_status\",\"title\":\"等待文件\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_wait_status\",\"comment\":\"等待文件 @{file_path} 状态为 @{status_type} 时执行下一步,保存等待结果到 @{wait_file_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"StatusType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"status_type\",\"title\":\"等待状态\",\"name\":\"status_type\",\"tip\":\"选择等待文件的状态\",\"options\":[{\"label\":\"被创建\",\"value\":\"created\"},{\"label\":\"被删除\",\"value\":\"deleted\"}],\"default\":\"created\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"超时时间\",\"name\":\"wait_time\",\"tip\":\"设置等待超时时间,单位为秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"wait_file_result\",\"title\":\"保存等待结果至\",\"tip\":\"输出等待结果至变量,数据类型为布尔值\"}],\"icon\":\"wait-file\",\"helpManual\":\"等待文件状态为指定状态时执行下一步,并输出等待结果至变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(219,'os/os.file','File.file_info','{\"key\":\"File.file_info\",\"title\":\"获取文件信息\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().file_info\",\"comment\":\"获取指定文件 @{file_path} 的 @{info_type} 信息,并保存文件信息至变量 @{file_info}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"InfoType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"info_type\",\"title\":\"获取信息类型\",\"name\":\"info_type\",\"tip\":\"指定获取的信息类型\",\"options\":[{\"label\":\"全部信息\",\"value\":\"all\"},{\"label\":\"绝对路径\",\"value\":\"abs_path\"},{\"label\":\"根目录\",\"value\":\"root\"},{\"label\":\"文件路径\",\"value\":\"directory\"},{\"label\":\"文件大小(字节)\",\"value\":\"size\"},{\"label\":\"文件名\",\"value\":\"name_ext\"},{\"label\":\"单独文件名\",\"value\":\"name\"},{\"label\":\"文件扩展名\",\"value\":\"extension\"},{\"label\":\"文件创建时间\",\"value\":\"c_time\"},{\"label\":\"文件修改时间\",\"value\":\"m_time\"}],\"default\":\"all\",\"required\":false}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"file_info\",\"title\":\"文件信息为\",\"tip\":\"输出文件信息至变量,获取全部信息时数据类型为字典,获取其他信息时数据类型为字符串\"}],\"icon\":\"get-file-info\",\"helpManual\":\"获取文件的全部信息/大小/文件路径/大小(字节)/根目录/文件夹目录/名称与扩展名/名称/扩展名/创建时间/修改时间,并保存到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(220,'os/os.file','File.get_file_list','{\"key\":\"File.get_file_list\",\"title\":\"获取文件列表\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.file.File().get_file_list\",\"comment\":\"获取指定目录 @{folder_path} 中的文件列表,并保存文件列表至变量 @{file_list}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件目录\",\"name\":\"folder_path\",\"tip\":\"选择或输入文件目录路径\",\"default\":\"\",\"required\":true},{\"types\":\"TraverseType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"traverse_subfolder\",\"title\":\"遍历子目录\",\"name\":\"traverse_subfolder\",\"tip\":\"选择是否遍历子目录\",\"options\":[{\"label\":\"遍历子目录\",\"value\":\"yes\"},{\"label\":\"不遍历子目录\",\"value\":\"no\"}],\"default\":\"no\",\"required\":true},{\"types\":\"OutputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出方式\",\"name\":\"output_type\",\"tip\":\"设置输出方式\",\"options\":[{\"label\":\"列表输出\",\"value\":\"list\"},{\"label\":\"存储到表格文件\",\"value\":\"excel\"}],\"default\":\"list\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"excel_path\",\"title\":\"Excel文件保存目录\",\"name\":\"excel_path\",\"tip\":\"选择或输入保存的Excel文件保存目录\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.excel_path.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"选择Excel文件保存目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"dynamics\":[{\"key\":\"$this.state_type.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_name\",\"title\":\"Excel文件名\",\"name\":\"excel_name\",\"tip\":\"输入保存的Excel文件名,不需输入文件扩展名,默认扩展名为.xlsx\",\"default\":\"1.xlsx\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.excel_name.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":true},{\"types\":\"SortMethod\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sort_method\",\"title\":\"排序方式\",\"name\":\"sort_method\",\"tip\":\"指定排序方式\",\"options\":[{\"label\":\"无\",\"value\":\"none\"},{\"label\":\"按创建时间排序\",\"value\":\"ctime\"},{\"label\":\"按修改时间排序\",\"value\":\"mtime\"}],\"default\":\"none\",\"level\":\"advanced\",\"required\":false},{\"types\":\"SortType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sort_type\",\"title\":\"排序类型\",\"name\":\"sort_type\",\"tip\":\"指定排序类型\",\"options\":[{\"label\":\"升序\",\"value\":\"ascending\"},{\"label\":\"降序\",\"value\":\"descending\"}],\"default\":\"ascending\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.sort_type.show\",\"expression\":\"return $this.sort_method.value [\'ctime\', \'mtime\']\"}],\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"file_list\",\"title\":\"获取文件列表为\",\"tip\":\"输出获取到的文件列表到变量,数据类型为列表\"}],\"icon\":\"get-file-list\",\"helpManual\":\"获取指定目录中的文件列表,并保存至输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(221,'os/os.path','Folder.folder_exist','{\"key\":\"Folder.folder_exist\",\"title\":\"IF 文件夹存在\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_exist\",\"comment\":\"判断文件夹 @{folder_path} @{exist_type},存在就执行以下操作\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"ExistType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_type\",\"title\":\"判断类型\",\"name\":\"exist_type\",\"tip\":\"设置判断类型\",\"options\":[{\"label\":\"存在\",\"value\":\"exist\"},{\"label\":\"不存在\",\"value\":\"not_exist\"}],\"default\":\"exist\",\"required\":false}],\"outputList\":[],\"icon\":\"check-folder-exists\",\"helpManual\":\"判断文件夹是否存在\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(222,'os/os.path','Folder.folder_open','{\"key\":\"Folder.folder_open\",\"title\":\"打开文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_open\",\"comment\":\"打开指定文件夹 @{folder_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入文件夹路径\",\"default\":\"\",\"required\":true}],\"outputList\":[],\"icon\":\"open-folder\",\"helpManual\":\"打开指定文件夹\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(223,'os/os.path','Folder.folder_create','{\"key\":\"Folder.folder_create\",\"title\":\"创建文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_create\",\"comment\":\"在指定目录 @{target_path} 创建文件夹 @{folder_name} ,并保存新文件夹路径至变量 @{new_folder_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_path\",\"title\":\"指定目录\",\"name\":\"target_path\",\"tip\":\"选择或输入创建文件夹路径,路径不存在时自动创建\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\",\"params\":{\"size\":\"middle\"}},\"key\":\"folder_name\",\"title\":\"文件夹名称\",\"name\":\"folder_name\",\"tip\":\"输入创建文件夹名称\",\"default\":\"\",\"required\":true},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件夹存在时\",\"name\":\"exist_options\",\"tip\":\"选择新建文件夹已存在时的操作;生成副本:将在文件名称后增加数字标识,如a(1),如果副本文件同样存在,则数字递增;覆盖:删除已存在文件夹并重新创建;跳过:返回当前已存在文件夹路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"new_folder_path\",\"title\":\"创建文件夹路径\",\"tip\":\"保存创建的文件夹路径到输出变量,数据类型为字符串\"}],\"icon\":\"create-folder\",\"helpManual\":\"在指定目录下创建文件夹,并保存新建文件夹路径到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(224,'os/os.path','Folder.folder_delete','{\"key\":\"Folder.folder_delete\",\"title\":\"删除文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_delete\",\"comment\":\"删除文件夹 @{folder_path} ,并将结果输出至变量 @{delete_folder_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"DeleteType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"delete_options\",\"title\":\"删除操作\",\"name\":\"delete_options\",\"tip\":\"选择将文件夹彻底删除或移入回收站\",\"options\":[{\"label\":\"彻底删除\",\"value\":\"delete\"},{\"label\":\"移入回收站\",\"value\":\"trash\"}],\"default\":\"delete\",\"required\":false}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"delete_folder_result\",\"title\":\"删除文件夹结果\",\"tip\":\"保存删除文件夹的结果到输出变量,数据类型为布尔值\"}],\"icon\":\"delete-folder-ftp\",\"helpManual\":\"删除文件夹\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(225,'os/os.path','Folder.folder_copy','{\"key\":\"Folder.folder_copy\",\"title\":\"复制文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_copy\",\"comment\":\"将文件夹 @{source_path} 复制到指定目录 @{target_path} 下,并保存复制后的文件夹路径至变量 @{copy_folder_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"source_path\",\"title\":\"文件夹路径\",\"name\":\"source_path\",\"tip\":\"选择或输入待复制文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_path\",\"title\":\"指定目录\",\"name\":\"target_path\",\"tip\":\"选择或输入目标文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"设置指定目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"folder_name\",\"title\":\"文件夹名称\",\"name\":\"folder_name\",\"tip\":\"默认为空,使用源文件夹名称\",\"default\":\"\",\"required\":false},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件夹存在时\",\"name\":\"exist_options\",\"tip\":\"选择文件夹已存在时的操作;生成副本:将在文件名称后增加数字标识,如a(1),如果副本文件夹同样存在,则数字递增;覆盖:删除已存在文件夹;跳过:返回当前已存在文件路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"copy_folder_path\",\"title\":\"复制后文件夹路径\",\"tip\":\"保存复制后的文件夹路径到输出变量,数据类型为字符串\"}],\"icon\":\"copy-folder\",\"helpManual\":\"复制文件夹到指定目录,并保存复制后的文件夹路径到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(226,'os/os.path','Folder.folder_move','{\"key\":\"Folder.folder_move\",\"title\":\"移动文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_move\",\"comment\":\"移动文件夹 @{folder_path} 到指定目录 @{target_folder} 下,并保存移动后的文件夹路径至变量 @{move_folder_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入待移动文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"target_folder\",\"title\":\"指定目录\",\"name\":\"target_folder\",\"tip\":\"选择或输入目标文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"设置指定目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"folder_name\",\"title\":\"文件夹名称\",\"name\":\"folder_name\",\"tip\":\"默认为空,使用原文件夹名称\",\"default\":\"\",\"required\":false},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件夹存在时\",\"name\":\"exist_options\",\"tip\":\"选择文件夹已存在时的操作;生成副本:将在文件名称后增加数字标识,如a(1),如果副本文件夹同样存在,则数字递增;覆盖:删除已存在文件夹;跳过:返回当前已存在文件路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"move_folder_path\",\"title\":\"移动后文件夹路径\",\"tip\":\"保存移动后文件夹路径至输出变量,数据类型为字符串\"}],\"icon\":\"move-folder\",\"helpManual\":\"移动文件夹到指定目录,并保存移动后的文件夹路径到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(227,'os/os.path','Folder.folder_rename','{\"key\":\"Folder.folder_rename\",\"title\":\"重命名文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_rename\",\"comment\":\"重命名文件夹 @{folder_path} 为 @{new_name} ,并保存重命名后的文件夹路径至变量 @{rename_folder_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入待重命名文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_name\",\"title\":\"新文件夹名称\",\"name\":\"new_name\",\"tip\":\"输入新文件夹名称\",\"default\":\"\",\"required\":true},{\"types\":\"OptionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"exist_options\",\"title\":\"文件夹存在时\",\"name\":\"exist_options\",\"tip\":\"选择文件夹已存在时的操作;生成副本:将在文件名称后增加数字标识,如a(1),如果副本文件夹同样存在,则数字递增;覆盖:删除已存在文件夹;跳过:返回当前已存在文件路径\",\"options\":[{\"label\":\"覆盖原有文件夹\",\"value\":\"overwrite\"},{\"label\":\"取消保存操作\",\"value\":\"skip\"},{\"label\":\"创建文件夹副本\",\"value\":\"generate\"}],\"default\":\"generate\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"rename_folder_path\",\"title\":\"重命名文件夹路径\",\"tip\":\"保存重命名后文件夹路径到输出变量,数据类型为字符串\"}],\"icon\":\"rename-folder\",\"helpManual\":\"重命名文件夹,并保存重命名后的文件夹路径到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(228,'os/os.path','Folder.folder_clear','{\"key\":\"Folder.folder_clear\",\"title\":\"清空文件夹\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().folder_clear\",\"comment\":\"清空文件夹 @{folder_path} ,并保存清空结果到变量 @{clear_folder_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹路径\",\"name\":\"folder_path\",\"tip\":\"选择或输入待清空文件夹\",\"default\":\"\",\"required\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"clear_folder_result\",\"title\":\"清空操作结果\",\"tip\":\"保存清空结果到输出变量,数据类型为布尔值\"}],\"icon\":\"clear-folder\",\"helpManual\":\"清空指定文件夹,并保存操作结果到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(229,'os/os.path','Folder.get_folder_list','{\"key\":\"Folder.get_folder_list\",\"title\":\"获取文件夹列表\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.folder.Folder().get_folder_list\",\"comment\":\"获取指定目录 @{folder_path} 下的文件夹列表,并保存文件夹列表至变量 @{folder_list}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"文件夹目录\",\"name\":\"folder_path\",\"tip\":\"选择或输入文件夹目录路径\",\"default\":\"\",\"required\":true},{\"types\":\"TraverseType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"traverse_subfolder\",\"title\":\"遍历子目录\",\"name\":\"traverse_subfolder\",\"tip\":\"选择是否遍历目录下的子目录\",\"options\":[{\"label\":\"遍历子目录\",\"value\":\"yes\"},{\"label\":\"不遍历子目录\",\"value\":\"no\"}],\"default\":\"no\",\"required\":false},{\"types\":\"OutputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_type\",\"title\":\"输出类型\",\"name\":\"output_type\",\"tip\":\"选择文件夹列表的输出类型\",\"options\":[{\"label\":\"列表输出\",\"value\":\"list\"},{\"label\":\"存储到表格文件\",\"value\":\"excel\"}],\"default\":\"list\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"excel_path\",\"title\":\"Excel文件保存目录\",\"name\":\"excel_path\",\"tip\":\"选择或输入保存的Excel文件保存目录\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.excel_path.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"目录不存在时\",\"name\":\"state_type\",\"tip\":\"选择Excel文件保存目录不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"dynamics\":[{\"key\":\"$this.state_type.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"excel_name\",\"title\":\"Excel文件名\",\"name\":\"excel_name\",\"tip\":\"输入保存的Excel文件名,不需输入文件扩展名,默认扩展名为.xlsx\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.excel_name.show\",\"expression\":\"return $this.output_type.value == \'excel\'\"}],\"required\":true},{\"types\":\"SortMethod\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sort_method\",\"title\":\"排序方式\",\"name\":\"sort_method\",\"tip\":\"选择排序方式\",\"options\":[{\"label\":\"无\",\"value\":\"none\"},{\"label\":\"按创建时间排序\",\"value\":\"ctime\"},{\"label\":\"按修改时间排序\",\"value\":\"mtime\"}],\"default\":\"none\",\"level\":\"advanced\",\"required\":true},{\"types\":\"SortType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sort_type\",\"title\":\"排序类型\",\"name\":\"sort_type\",\"tip\":\"指定排序类型\",\"options\":[{\"label\":\"升序\",\"value\":\"ascending\"},{\"label\":\"降序\",\"value\":\"descending\"}],\"default\":\"ascending\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.sort_type.show\",\"expression\":\"return $this.sort_method.value [\'ctime\', \'mtime\']\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"folder_list\",\"title\":\"文件夹列表\",\"tip\":\"保存获取的文件夹列表到输出变量,数据类型为列表\"}],\"icon\":\"get-folder-list\",\"helpManual\":\"获取文件夹列表,并保存到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(230,'os/os.system','System.run_command','{\"key\":\"System.run_command\",\"title\":\"运行或打开\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.process.Process().run_command\",\"comment\":\"运行或打开 @{command} ,并将执行结果保存到输出变量 @{process_out}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"command\",\"title\":\"命令或可执行文件\",\"name\":\"command\",\"tip\":\"输入cmd命令行或可执行.exe文件\",\"default\":\"\",\"required\":true},{\"types\":\"CmdType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"cmd_type\",\"title\":\"运行方式\",\"name\":\"cmd_type\",\"tip\":\"选择命令行执行方式\",\"options\":[{\"label\":\"运行\",\"value\":\"normal\"},{\"label\":\"管理员身份运行\",\"value\":\"admin\"}],\"default\":\"normal\",\"required\":false},{\"types\":\"RunType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"run_type\",\"title\":\"在指令执行结束之后\",\"name\":\"run_type\",\"tip\":\"选择是否继续执行指令,或等待程序执行结束\",\"options\":[{\"label\":\"继续执行下一指令\",\"value\":\"continue\"},{\"label\":\"等待指令执行结束\",\"value\":\"complete\"}],\"default\":\"continue\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"params\",\"title\":\"参数\",\"name\":\"params\",\"tip\":\"程序执行的所需参数\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"work_dir\",\"title\":\"工作目录\",\"name\":\"work_dir\",\"tip\":\"所执行命令的工作目录\",\"default\":\"\",\"level\":\"advanced\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待时间不超过\",\"name\":\"wait_time\",\"tip\":\"指定命令执行的等待时间,默认60秒\",\"default\":60,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.wait_time.show\",\"expression\":\"return $this.run_type.value == \'complete\'\"}],\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"process_out\",\"title\":\"保存执行结果至\",\"tip\":\"输出cmd命令的执行结果并保存至变量,数据类型为布尔值\"}],\"icon\":\"run-or-open\",\"helpManual\":\"运行cmd命令或打开可执行.exe文件,并将执行结果保存到输出变量\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(231,'os/os.system','System.get_pid','{\"key\":\"System.get_pid\",\"title\":\"获取进程PID\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.process.Process().get_pid\",\"comment\":\"获取与输入 @{process_name} 匹配的进程PID,并保存至输出变量 @{match_proces_pid}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"process_name\",\"title\":\"待匹配内容\",\"name\":\"process_name\",\"tip\":\"输入待匹配进程内容,精确匹配模式下需输入完整进程名,区分大小写\",\"default\":\"\",\"required\":true},{\"types\":\"SearchType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"search_type\",\"title\":\"匹配方式\",\"name\":\"search_type\",\"tip\":\"选择匹配方式\",\"options\":[{\"label\":\"精确匹配\",\"value\":\"exact\"},{\"label\":\"模糊匹配\",\"value\":\"fuzzy\"},{\"label\":\"正则表达式匹配\",\"value\":\"regex\"}],\"default\":\"exact\",\"required\":false},{\"types\":\"PidType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"pid_type\",\"title\":\"获取内容\",\"name\":\"pid_type\",\"tip\":\"指定获取到匹配进程PID的内容\",\"options\":[{\"label\":\"获取全部\",\"value\":\"all\"},{\"label\":\"获取一个\",\"value\":\"one\"}],\"default\":\"all\",\"required\":false}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"match_proces_pid\",\"title\":\"输出匹配进程PID至\",\"tip\":\"当获取全部匹配PID时输出为列表,获取单个匹配PID时输出为整型\"}],\"icon\":\"get-process-pid\",\"helpManual\":\"输入待匹配进程名称,支持精确匹配/模糊匹配/正则表达式匹配,可获取单个匹配进程PID或全部匹配进程PID,并输出至变量列表\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(232,'os/os.system','System.terminate_process','{\"key\":\"System.terminate_process\",\"title\":\"终止进程\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.process.Process().terminate_process\",\"comment\":\"终止指定 @{termination_type} 的进程,并保存执行结果至输出变量 @{termination_process}\",\"inputList\":[{\"types\":\"TerminationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"termination_type\",\"title\":\"终止方式\",\"name\":\"termination_type\",\"tip\":\"指定终止方式,通过进程PID/进程名称终止进程\",\"options\":[{\"label\":\"进程PID\",\"value\":\"pid\"},{\"label\":\"进程名称\",\"value\":\"name\"}],\"default\":\"pid\",\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"pid\",\"title\":\"进程PID\",\"name\":\"pid\",\"tip\":\"要终止的进程PID\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.pid.show\",\"expression\":\"return $this.termination_type.value == \'pid\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"process_name\",\"title\":\"进程名称\",\"name\":\"process_name\",\"tip\":\"要终止的进程名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.process_name.show\",\"expression\":\"return $this.termination_type.value == \'name\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"time_out\",\"title\":\"超时时间\",\"name\":\"time_out\",\"tip\":\"指定超时时间,单位为秒\",\"default\":5,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"termination_process\",\"title\":\"保存执行结果至\",\"tip\":\"输出执行结果并保存到变量,数据类型为布尔值\"}],\"icon\":\"terminate-process\",\"helpManual\":\"终止指定PID/名称的进程程序\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(233,'os/os.screenshot','System.screen_shot','{\"key\":\"System.screen_shot\",\"title\":\"屏幕截图\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.system.System().screen_shot\",\"comment\":\"按 @{screen_type} 方式截图并保存到 @{png_path} ,输出图片保存路径 @{screenshot_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"png_path\",\"title\":\"图片保存路径\",\"name\":\"png_path\",\"tip\":\"输入或选择截图后的文件保存路径\",\"default\":\"\",\"required\":true},{\"types\":\"StateType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"state_type\",\"title\":\"路径不存在时\",\"name\":\"state_type\",\"tip\":\"选择图片保存文件夹不存在时执行的操作\",\"options\":[{\"label\":\"创建\",\"value\":\"create\"},{\"label\":\"提示并报错\",\"value\":\"error\"}],\"default\":\"error\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"png_name\",\"title\":\"图片保存名称\",\"name\":\"png_name\",\"tip\":\"输入截图后的文件名,不需加文件后缀\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ScreenType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"screen_type\",\"title\":\"截图区域\",\"name\":\"screen_type\",\"tip\":\"指定截图区域\",\"options\":[{\"label\":\"全屏\",\"value\":\"full\"},{\"label\":\"选择区域\",\"value\":\"region\"}],\"default\":\"full\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"top_left_x\",\"title\":\"左上角x坐标\",\"name\":\"top_left_x\",\"tip\":\"输入截图的矩形区域左上角横坐标,单位为像素px\",\"default\":0,\"dynamics\":[{\"key\":\"$this.top_left_x.show\",\"expression\":\"return $this.screen_type.value == \'region\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"top_left_y\",\"title\":\"左上角y坐标\",\"name\":\"top_left_y\",\"tip\":\"输入截图的矩形区域左上角纵坐标,单位为像素px\",\"default\":0,\"dynamics\":[{\"key\":\"$this.top_left_y.show\",\"expression\":\"return $this.screen_type.value == \'region\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"bottom_right_x\",\"title\":\"右下角x坐标\",\"name\":\"bottom_right_x\",\"tip\":\"输入截图的矩形区域右下角横坐标,单位为像素px\",\"default\":0,\"dynamics\":[{\"key\":\"$this.bottom_right_x.show\",\"expression\":\"return $this.screen_type.value == \'region\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"bottom_right_y\",\"title\":\"右下角y坐标\",\"name\":\"bottom_right_y\",\"tip\":\"输入截图的矩形区域右下角纵坐标,单位为像素px\",\"default\":0,\"dynamics\":[{\"key\":\"$this.bottom_right_y.show\",\"expression\":\"return $this.screen_type.value == \'region\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"screenshot_path\",\"title\":\"截图保存路径\",\"tip\":\"\"}],\"icon\":\"screen-screenshot\",\"helpManual\":\"选择屏幕或指定区域进行截屏,并保存到指定路径\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(234,'','System.screen_lock','{\"key\":\"System.screen_lock\",\"title\":\"屏幕锁定\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.system.System().screen_lock\",\"comment\":\"系统屏幕锁屏,并将操作结果输出至变量 @{screen_lock_result}\",\"inputList\":[],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"screen_lock_result\",\"title\":\"锁屏执行结果\",\"tip\":\"输出锁屏操作的执行结果并保存至变量,变量类型为布尔值\"}],\"icon\":\"screen-lock\",\"helpManual\":\"系统屏幕锁屏\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(235,'','System.screen_unlock','{\"key\":\"System.screen_unlock\",\"title\":\"屏幕解锁\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.system.System().screen_unlock\",\"comment\":\"通过用户名 @{user_name} 和 @{pwd_type} @{password_text||password_rsa} 进行系统屏幕解锁,并将操作结果输出至变量 @{screen_unlock_result}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"user_name\",\"title\":\"用户名\",\"name\":\"user_name\",\"tip\":\"待解锁屏幕用户名\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"PwdType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"pwd_type\",\"title\":\"解锁类型\",\"name\":\"pwd_type\",\"tip\":\"待解锁屏幕解锁类型\",\"options\":[{\"label\":\"密钥\",\"value\":\"rsa\"},{\"label\":\"密码\",\"value\":\"password\"}],\"default\":\"password\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password_text\",\"title\":\"密码\",\"name\":\"password_text\",\"tip\":\"待解锁屏幕解锁密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.password_text.show\",\"expression\":\"return $this.pwd_type.value == \'password\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"password_rsa\",\"title\":\"密钥\",\"name\":\"password_rsa\",\"tip\":\"待解锁屏幕解锁密钥\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.password_rsa.show\",\"expression\":\"return $this.pwd_type.value == \'rsa\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"screen_unlock_result\",\"title\":\"执行结果\",\"tip\":\"输出屏幕解锁的执行结果并保存至变量,数据类型为布尔值\"}],\"icon\":\"screen-unlock\",\"helpManual\":\"输入用户名和密码进行系统屏幕解锁\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(236,'','System.printer','{\"key\":\"System.printer\",\"title\":\"打印机打印\",\"version\":\"1.0.0\",\"src\":\"astronverse.system.system.System().printer\",\"comment\":\"使用打印机 @{printer_name} @{batch_print} 打印指定文件 @{file_path||folder_path} ,并保存执行结果至输出变量 @{printer_status}\",\"inputList\":[{\"types\":\"FileType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"file_type\",\"title\":\"打印对象\",\"name\":\"file_type\",\"tip\":\"指定打印对象,支持打印WORD、EXCEL、PDF和图像文件,或批量打印文件夹中文件\",\"options\":[{\"label\":\"图片\",\"value\":\"picture\"},{\"label\":\"word\",\"value\":\"word\"},{\"label\":\"pdf\",\"value\":\"pdf\"},{\"label\":\"excel\",\"value\":\"excel\"}],\"default\":\"word\",\"required\":true},{\"types\":\"BatchType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"batch_print\",\"title\":\"批量打印\",\"name\":\"batch_print\",\"tip\":\"设置批量打印\",\"options\":[{\"label\":\"批量打印\",\"value\":\"batch\"},{\"label\":\"单张打印\",\"value\":\"single\"}],\"default\":\"single\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"待打印文件路径\",\"name\":\"file_path\",\"tip\":\"选择或输入待打印文件路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.batch_print.value == \'single\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"folder_path\",\"title\":\"待打印文件目录\",\"name\":\"folder_path\",\"tip\":\"选择或输入待打印文件目录\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.folder_path.show\",\"expression\":\"return $this.batch_print.value == \'batch\'\"}],\"required\":true},{\"types\":\"PrinterType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"printer_type\",\"title\":\"打印设置\",\"name\":\"printer_type\",\"tip\":\"设置打印相关参数\",\"options\":[{\"label\":\"自定义\",\"value\":\"custom\"},{\"label\":\"默认\",\"value\":\"default\"}],\"default\":\"default\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"printer_name\",\"title\":\"打印机名称\",\"name\":\"printer_name\",\"tip\":\"输入打印机名称,为空时使用默认打印机\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"PaperType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"paper_size\",\"title\":\"纸张尺寸\",\"name\":\"paper_size\",\"tip\":\"指定打印纸张尺寸\",\"options\":[{\"label\":\"A3\",\"value\":\"A3\"},{\"label\":\"A4\",\"value\":\"A4\"},{\"label\":\"LA4\",\"value\":\"LA4\"},{\"label\":\"A5\",\"value\":\"A5\"},{\"label\":\"B4\",\"value\":\"B4\"},{\"label\":\"B5\",\"value\":\"B5\"},{\"label\":\"C_SHEET\",\"value\":\"C_SHEET\"},{\"label\":\"D_SHEET\",\"value\":\"D_SHEET\"},{\"label\":\"默认\",\"value\":\"CUSTOM\"}],\"default\":\"A4\",\"dynamics\":[{\"key\":\"$this.paper_size.show\",\"expression\":\"return $this.printer_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_weight\",\"title\":\"纸张宽度\",\"name\":\"page_weight\",\"tip\":\"输入打印纸张宽度\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_weight.show\",\"expression\":\"return $this.paper_size.value == \'CUSTOM\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_height\",\"title\":\"纸张高度\",\"name\":\"page_height\",\"tip\":\"输入打印纸张高度\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_height.show\",\"expression\":\"return $this.paper_size.value == \'CUSTOM\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"print_num\",\"title\":\"打印份数\",\"name\":\"print_num\",\"tip\":\"输入打印份数,默认1份\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.print_num.show\",\"expression\":\"return $this.printer_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"OrientationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"orientation_type\",\"title\":\"打印版式\",\"name\":\"orientation_type\",\"tip\":\"\",\"options\":[{\"label\":\"水平方向\",\"value\":\"horizontal\"},{\"label\":\"垂直方向\",\"value\":\"vertical\"}],\"default\":\"vertical\",\"dynamics\":[{\"key\":\"$this.orientation_type.show\",\"expression\":\"return $this.printer_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"scale\",\"title\":\"缩放(%)\",\"name\":\"scale\",\"tip\":\"\",\"default\":100,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.scale.show\",\"expression\":\"return $this.printer_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"MarginType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"margin_type\",\"title\":\"边距(单位毫米)\",\"name\":\"margin_type\",\"tip\":\"\",\"options\":[{\"label\":\"自定义\",\"value\":\"custom\"},{\"label\":\"默认\",\"value\":\"default\"}],\"default\":\"default\",\"dynamics\":[{\"key\":\"$this.margin_type.show\",\"expression\":\"return $this.printer_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"left_margin\",\"title\":\"左\",\"name\":\"left_margin\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.left_margin.show\",\"expression\":\"return $this.margin_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"top_margin\",\"title\":\"上\",\"name\":\"top_margin\",\"tip\":\"\",\"default\":9.5,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.top_margin.show\",\"expression\":\"return $this.margin_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"right_margin\",\"title\":\"右\",\"name\":\"right_margin\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.right_margin.show\",\"expression\":\"return $this.margin_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"bottom_margin\",\"title\":\"下\",\"name\":\"bottom_margin\",\"tip\":\"\",\"default\":9.5,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.bottom_margin.show\",\"expression\":\"return $this.margin_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page\",\"title\":\"页码范围\",\"name\":\"page\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page.show\",\"expression\":\"return $this.file_type.value == \'excel\'\"}],\"required\":false}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"printer_status\",\"title\":\"打印结果\",\"tip\":\"保存打印结果到输出变量,数据类型为布尔值\"}],\"icon\":\"printer-print\",\"helpManual\":\"连接指定打印机进行文档打印\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(237,'ai/verify','VerifyCode.picture_code','{\"key\":\"VerifyCode.picture_code\",\"title\":\"通用数英验证码\",\"version\":\"1.0.0\",\"src\":\"astronverse.verifycode.verifycode.VerifyCode().picture_code\",\"comment\":\"识别浏览器中的验证码 @{picture_pick} ,返回识别结果 @{code_result}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"picture_pick\",\"title\":\"拾取验证码对象\",\"name\":\"picture_pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"PictureCodeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"code_type\",\"title\":\"验证码类型\",\"name\":\"code_type\",\"tip\":\"\",\"options\":[{\"label\":\"通用数英验证码1-4位\",\"value\":\"10110\"},{\"label\":\"通用数英验证码5-8位\",\"value\":\"10111\"},{\"label\":\"通用数英验证码(特殊)\",\"value\":\"10211\"}],\"default\":\"10110\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"input_flag\",\"title\":\"是否填写输入框\",\"name\":\"input_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"input_box\",\"title\":\"输入框对象\",\"name\":\"input_box\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.input_box.show\",\"expression\":\"return $this.input_flag.value == true\"}],\"required\":true,\"noInput\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"code_result\",\"title\":\"识别结果\",\"tip\":\"\"}],\"icon\":\"captcha-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(238,'ai/verify','VerifyCode.slider_code','{\"key\":\"VerifyCode.slider_code\",\"title\":\"通用滑块验证码\",\"version\":\"1.0.0\",\"src\":\"astronverse.verifycode.verifycode.VerifyCode().slider_code\",\"comment\":\"在背景图为 @{picture_pick} 的滑块验证码上,拖动滑块 @{slider_pick} 至指定位置,拖动距离为 @{drag_distance}\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"picture_pick\",\"title\":\"背景图片对象\",\"name\":\"picture_pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"slider_pick\",\"title\":\"拾取滑块对象\",\"name\":\"slider_pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"unmatched_flag\",\"title\":\"是否为变速验证码\",\"name\":\"unmatched_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"move_pic_pick\",\"title\":\"拾取滑动图片对象\",\"name\":\"move_pic_pick\",\"tip\":\"\",\"dynamics\":[{\"key\":\"$this.move_pic_pick.show\",\"expression\":\"return $this.unmatched_flag.value == true\"}],\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"mini_step\",\"title\":\"微调步长(px)\",\"name\":\"mini_step\",\"tip\":\"\",\"default\":5,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.mini_step.show\",\"expression\":\"return $this.unmatched_flag.value == true\"}],\"required\":true}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"drag_distance\",\"title\":\"移动距离\",\"tip\":\"\"}],\"icon\":\"captcha-slider\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(239,'ai/verify','VerifyCode.click_code','{\"key\":\"VerifyCode.click_code\",\"title\":\"通用点击验证码\",\"version\":\"1.0.0\",\"src\":\"astronverse.verifycode.verifycode.VerifyCode().click_code\",\"comment\":\"在背景图为 @{picture_pick} 的点击验证码上,点击对应位置\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"picture_pick\",\"title\":\"点击图片对象\",\"name\":\"picture_pick\",\"tip\":\"需要包括大的点击图片和小的指明顺序的图片\",\"required\":true,\"noInput\":true},{\"types\":\"HintPosition\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"hint_position\",\"title\":\"指示顺序的图片相对大图的位置\",\"name\":\"hint_position\",\"tip\":\"如果指示顺序的图片相对大图在下,则选择下,反之选择上\",\"options\":[{\"label\":\"下\",\"value\":\"bottom\"},{\"label\":\"上\",\"value\":\"top\"}],\"default\":\"bottom\",\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"click_positions\"}],\"icon\":\"captcha-click\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(240,'cv','CV.cv_click','{\"key\":\"CV.cv_click\",\"title\":\"点击图像\",\"version\":\"1.0.0\",\"src\":\"astronverse.vision.cv.CV().cv_click\",\"comment\":\"鼠标(@{btn_type})(@{btn_model})图像(@{input_data})(@{click_position})\",\"inputList\":[{\"types\":\"IMGPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"CV\"}},\"key\":\"input_data\",\"title\":\"目标图像\",\"name\":\"input_data\",\"tip\":\"支持从图形库中选择或拾取图像获取图像元素\",\"required\":true,\"noInput\":true},{\"types\":\"BtnType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"btn_type\",\"title\":\"鼠标按键\",\"name\":\"btn_type\",\"tip\":\"\",\"options\":[{\"label\":\"左键\",\"value\":\"left\"},{\"label\":\"中键\",\"value\":\"middle\"},{\"label\":\"右键\",\"value\":\"right\"}],\"default\":\"left\",\"required\":false},{\"types\":\"BtnModel\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"btn_model\",\"title\":\"点击方式\",\"name\":\"btn_model\",\"tip\":\"\",\"options\":[{\"label\":\"单击\",\"value\":\"click\"},{\"label\":\"双击\",\"value\":\"double_click\"}],\"default\":\"click\",\"required\":false},{\"types\":\"PositionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"click_position\",\"title\":\"点击位置\",\"name\":\"click_position\",\"tip\":\"选择鼠标点击位置\",\"options\":[{\"label\":\"中心点\",\"value\":\"center\"},{\"label\":\"随机位置\",\"value\":\"random\"},{\"label\":\"指定位置\",\"value\":\"specific\"}],\"default\":\"center\",\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"GRID\"},\"key\":\"specified_position\",\"title\":\"指定位置\",\"name\":\"specified_position\",\"tip\":\"\",\"default\":5,\"dynamics\":[{\"key\":\"$this.specified_position.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"horizontal_move\",\"title\":\"横向平移\",\"name\":\"horizontal_move\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.horizontal_move.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"vertical_move\",\"title\":\"纵向平移\",\"name\":\"vertical_move\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.vertical_move.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"SLIDER\"},\"key\":\"match_similarity\",\"title\":\"匹配相似度\",\"name\":\"match_similarity\",\"tip\":\"查找目标图像与当前页面的相似度阈值(最高匹配精确度为99%),精准匹配表示当前界面存在于截取的目标图像完全一致的时候才能匹配成功\",\"default\":0.95,\"required\":false},{\"types\":\"MoveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_type\",\"title\":\"鼠标移动方式\",\"name\":\"move_type\",\"tip\":\"\",\"options\":[{\"label\":\"匀速直线\",\"value\":\"linear\"},{\"label\":\"模拟人工\",\"value\":\"simulation\"},{\"label\":\"瞬时移动\",\"value\":\"teleportation\"}],\"default\":\"linear\",\"level\":\"advanced\",\"required\":false},{\"types\":\"Speed\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_speed\",\"title\":\"鼠标移动速度\",\"name\":\"move_speed\",\"tip\":\"\",\"options\":[{\"label\":\"慢速\",\"value\":\"slow\"},{\"label\":\"正常\",\"value\":\"normal\"},{\"label\":\"快速\",\"value\":\"fast\"}],\"default\":\"normal\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.move_speed.show\",\"expression\":\"return [\'linear\',\'simulation\'].includes($this.move_type.value)\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待图像出现时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待,默认为10秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[],\"icon\":\"click-image\",\"helpManual\":\"在当前(激活)窗口检索目标图片,并在窗口界面中点击该图片。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(241,'cv','CV.hover_image','{\"key\":\"CV.hover_image\",\"title\":\"鼠标悬浮在图像上\",\"version\":\"1.0.0\",\"src\":\"astronverse.vision.cv.CV().hover_image\",\"comment\":\"鼠标悬浮在图像(@{input_data})的(@{click_position})\",\"inputList\":[{\"types\":\"IMGPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"CV\"}},\"key\":\"input_data\",\"title\":\"目标图像\",\"name\":\"input_data\",\"tip\":\"支持从图形库中选择或拾取图像获取图像元素\",\"required\":true,\"noInput\":true},{\"types\":\"PositionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"click_position\",\"title\":\"悬浮位置\",\"name\":\"click_position\",\"tip\":\"\",\"options\":[{\"label\":\"中心点\",\"value\":\"center\"},{\"label\":\"随机位置\",\"value\":\"random\"},{\"label\":\"指定位置\",\"value\":\"specific\"}],\"default\":\"center\",\"required\":false},{\"types\":\"Any\",\"formType\":{\"type\":\"GRID\"},\"key\":\"specified_position\",\"title\":\"指定位置\",\"name\":\"specified_position\",\"tip\":\"按照九宫格切割图像,支持选择九宫格任意一个区域,选择区域则悬浮在目标图像对应区域的中心点位置\",\"default\":5,\"dynamics\":[{\"key\":\"$this.specified_position.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"horizontal_move\",\"title\":\"横向平移\",\"name\":\"horizontal_move\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.horizontal_move.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"vertical_move\",\"title\":\"纵向平移\",\"name\":\"vertical_move\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.vertical_move.show\",\"expression\":\"return $this.click_position.value == \'specific\'\"}],\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"SLIDER\"},\"key\":\"match_similarity\",\"title\":\"匹配相似度\",\"name\":\"match_similarity\",\"tip\":\"查找目标图像与当前页面的相似度阈值(最高匹配精确度为99%),精准匹配表示当前界面存在于截取的目标图像完全一致的时候才能匹配成功\",\"default\":0.95,\"required\":false},{\"types\":\"MoveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_type\",\"title\":\"鼠标移动方式\",\"name\":\"move_type\",\"tip\":\"\",\"options\":[{\"label\":\"匀速直线\",\"value\":\"linear\"},{\"label\":\"模拟人工\",\"value\":\"simulation\"},{\"label\":\"瞬时移动\",\"value\":\"teleportation\"}],\"default\":\"linear\",\"level\":\"advanced\",\"required\":false},{\"types\":\"Speed\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"move_speed\",\"title\":\"鼠标移动速度\",\"name\":\"move_speed\",\"tip\":\"\",\"options\":[{\"label\":\"慢速\",\"value\":\"slow\"},{\"label\":\"正常\",\"value\":\"normal\"},{\"label\":\"快速\",\"value\":\"fast\"}],\"default\":\"normal\",\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.move_speed.show\",\"expression\":\"return [\'linear\',\'simulation\'].includes($this.move_type.value)\"}],\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待图像出现时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待,默认为10秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[],\"icon\":\"mouse-hover-image\",\"helpManual\":\"在当前(激活)窗口检索图像,并将鼠标移动到该图像的指定位置上。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(242,'cv','CV.is_image_exist','{\"key\":\"CV.is_image_exist\",\"title\":\"IF 图像存在\",\"version\":\"1.0.0\",\"src\":\"astronverse.vision.cv.CV().is_image_exist\",\"comment\":\"判断图像(@{input_data})在当前界面(@{exist_type})\",\"inputList\":[{\"types\":\"IMGPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"CV\"}},\"key\":\"input_data\",\"title\":\"目标图像\",\"name\":\"input_data\",\"tip\":\"支持从图形库中选择或拾取图像获取图像元素\",\"required\":true,\"noInput\":true},{\"types\":\"ExistType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_type\",\"title\":\"判断类型\",\"name\":\"exist_type\",\"tip\":\"判断图像存在或不存在,分别执行对应代码块内容\",\"options\":[{\"label\":\"存在\",\"value\":\"exist\"},{\"label\":\"不存在\",\"value\":\"not_exist\"}],\"default\":\"exist\",\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"SLIDER\"},\"key\":\"match_similarity\",\"title\":\"匹配相似度\",\"name\":\"match_similarity\",\"tip\":\"查找目标图像与当前页面的相似度阈值(最高匹配精确度为99%),精准匹配表示当前界面存在于截取的目标图像完全一致的时候才能匹配成功\",\"default\":0.95,\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待图像时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待,默认为10秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[],\"icon\":\"if-image-exists\",\"helpManual\":\"在当前(激活)窗口检索图像,判断是否存在。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(243,'cv','CV.wait_image','{\"key\":\"CV.wait_image\",\"title\":\"等待图像\",\"version\":\"1.0.0\",\"src\":\"astronverse.vision.cv.CV().wait_image\",\"comment\":\"等待图像(@{input_data})(@{wait_type})\",\"inputList\":[{\"types\":\"IMGPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"CV\"}},\"key\":\"input_data\",\"title\":\"目标图像\",\"name\":\"input_data\",\"tip\":\"支持从图形库中选择或拾取图像获取图像元素\",\"required\":true,\"noInput\":true},{\"types\":\"WaitType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"wait_type\",\"title\":\"等待类型\",\"name\":\"wait_type\",\"tip\":\"等待图像出现或消失\",\"options\":[{\"label\":\"图像出现\",\"value\":\"appear\"},{\"label\":\"图像消失\",\"value\":\"disappear\"}],\"default\":\"appear\",\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待,默认为10秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"SLIDER\"},\"key\":\"match_similarity\",\"title\":\"匹配相似度\",\"name\":\"match_similarity\",\"tip\":\"查找目标图像与当前页面的相似度阈值(最高匹配精确度为99%),精准匹配表示当前界面存在于截取的目标图像完全一致的时候才能匹配成功\",\"default\":0.95,\"required\":false}],\"outputList\":[{\"types\":\"Bool\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"image_wait_result\",\"title\":\"等待结果\",\"tip\":\"目标图像是否出现/消失,在等待时间内出现/消失为true,反之为false\"}],\"icon\":\"wait-image\",\"helpManual\":\"等待目标图像出现或消失,再继续执行流程。\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(244,'cv','CV.image_input','{\"key\":\"CV.image_input\",\"title\":\"图像输入框输入\",\"version\":\"1.0.0\",\"src\":\"astronverse.vision.cv.CV().image_input\",\"comment\":\"拾取图像输入框(@{input_data})以(@{input_type})输入(@{input_content})\",\"inputList\":[{\"types\":\"IMGPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"CV\"}},\"key\":\"input_data\",\"title\":\"目标图像\",\"name\":\"input_data\",\"tip\":\"支持从图形库中选择或拾取图像获取图像元素\",\"required\":true,\"noInput\":true},{\"types\":\"InputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"input_type\",\"title\":\"输入方式\",\"name\":\"input_type\",\"tip\":\"\",\"options\":[{\"label\":\"字符输入\",\"value\":\"text\"},{\"label\":\"剪切板输入\",\"value\":\"clip\"}],\"default\":\"text\",\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"input_content\",\"title\":\"输入内容\",\"name\":\"input_content\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.input_content.show\",\"expression\":\"return $this.input_type.value == \'text\'\"}],\"required\":true},{\"types\":\"Simulate_flag\",\"formType\":{\"type\":\"SWITCH\"},\"key\":\"simulate_flag\",\"title\":\"模拟人工输入\",\"name\":\"simulate_flag\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":\"yes\"},{\"label\":\"否\",\"value\":\"no\"}],\"default\":\"yes\",\"dynamics\":[{\"key\":\"$this.simulate_flag.show\",\"expression\":\"return $this.input_type.value == \'text\'\"}],\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"interval\",\"title\":\"输入间隔\",\"name\":\"interval\",\"tip\":\"\",\"default\":0.1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.interval.show\",\"expression\":\"return $this.input_type.value == \'text\'\"}],\"required\":false},{\"types\":\"Float\",\"formType\":{\"type\":\"SLIDER\"},\"key\":\"match_similarity\",\"title\":\"匹配相似度\",\"name\":\"match_similarity\",\"tip\":\"\",\"default\":0.95,\"required\":false},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待图像出现时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待,默认为10秒\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false}],\"outputList\":[],\"icon\":\"image-input-box\",\"helpManual\":\"点击目标输入框的图像,并输入内容\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(245,'desktop/desktop.window','Window.exist','{\"key\":\"Window.exist\",\"title\":\"IF 窗口存在\",\"version\":\"1.0.0\",\"src\":\"astronverse.window.window.Window().exist\",\"comment\":\"判断拾取窗口(@{pick})是否(@{check_type}),是就执行以下操作\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WINDOW\"}},\"key\":\"pick\",\"title\":\"窗口拾取\",\"name\":\"pick\",\"tip\":\"选择需要判断是否存在的窗口对象\",\"required\":true,\"noInput\":true},{\"types\":\"WindowExistType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"check_type\",\"title\":\"判断方式\",\"name\":\"check_type\",\"tip\":\"选择判断方式,默认为存在\",\"options\":[{\"label\":\"存在\",\"value\":\"exist\"},{\"label\":\"不存在\",\"value\":\"not_exist\"}],\"default\":\"exist\",\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待时间(秒)\",\"name\":\"wait_time\",\"tip\":\"等待判断结果的时间\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"check-window-exists\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(246,'desktop/desktop.window','Window.top','{\"key\":\"Window.top\",\"title\":\"置顶窗口\",\"version\":\"1.0.0\",\"src\":\"astronverse.window.window.Window().top\",\"comment\":\"置顶窗口对象(@{pick})\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WINDOW\"}},\"key\":\"pick\",\"title\":\"窗口拾取\",\"name\":\"pick\",\"tip\":\"拾取需要置顶的窗口对象\",\"required\":true,\"noInput\":true}],\"outputList\":[],\"icon\":\"pin-window\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(247,'desktop/desktop.window','Window.close','{\"key\":\"Window.close\",\"title\":\"关闭窗口\",\"version\":\"1.0.0\",\"src\":\"astronverse.window.window.Window().close\",\"comment\":\"关闭桌面软件窗口对象(@{pick})\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WINDOW\"}},\"key\":\"pick\",\"title\":\"窗口拾取\",\"name\":\"pick\",\"tip\":\"拾取需要置顶的窗口对象\",\"required\":true,\"noInput\":true}],\"outputList\":[],\"icon\":\"close-window\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(248,'desktop/desktop.window','Window.set_size','{\"key\":\"Window.set_size\",\"title\":\"调整窗口大小\",\"version\":\"1.0.0\",\"src\":\"astronverse.window.window.Window().set_size\",\"comment\":\"调整拾取到的窗口(@{pick})大小,通过(@{size_type:自定义})的形式将窗口宽度和高度分别设置为(@{width}) (@{height})\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WINDOW\"}},\"key\":\"pick\",\"title\":\"窗口拾取\",\"name\":\"pick\",\"tip\":\"拾取需要调整大小的桌面软件窗口\",\"required\":true,\"noInput\":true},{\"types\":\"WindowSizeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"size_type\",\"title\":\"调整方式\",\"name\":\"size_type\",\"tip\":\"\",\"options\":[{\"label\":\"自定义\",\"value\":\"custom\"},{\"label\":\"最大化\",\"value\":\"max\"},{\"label\":\"最小化\",\"value\":\"min\"}],\"default\":\"max\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"width\",\"title\":\"窗口宽度\",\"name\":\"width\",\"tip\":\"设置指定窗口宽度值,单位为屏幕的分辨率像素px,一般为0-9999之间的数值\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.width.show\",\"expression\":\"return $this.size_type.value == \'custom\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"height\",\"title\":\"窗口高度\",\"name\":\"height\",\"tip\":\"设置指定窗口高度值,单位为屏幕的分辨率像素px,一般为0-9999之间的数值\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.height.show\",\"expression\":\"return $this.size_type.value == \'custom\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"resize-window\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(249,'desktop','WinEle.click_element','{\"key\":\"WinEle.click_element\",\"title\":\"点击元素(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().click_element\",\"comment\":\"@{click_type} 拾取到的元素 @{pick} ,最长等待 @{wait_time} 秒直到此元素出现\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"ELEMENT\"}},\"key\":\"pick\",\"title\":\"元素拾取\",\"name\":\"pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"MouseClickButton\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"click_button\",\"title\":\"点击按钮\",\"name\":\"click_button\",\"tip\":\"\",\"options\":[{\"label\":\"左键\",\"value\":\"left\"},{\"label\":\"右键\",\"value\":\"right\"},{\"label\":\"中键\",\"value\":\"middle\"}],\"default\":\"left\",\"required\":true},{\"types\":\"MouseClickType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"click_type\",\"title\":\"点击类型\",\"name\":\"click_type\",\"tip\":\"\",\"options\":[{\"label\":\"单击\",\"value\":\"click\"},{\"label\":\"双击\",\"value\":\"double_click\"}],\"default\":\"click\",\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待元素出现时间\",\"name\":\"wait_time\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"horizontals_offset\",\"title\":\"横向偏移量\",\"name\":\"horizontals_offset\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"verticals_offset\",\"title\":\"纵向偏移量\",\"name\":\"verticals_offset\",\"tip\":\"\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MouseClickKeyboard\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"keyboard_input\",\"title\":\"键盘辅助输入\",\"name\":\"keyboard_input\",\"tip\":\"点击时同时按下键盘上的某些键\",\"options\":[{\"label\":\"不使用\",\"value\":\"none\"},{\"label\":\"Alt键\",\"value\":\"alt\"},{\"label\":\"Ctrl键\",\"value\":\"ctrl\"},{\"label\":\"Shift键\",\"value\":\"shift\"},{\"label\":\"Win键\",\"value\":\"win\"}],\"default\":\"none\",\"required\":true}],\"outputList\":[],\"icon\":\"click-element-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(250,'desktop','WinEle.screenshot_element','{\"key\":\"WinEle.screenshot_element\",\"title\":\"元素截图(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().screenshot_element\",\"comment\":\"截图拾取到的元素 @{pick} ,并保存至文件夹 @{file_path}\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"ELEMENT\"}},\"key\":\"pick\",\"title\":\"元素拾取\",\"name\":\"pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"文件保存路径\",\"name\":\"file_path\",\"tip\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文件名\",\"name\":\"file_name\",\"tip\":\"\",\"default\":\"桌面元素截图\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_type\",\"title\":\"文件同名时处理方式\",\"name\":\"exist_type\",\"tip\":\"\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":true}],\"outputList\":[],\"icon\":\"element-screenshot-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(251,'desktop','WinEle.hover_element','{\"key\":\"WinEle.hover_element\",\"title\":\"鼠标悬停元素(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().hover_element\",\"comment\":\"鼠标悬停拾取到的元素 @{pick} ,并设置等待时间 @{wait_time} 秒\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"ELEMENT\"}},\"key\":\"pick\",\"title\":\"元素拾取\",\"name\":\"pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待元素出现时间\",\"name\":\"wait_time\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"mouse-hover-element-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(252,'desktop','WinEle.input_text_element','{\"key\":\"WinEle.input_text_element\",\"title\":\"填写输入框(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().input_text_element\",\"comment\":\"填写输入框 @{pick} ,并设置输入类型为 @{input_type} ,输入内容为 @{text}\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"ELEMENT\"}},\"key\":\"pick\",\"title\":\"元素拾取\",\"name\":\"pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"ElementInputType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"input_type\",\"title\":\"输入类型\",\"name\":\"input_type\",\"tip\":\"\",\"options\":[{\"label\":\"键盘输入\",\"value\":\"keyboard\"},{\"label\":\"剪贴板输入\",\"value\":\"clipboard\"}],\"default\":\"keyboard\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"输入内容\",\"name\":\"text\",\"tip\":\"\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.text.show\",\"expression\":\"return $this.input_type.value == \'keyboard\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"clear_first\",\"title\":\"是否清空输入框内容\",\"name\":\"clear_first\",\"tip\":\"\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待元素出现时间\",\"name\":\"wait_time\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"fill-input-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(253,'desktop','WinEle.get_element_text','{\"key\":\"WinEle.get_element_text\",\"title\":\"获取元素文本(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().get_element_text\",\"comment\":\"从元素 @{pick} 提取文本并保存到 @{ele_text}\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"ELEMENT\"}},\"key\":\"pick\",\"title\":\"元素拾取\",\"name\":\"pick\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待元素出现时间\",\"name\":\"wait_time\",\"tip\":\"\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"ele_text\",\"title\":\"元素文本\",\"tip\":\"\"}],\"icon\":\"get-element-text-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(254,'desktop','WinEle.similar','{\"key\":\"WinEle.similar\",\"title\":\"获取相似元素列表(桌面)\",\"version\":\"1.0.0\",\"src\":\"astronverse.winelement.winele.WinEle().similar\",\"comment\":\"获取拾取到的元素 @{pick} 相似的元素,并将相似元素数组输出至 @{get_similar_ele}\",\"inputList\":[{\"types\":\"WinPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WinPick\"}},\"key\":\"pick\",\"title\":\"相识元素拾取\",\"name\":\"pick\",\"tip\":\"在桌面上拾取不同位置的两个相似元素\",\"required\":true,\"noInput\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"wait_time\",\"title\":\"等待元素出现时间(秒)\",\"name\":\"wait_time\",\"tip\":\"超过该时间停止等待\",\"default\":10,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"get_similar_ele\",\"title\":\"元素信息\",\"tip\":\"\"}],\"icon\":\"get-similar-elements-win\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(255,'','Docx.open_document','{\"key\":\"Docx.open_document\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().open_document\",\"inputList\":[{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"name\":\"file_path\",\"default\":\"\",\"required\":true},{\"types\":\"ApplicationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"default_application\",\"name\":\"default_application\",\"options\":[{\"label\":\"Word\",\"value\":\"Word\"},{\"label\":\"WPS\",\"value\":\"WPS\"},{\"label\":\"默认软件\",\"value\":\"Default\"}],\"default\":\"Default\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"visible_flag\",\"name\":\"visible_flag\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"EncodingType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"encoding\",\"name\":\"encoding\",\"options\":[{\"label\":\"utf-8\",\"value\":\"utf-8\"},{\"label\":\"gbk\",\"value\":\"gbk\"}],\"default\":\"utf-8\",\"level\":\"advanced\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"open_pwd_flag\",\"name\":\"open_pwd_flag\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"open_pwd\",\"name\":\"open_pwd\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.open_pwd.show\",\"expression\":\"return $this.open_pwd_flag.value == true\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"write_pwd_flag\",\"name\":\"write_pwd_flag\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"write_pwd\",\"name\":\"write_pwd\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.write_pwd.show\",\"expression\":\"return $this.write_pwd_flag.value == true\"}],\"required\":false}],\"outputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_obj\"}]}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(256,'','Docx.read_document_content','{\"key\":\"Docx.read_document_content\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().read_document_content\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"name\":\"doc\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"name\":\"select_range\",\"options\":[{\"label\":\"整个文档\",\"value\":\"all\"},{\"label\":\"选中区域\",\"value\":\"selected\"}],\"default\":\"all\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_data\"}]}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(257,'document/document.Word','Docx.create_docx','{\"key\":\"Docx.create_docx\",\"title\":\"新建Word文档\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().create_docx\",\"comment\":\"新建Word文档,保存到路径为 @{file_path} 的位置,文件名为 @{file_name} ,返回Word对象 @{doc_obj} 和保存路径 @{doc_create_path}\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"文档保存文件夹\",\"name\":\"file_path\",\"tip\":\"填写Word文档的保存文件夹路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文档名称\",\"name\":\"file_name\",\"tip\":\"填写Word文档的名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ApplicationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"default_application\",\"title\":\"驱动方式\",\"name\":\"default_application\",\"tip\":\"选择Word文档的打开方式,如Word或WPS\",\"options\":[{\"label\":\"Word\",\"value\":\"Word\"},{\"label\":\"WPS\",\"value\":\"WPS\"},{\"label\":\"默认软件\",\"value\":\"Default\"}],\"default\":\"Word\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"visible_flag\",\"title\":\"是否可见\",\"name\":\"visible_flag\",\"tip\":\"是否显示Word文档窗口\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"required\":true}],\"outputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_obj\",\"title\":\"Word对象\",\"tip\":\"\"},{\"types\":\"PATH\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_create_path\",\"title\":\"文档保存路径\",\"tip\":\"保存的Word文档的完整路径\"}],\"icon\":\"word-new-document\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(258,'document/document.Word','Docx.save_docx','{\"key\":\"Docx.save_docx\",\"title\":\"保存Word文档\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().save_docx\",\"comment\":\"保存Word文档对象 @{doc} ,保存方式为 @{save_type}, 并输出保存路径 @{save_file_path}\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"保存类型\",\"name\":\"save_type\",\"tip\":\"选择保存类型,如保存、另存为、不保存\",\"options\":[{\"label\":\"保存\",\"value\":\"save\"},{\"label\":\"另存为\",\"value\":\"save_as\"},{\"label\":\"不保存\",\"value\":\"abort\"}],\"default\":\"save\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"文档路径\",\"name\":\"file_path\",\"tip\":\"填写Word文档的保存路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"文档名称\",\"name\":\"file_name\",\"tip\":\"填写Word文档的名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return $this.save_type.value == \'save_as\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"close_flag\",\"title\":\"是否关闭文档\",\"name\":\"close_flag\",\"tip\":\"选择是否关闭文档窗口\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[{\"types\":\"PATH\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"save_file_path\",\"title\":\"保存路径\",\"tip\":\"保存的Word文档的完整路径\"}],\"icon\":\"word-save-document\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(259,'document/document.Word','Docx.close_docx','{\"key\":\"Docx.close_docx\",\"title\":\"关闭Word文档\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().close_docx\",\"comment\":\"关闭Word文档对象 @{doc}\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CloseRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"close_range_flag\",\"title\":\"关闭文档范围\",\"name\":\"close_range_flag\",\"tip\":\"选择关闭文档的范围,如关闭当前文档或关闭所有文档\",\"options\":[{\"label\":\"关闭当前文档\",\"value\":\"one\"},{\"label\":\"关闭所有文档\",\"value\":\"all\"}],\"default\":\"one\",\"required\":true},{\"types\":\"SaveType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"保存类型\",\"name\":\"save_type\",\"tip\":\"选择保存类型,如保存、另存为、不保存\",\"options\":[{\"label\":\"保存\",\"value\":\"save\"},{\"label\":\"另存为\",\"value\":\"save_as\"},{\"label\":\"不保存\",\"value\":\"abort\"}],\"default\":\"save\",\"dynamics\":[{\"key\":\"$this.save_type.show\",\"expression\":\"return $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"file_path\",\"title\":\"另存为文档路径\",\"name\":\"file_path\",\"tip\":\"填写Word文档的保存路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.file_path.show\",\"expression\":\"return $this.save_type.value == \'save_as\' && $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"file_name\",\"title\":\"另存为文档名称\",\"name\":\"file_name\",\"tip\":\"填写Word文档的名称\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.file_name.show\",\"expression\":\"return $this.save_type.value == \'save_as\' && $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"FileExistenceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"exist_handle_type\",\"title\":\"存在同名文件处理方式\",\"name\":\"exist_handle_type\",\"tip\":\"选择存在文件处理方式,支持覆盖原有文件、创建文件副本、取消保存操作\",\"options\":[{\"label\":\"覆盖原有文件\",\"value\":\"overwrite\"},{\"label\":\"创建文件副本\",\"value\":\"rename\"},{\"label\":\"取消保存操作\",\"value\":\"cancel\"}],\"default\":\"rename\",\"dynamics\":[{\"key\":\"$this.exist_handle_type.show\",\"expression\":\"return $this.close_range_flag.value == \'one\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"pkill_flag\",\"title\":\"是否结束Word或WPS进程\",\"name\":\"pkill_flag\",\"tip\":\"选择是否结束Word或WPS进程\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.pkill_flag.show\",\"expression\":\"return $this.close_range_flag.value == \'all\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-close-document\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(260,'document/document.Word','Docx.insert_docx','{\"key\":\"Docx.insert_docx\",\"title\":\"插入文本到Word文档\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().insert_docx\",\"comment\":\"向Word文档对象 @{doc} 插入文本 @{text}\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"插入文本\",\"name\":\"text\",\"tip\":\"填写要插入的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"enter_flag\",\"title\":\"是否在插入前换行\",\"name\":\"enter_flag\",\"tip\":\"选择是否在插入前换行\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"font_size\",\"title\":\"字体大小\",\"name\":\"font_size\",\"tip\":\"填写字体大小\",\"default\":12,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"bold_flag\",\"title\":\"是否加粗\",\"name\":\"bold_flag\",\"tip\":\"选择是否加粗\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"italic_flag\",\"title\":\"是否斜体\",\"name\":\"italic_flag\",\"tip\":\"选择是否斜体\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"UnderLineStyle\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"underline_flag\",\"title\":\"是否添加下划线\",\"name\":\"underline_flag\",\"tip\":\"选择是否添加下划线\",\"options\":[{\"label\":\"无下划线\",\"value\":0},{\"label\":\"有下划线\",\"value\":1}],\"default\":0,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"font_name\",\"title\":\"字体名称\",\"name\":\"font_name\",\"tip\":\"填写字体名称\",\"default\":\"宋体\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":false},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_COLOR\"},\"key\":\"font_color\",\"title\":\"字体颜色\",\"name\":\"font_color\",\"tip\":\"选择字体颜色\",\"default\":\"0,0,0\",\"required\":false}],\"outputList\":[],\"icon\":\"word-insert-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(261,'document/document.Word','Docx.select_text','{\"key\":\"Docx.select_text\",\"title\":\"选择Word文本\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().select_text\",\"comment\":\"从Word文档对象 @{doc} 中选择文本\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SelectTextType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_type\",\"title\":\"选择文本方式\",\"name\":\"select_type\",\"tip\":\"支持选择全文、段落、行\",\"options\":[{\"label\":\"全文\",\"value\":\"all\"},{\"label\":\"段落\",\"value\":\"paragraph\"},{\"label\":\"行\",\"value\":\"row\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_start\",\"title\":\"起始段落号\",\"name\":\"p_start\",\"tip\":\"输入起始段落号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_start.show\",\"expression\":\"return $this.select_type.value == \'paragraph\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_end\",\"title\":\"结束段落号\",\"name\":\"p_end\",\"tip\":\"输入结束段落号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_end.show\",\"expression\":\"return $this.select_type.value == \'paragraph\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"r_start\",\"title\":\"起始行号\",\"name\":\"r_start\",\"tip\":\"输入起始行号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.r_start.show\",\"expression\":\"return $this.select_type.value == \'row\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"r_end\",\"title\":\"结束行号\",\"name\":\"r_end\",\"tip\":\"输入结束行号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.r_end.show\",\"expression\":\"return $this.select_type.value == \'row\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-select-text\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(262,'document/document.Word','Docx.get_cursor_position','{\"key\":\"Docx.get_cursor_position\",\"title\":\"定位Word光标\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().get_cursor_position\",\"comment\":\"在Word文档对象 @{doc} 中定位光标位置\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CursorPointerType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"by\",\"title\":\"光标定位方式\",\"name\":\"by\",\"tip\":\"选择定位到全文、段落、行、文本\",\"options\":[{\"label\":\"文档\",\"value\":\"all\"},{\"label\":\"段落\",\"value\":\"paragraph\"},{\"label\":\"行\",\"value\":\"row\"},{\"label\":\"文本内容\",\"value\":\"content\"}],\"default\":\"all\",\"required\":true},{\"types\":\"CursorPositionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"pos\",\"title\":\"光标相对位置\",\"name\":\"pos\",\"tip\":\"所选内容之前/所选内容之后\",\"options\":[{\"label\":\"所选内容之前\",\"value\":\"head\"},{\"label\":\"所选内容之后\",\"value\":\"tail\"}],\"default\":\"head\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"content\",\"title\":\"文本内容\",\"name\":\"content\",\"tip\":\"如果选择定位文本,输入文本内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.content.show\",\"expression\":\"return $this.by.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"c_idx\",\"title\":\"文本序号\",\"name\":\"c_idx\",\"tip\":\"输入定位文本的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.c_idx.show\",\"expression\":\"return $this.by.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_idx\",\"title\":\"段落号\",\"name\":\"p_idx\",\"tip\":\"输入定位段落号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_idx.show\",\"expression\":\"return $this.by.value == \'paragraph\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"r_idx\",\"title\":\"行号\",\"name\":\"r_idx\",\"tip\":\"选择定位的行号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.r_idx.show\",\"expression\":\"return $this.by.value == \'row\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-position-cursor\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(263,'document/document.Word','Docx.move_cursor','{\"key\":\"Docx.move_cursor\",\"title\":\"移动Word光标\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().move_cursor\",\"comment\":\"在Word文档对象 @{doc} 中移动光标\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"MoveDirectionType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"direction\",\"title\":\"光标移动方向\",\"name\":\"direction\",\"tip\":\"可选择向上、向下、向左、向右\",\"options\":[{\"label\":\"向上\",\"value\":\"up\"},{\"label\":\"向下\",\"value\":\"down\"},{\"label\":\"向左\",\"value\":\"left\"},{\"label\":\"向右\",\"value\":\"right\"}],\"default\":\"up\",\"required\":true},{\"types\":\"MoveUpDownType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"unitupdown\",\"title\":\"上下移动单位\",\"name\":\"unitupdown\",\"tip\":\"可选择行、段落\",\"options\":[{\"label\":\"段落\",\"value\":\"paragraph\"},{\"label\":\"行\",\"value\":\"row\"}],\"default\":\"row\",\"dynamics\":[{\"key\":\"$this.unitupdown.show\",\"expression\":\"return [\'up\', \'down\'].includes($this.direction.value)\"}],\"required\":true},{\"types\":\"MoveLeftRightType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"unitleftright\",\"title\":\"左右移动单位\",\"name\":\"unitleftright\",\"tip\":\"可选择字符、单词\",\"options\":[{\"label\":\"字符\",\"value\":\"character\"},{\"label\":\"单词\",\"value\":\"word\"}],\"default\":\"character\",\"dynamics\":[{\"key\":\"$this.unitleftright.show\",\"expression\":\"return [\'left\', \'right\'].includes($this.direction.value)\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"distance\",\"title\":\"移动距离\",\"name\":\"distance\",\"tip\":\"输入移动的距离\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"with_shift\",\"title\":\"是否按下Shift\",\"name\":\"with_shift\",\"tip\":\"选择是否按下Shift\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[],\"icon\":\"word-move-cursor\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(264,'document/document.Word','Docx.insert_sep','{\"key\":\"Docx.insert_sep\",\"title\":\"Word插入页/段落\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().insert_sep\",\"comment\":\"在Word文档对象 @{doc} 中插入页/段落\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"InsertionType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"sep_type\",\"title\":\"插入类型\",\"name\":\"sep_type\",\"tip\":\"选择页、段落\",\"options\":[{\"label\":\"页\",\"value\":\"page\"},{\"label\":\"段落\",\"value\":\"paragraph\"}],\"default\":\"paragraph\",\"required\":true}],\"outputList\":[],\"icon\":\"word-insert-page\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(265,'document/document.Word','Docx.insert_hyperlink','{\"key\":\"Docx.insert_hyperlink\",\"title\":\"Word插入超链接\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().insert_hyperlink\",\"comment\":\"在Word文档对象 @{doc} 中插入超链接\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"url\",\"title\":\"链接\",\"name\":\"url\",\"tip\":\"输入插入的链接\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"display\",\"title\":\"显示文本\",\"name\":\"display\",\"tip\":\"输入要显示的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-insert-hyperlink\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(266,'document/document.Word','Docx.insert_img','{\"key\":\"Docx.insert_img\",\"title\":\"Word插入图片\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().insert_img\",\"comment\":\"在Word文档对象 @{doc} 中,从路径 @{img_path} 或剪贴板插入图片\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"InsertImgType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"img_from\",\"title\":\"图片来源\",\"name\":\"img_from\",\"tip\":\"选择通过文件路径、剪贴板\",\"options\":[{\"label\":\"文件\",\"value\":\"file\"},{\"label\":\"剪贴板\",\"value\":\"clipboard\"}],\"default\":\"file\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"img_path\",\"title\":\"文件路径\",\"name\":\"img_path\",\"tip\":\"输入文件路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.img_path.show\",\"expression\":\"return $this.img_from.value == \'file\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"scale\",\"title\":\"缩放比例(%)\",\"name\":\"scale\",\"tip\":\"默认为100%,可以输入其他值,不需要输入百分号\",\"default\":100,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"newline\",\"title\":\"是否换行\",\"name\":\"newline\",\"tip\":\"选择插入前是否换行\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true}],\"outputList\":[],\"icon\":\"word-insert-image\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(267,'document/document.Word','Docx.read_table','{\"key\":\"Docx.read_table\",\"title\":\"Word读取表格\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().read_table\",\"comment\":\"在Word文档对象 @{doc} 中,读取表格内容并返回表格数据对象 @{table_content}\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SearchTableType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"search_type\",\"title\":\"查找表格方式\",\"name\":\"search_type\",\"tip\":\"选择查找表格方式\",\"options\":[{\"label\":\"文本\",\"value\":\"text\"},{\"label\":\"序号\",\"value\":\"idx\"}],\"default\":\"idx\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"idx\",\"title\":\"序号\",\"name\":\"idx\",\"tip\":\"输入查找表格的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"text\",\"title\":\"文本\",\"name\":\"text\",\"tip\":\"输入查找表格的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.text.show\",\"expression\":\"return $this.search_type.value == \'text\'\"}],\"required\":true}],\"outputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"table_content\",\"title\":\"表格内容\",\"tip\":\"\"}],\"icon\":\"word-read-table\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(268,'document/document.Word','Docx.insert_table','{\"key\":\"Docx.insert_table\",\"title\":\"Word插入表格\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().insert_table\",\"comment\":\"在Word文档对象 @{doc} 中,插入表格内容\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"List\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"table_content\",\"title\":\"表格内容\",\"name\":\"table_content\",\"tip\":\"要创建的表格内容,格式为二维数组或者为读取的Excel表格内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"TableBehavior\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"table_behavior\",\"title\":\"表格大小\",\"name\":\"table_behavior\",\"tip\":\"选择默认还是适应大小\",\"options\":[{\"label\":\"默认\",\"value\":0},{\"label\":\"适应大小\",\"value\":1}],\"default\":0,\"required\":true},{\"types\":\"RowAlignment\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"alignment\",\"title\":\"文本左右位置\",\"name\":\"alignment\",\"tip\":\"选择左对齐、居中对齐、右对齐\",\"options\":[{\"label\":\"左对齐\",\"value\":0},{\"label\":\"居中对齐\",\"value\":1},{\"label\":\"右对齐\",\"value\":2}],\"default\":0,\"required\":true},{\"types\":\"VerticalAlignment\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"v_alignment\",\"title\":\"文本垂直位置\",\"name\":\"v_alignment\",\"tip\":\"选择顶部对齐、居中对齐、底部对齐\",\"options\":[{\"label\":\"顶部对齐\",\"value\":0},{\"label\":\"居中对齐\",\"value\":1},{\"label\":\"底部对齐\",\"value\":3}],\"default\":0,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"border\",\"title\":\"表格边框\",\"name\":\"border\",\"tip\":\"选择是否有边框\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"if_change_font\",\"title\":\"是否改变字体\",\"name\":\"if_change_font\",\"tip\":\"选择是否改变字体\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"font_size\",\"title\":\"大小\",\"name\":\"font_size\",\"tip\":\"选择字体大小\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.font_size.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_COLOR\"},\"key\":\"font_color\",\"title\":\"颜色\",\"name\":\"font_color\",\"tip\":\"选择字体颜色\",\"dynamics\":[{\"key\":\"$this.font_color.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"font_set\",\"title\":\"字体\",\"name\":\"font_set\",\"tip\":\"选择字体\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.font_set.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"font_bold\",\"title\":\"加粗\",\"name\":\"font_bold\",\"tip\":\"选择是否加粗\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.font_bold.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"font_italic\",\"title\":\"斜体\",\"name\":\"font_italic\",\"tip\":\"选择是否斜体\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.font_italic.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"UnderLineStyle\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"underline\",\"title\":\"下划线\",\"name\":\"underline\",\"tip\":\"选择是否下划线\",\"options\":[{\"label\":\"无下划线\",\"value\":0},{\"label\":\"有下划线\",\"value\":1}],\"default\":0,\"dynamics\":[{\"key\":\"$this.underline.show\",\"expression\":\"return $this.if_change_font.value == true\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"newline\",\"title\":\"在新的一行插入\",\"name\":\"newline\",\"tip\":\"选择是否在新的一行插入\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[],\"icon\":\"word-insert-table\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(269,'document/document.Word','Docx.delete','{\"key\":\"Docx.delete\",\"title\":\"Word删除内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().delete\",\"comment\":\"在Word文档对象 @{doc} 中,删除内容\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"DeleteMode\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"delete_mode\",\"title\":\"删除方式\",\"name\":\"delete_mode\",\"tip\":\"选择删除方式\",\"options\":[{\"label\":\"全部\",\"value\":\"all\"},{\"label\":\"指定文本\",\"value\":\"content\"},{\"label\":\"指定范围\",\"value\":\"range\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_str\",\"title\":\"文本\",\"name\":\"delete_str\",\"tip\":\"选择要删除的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_str.show\",\"expression\":\"return $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_idx\",\"title\":\"序号\",\"name\":\"delete_idx\",\"tip\":\"选择删除第几个文本\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_idx.show\",\"expression\":\"return $this.str_delete_all.value == false && $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"str_delete_all\",\"title\":\"删除所有找到的文本\",\"name\":\"str_delete_all\",\"tip\":\"选择是否删除所有找到的文本\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.str_delete_all.show\",\"expression\":\"return $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_start\",\"title\":\"起始段落\",\"name\":\"p_start\",\"tip\":\"输入起始段落\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_start.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"c_start\",\"title\":\"起始字符\",\"name\":\"c_start\",\"tip\":\"输入起始字符\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.c_start.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_end\",\"title\":\"结束段落\",\"name\":\"p_end\",\"tip\":\"输入结束段落\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_end.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"c_end\",\"title\":\"结束字符\",\"name\":\"c_end\",\"tip\":\"输入结束字符\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.c_end.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-delete-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(270,'document/document.Word','Docx.replace','{\"key\":\"Docx.replace\",\"title\":\"Word替换内容\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().replace\",\"comment\":\"在Word文档对象 @{doc} 中,查找内容并替换\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"origin_word\",\"title\":\"查找内容\",\"name\":\"origin_word\",\"tip\":\"输入要查找的内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ReplaceType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"replace_type\",\"title\":\"替换为\",\"name\":\"replace_type\",\"tip\":\"选择替换为图片或文本\",\"options\":[{\"label\":\"图片\",\"value\":\"img\"},{\"label\":\"文本\",\"value\":\"str\"}],\"default\":\"str\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"new_word\",\"title\":\"新内容\",\"name\":\"new_word\",\"tip\":\"输入要替换为的内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.new_word.show\",\"expression\":\"return $this.replace_type.value == \'str\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"img_path\",\"title\":\"图片路径\",\"name\":\"img_path\",\"tip\":\"选择图片路径\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.img_path.show\",\"expression\":\"return $this.replace_type.value == \'img\'\"}],\"required\":true},{\"types\":\"ReplaceMethodType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"replace_method\",\"title\":\"替换模式\",\"name\":\"replace_method\",\"tip\":\"选择替换全部、替换首个\",\"options\":[{\"label\":\"首个\",\"value\":\"first\"},{\"label\":\"全部\",\"value\":\"all\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"ignore_case\",\"title\":\"大小写忽略\",\"name\":\"ignore_case\",\"tip\":\"选择是否大小写忽略\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true}],\"outputList\":[],\"icon\":\"word-replace-content\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(271,'document/document.Word','Docx.create_comment','{\"key\":\"Docx.create_comment\",\"title\":\"Word创建批注\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().create_comment\",\"comment\":\"在Word文档对象 @{doc} 中,创建批注\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment\",\"title\":\"批注内容\",\"name\":\"comment\",\"tip\":\"输入批注内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CommentType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"comment_type\",\"title\":\"批注对象\",\"name\":\"comment_type\",\"tip\":\"选择指定范围还是指定内容\",\"options\":[{\"label\":\"指定位置\",\"value\":\"position\"},{\"label\":\"指定内容\",\"value\":\"content\"}],\"default\":\"position\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"paragraph_idx\",\"title\":\"段落号\",\"name\":\"paragraph_idx\",\"tip\":\"输入创建批注的段落号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.paragraph_idx.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start\",\"title\":\"起始字符序号\",\"name\":\"start\",\"tip\":\"输入批注的起始字符序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end\",\"title\":\"结束字符序号\",\"name\":\"end\",\"tip\":\"输入批注的结束字符序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"target_str\",\"title\":\"目标内容\",\"name\":\"target_str\",\"tip\":\"输入批注的目标内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.target_str.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"comment_all\",\"title\":\"全部批注\",\"name\":\"comment_all\",\"tip\":\"选择是否全部批注\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"dynamics\":[{\"key\":\"$this.comment_all.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment_index\",\"title\":\"序号\",\"name\":\"comment_index\",\"tip\":\"选择批注内容的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.comment_index.show\",\"expression\":\"return $this.comment_all.value == false\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-create-comment\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(272,'document/document.Word','Docx.delete_comment','{\"key\":\"Docx.delete_comment\",\"title\":\"Word删除批注\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().delete_comment\",\"comment\":\"在Word文档对象 @{doc} 中,删除指定批注\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"delete_all\",\"title\":\"删除全部\",\"name\":\"delete_all\",\"tip\":\"选择是否删除全部批注\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment_index\",\"title\":\"批注序号\",\"name\":\"comment_index\",\"tip\":\"输入要删除批注的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.comment_index.show\",\"expression\":\"return $this.delete_all.value == false\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-delete-comment\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(273,'document/document.Word','Docx.convert_format','{\"key\":\"Docx.convert_format\",\"title\":\"Word导出为PDF/TXT\",\"version\":\"1.0.0\",\"src\":\"astronverse.word.docx.Docx().convert_format\",\"comment\":\"在Word文档对象 @{doc} 中,导出指定内容为PDF或TXT文件\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"output_path\",\"title\":\"导出路径\",\"name\":\"output_path\",\"tip\":\"选择要导出的文件夹\",\"default\":\"\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"default_name\",\"title\":\"是否使用默认名称\",\"name\":\"default_name\",\"tip\":\"选择是否使用默认名称\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"output_name\",\"title\":\"导出文件名\",\"name\":\"output_name\",\"tip\":\"输入不含格式后缀的文件名\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.output_name.show\",\"expression\":\"return $this.default_name.value == false\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_file_type\",\"title\":\"导出文件格式\",\"name\":\"output_file_type\",\"tip\":\"选中导出为pdf还是txt\",\"options\":[{\"label\":\"PDF\",\"value\":\"pdf\"},{\"label\":\"TXT\",\"value\":\"txt\"}],\"default\":\"pdf\",\"required\":true},{\"types\":\"ConvertPageType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"page_type\",\"title\":\"页面范围\",\"name\":\"page_type\",\"tip\":\"选择要导出全部页面、指定页面范围、当前页面、当前选中内容\",\"options\":[{\"label\":\"全部页面\",\"value\":0},{\"label\":\"当前页面\",\"value\":2},{\"label\":\"指定页面范围\",\"value\":3},{\"label\":\"选中内容\",\"value\":1}],\"default\":0,\"dynamics\":[{\"key\":\"$this.page_type.show\",\"expression\":\"return $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_start\",\"title\":\"起始页面\",\"name\":\"page_start\",\"tip\":\"输入导出的起始页面\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_start.show\",\"expression\":\"return $this.page_type.value == \'3\' && $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_end\",\"title\":\"结束页面\",\"name\":\"page_end\",\"tip\":\"输入要导出的结束页面\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_end.show\",\"expression\":\"return $this.page_type.value == \'3\' && $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"SaveFileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"重名保存方式\",\"name\":\"save_type\",\"tip\":\"选择存在重名文件时报错、覆盖、自动重命名\",\"options\":[{\"label\":\"重名提示\",\"value\":\"warn\"},{\"label\":\"自动生成名称\",\"value\":\"generate\"},{\"label\":\"覆盖同名文件\",\"value\":\"overwrite\"}],\"default\":\"warn\",\"required\":true}],\"outputList\":[],\"icon\":\"word-export-pdf-txt\",\"helpManual\":\"\"}',0,'1','2025-10-11 14:12:21',1,'2025-10-11 14:12:21','1.0.0',NULL,'1000000'),(274,'','Docx.open_document','{\"key\":\"Docx.open_document\",\"title\":\"打开Word\",\"version\":\"1.0.3\",\"src\":\"astronverse.word.docx.Docx().open_document\",\"comment\":\"打开路径为 @{file_path} 的Word文档,返回Word对象 @{doc_obj}\",\"inputList\":[{\"types\":\"PATH\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"file\"}},\"key\":\"file_path\",\"title\":\"文档路径\",\"name\":\"file_path\",\"tip\":\"填写Word文档的路径\",\"default\":\"\",\"required\":true},{\"types\":\"ApplicationType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"default_application\",\"title\":\"驱动方式\",\"name\":\"default_application\",\"tip\":\"选择Word文档的打开方式,如Word或WPS\",\"options\":[{\"label\":\"Word\",\"value\":\"Word\"},{\"label\":\"WPS\",\"value\":\"WPS\"},{\"label\":\"默认软件\",\"value\":\"Default\"}],\"default\":\"Default\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"visible_flag\",\"title\":\"是否可见\",\"name\":\"visible_flag\",\"tip\":\"是否显示Word文档窗口\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"EncodingType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"encoding\",\"title\":\"编码模式\",\"name\":\"encoding\",\"tip\":\"选择文档的编码模式,如UTF-8或GBK\",\"options\":[{\"label\":\"utf-8\",\"value\":\"utf-8\"},{\"label\":\"gbk\",\"value\":\"gbk\"}],\"default\":\"utf-8\",\"level\":\"advanced\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"open_pwd_flag\",\"title\":\"是否填写Word打开密码\",\"name\":\"open_pwd_flag\",\"tip\":\"选择是否需要输入密码打开文档\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"open_pwd\",\"title\":\"Word打开密码\",\"name\":\"open_pwd\",\"tip\":\"输入Word打开密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"dynamics\":[{\"key\":\"$this.open_pwd.show\",\"expression\":\"return $this.open_pwd_flag.value == true\"}],\"required\":false},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"write_pwd_flag\",\"title\":\"是否填写Word写入密码\",\"name\":\"write_pwd_flag\",\"tip\":\"选择是否需要输入密码写入文档\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"level\":\"advanced\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"write_pwd\",\"title\":\"Word写入密码\",\"name\":\"write_pwd\",\"tip\":\"输入Word写入密码\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.write_pwd.show\",\"expression\":\"return $this.write_pwd_flag.value == true\"}],\"required\":false}],\"outputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_obj\",\"title\":\"Word对象\",\"tip\":\"返回Word对象,用于后续操作\"}],\"icon\":\"open-atom\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.3',NULL,'1000003'),(275,'','Docx.read_document_content','{\"key\":\"Docx.read_document_content\",\"title\":\"读取Word内容\",\"version\":\"1.0.2\",\"src\":\"astronverse.word.docx.Docx().read_document_content\",\"comment\":\"读取Word文档对象 @{doc} 内容,返回文档内容 @{doc_data}\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"SelectRangeType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"select_range\",\"title\":\"读取范围\",\"name\":\"select_range\",\"tip\":\"选择读取文档的范围,如整个文档或选中区域\",\"options\":[{\"label\":\"整个文档\",\"value\":\"all\"},{\"label\":\"选中区域\",\"value\":\"selected\"}],\"default\":\"all\",\"required\":true}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"doc_data\",\"title\":\"文档内容\",\"tip\":\"\"}],\"icon\":\"icon-list-read-word\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.2',NULL,'1000002'),(276,'document/document.Word','Docx.delete','{\"key\":\"Docx.delete\",\"title\":\"Word删除内容\",\"version\":\"1.0.1\",\"src\":\"astronverse.word.docx.Docx().delete\",\"comment\":\"在Word文档对象 @{doc} 中,删除内容\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"DeleteMode\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"delete_mode\",\"title\":\"删除方式\",\"name\":\"delete_mode\",\"tip\":\"选择删除方式\",\"options\":[{\"label\":\"全部\",\"value\":\"all\"},{\"label\":\"指定文本\",\"value\":\"content\"},{\"label\":\"指定范围\",\"value\":\"range\"}],\"default\":\"all\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_str\",\"title\":\"文本\",\"name\":\"delete_str\",\"tip\":\"选择要删除的文本\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_str.show\",\"expression\":\"return $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"delete_idx\",\"title\":\"序号\",\"name\":\"delete_idx\",\"tip\":\"选择删除第几个文本\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.delete_idx.show\",\"expression\":\"return $this.str_delete_all.value == False && $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"str_delete_all\",\"title\":\"删除所有找到的文本\",\"name\":\"str_delete_all\",\"tip\":\"选择是否删除所有找到的文本\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"dynamics\":[{\"key\":\"$this.str_delete_all.show\",\"expression\":\"return $this.delete_mode.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_start\",\"title\":\"起始段落\",\"name\":\"p_start\",\"tip\":\"输入起始段落\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_start.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"c_start\",\"title\":\"起始字符\",\"name\":\"c_start\",\"tip\":\"输入起始字符\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.c_start.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"p_end\",\"title\":\"结束段落\",\"name\":\"p_end\",\"tip\":\"输入结束段落\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.p_end.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"c_end\",\"title\":\"结束字符\",\"name\":\"c_end\",\"tip\":\"输入结束字符\",\"default\":0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.c_end.show\",\"expression\":\"return $this.delete_mode.value == \'range\'\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-delete-content\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.1',NULL,'1000001'),(277,'document/document.Word','Docx.create_comment','{\"key\":\"Docx.create_comment\",\"title\":\"Word创建批注\",\"version\":\"1.0.1\",\"src\":\"astronverse.word.docx.Docx().create_comment\",\"comment\":\"在Word文档对象 @{doc} 中,创建批注\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment\",\"title\":\"批注内容\",\"name\":\"comment\",\"tip\":\"输入批注内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"CommentType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"comment_type\",\"title\":\"批注对象\",\"name\":\"comment_type\",\"tip\":\"选择指定范围还是指定内容\",\"options\":[{\"label\":\"指定位置\",\"value\":\"position\"},{\"label\":\"指定内容\",\"value\":\"content\"}],\"default\":\"position\",\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"paragraph_idx\",\"title\":\"段落号\",\"name\":\"paragraph_idx\",\"tip\":\"输入创建批注的段落号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.paragraph_idx.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start\",\"title\":\"起始字符序号\",\"name\":\"start\",\"tip\":\"输入批注的起始字符序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.start.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end\",\"title\":\"结束字符序号\",\"name\":\"end\",\"tip\":\"输入批注的结束字符序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.end.show\",\"expression\":\"return $this.comment_type.value == \'position\'\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"target_str\",\"title\":\"目标内容\",\"name\":\"target_str\",\"tip\":\"输入批注的目标内容\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.target_str.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"comment_all\",\"title\":\"全部批注\",\"name\":\"comment_all\",\"tip\":\"选择是否全部批注\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"dynamics\":[{\"key\":\"$this.comment_all.show\",\"expression\":\"return $this.comment_type.value == \'content\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment_index\",\"title\":\"序号\",\"name\":\"comment_index\",\"tip\":\"选择批注内容的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.comment_index.show\",\"expression\":\"return $this.comment_all.value == False\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-create-comment\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.1',NULL,'1000001'),(278,'document/document.Word','Docx.delete_comment','{\"key\":\"Docx.delete_comment\",\"title\":\"Word删除批注\",\"version\":\"1.0.1\",\"src\":\"astronverse.word.docx.Docx().delete_comment\",\"comment\":\"在Word文档对象 @{doc} 中,删除指定批注\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"delete_all\",\"title\":\"删除全部\",\"name\":\"delete_all\",\"tip\":\"选择是否删除全部批注\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":false,\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"comment_index\",\"title\":\"批注序号\",\"name\":\"comment_index\",\"tip\":\"输入要删除批注的序号\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.comment_index.show\",\"expression\":\"return $this.delete_all.value == False\"}],\"required\":true}],\"outputList\":[],\"icon\":\"word-delete-comment\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.1',NULL,'1000001'),(279,'document/document.Word','Docx.convert_format','{\"key\":\"Docx.convert_format\",\"title\":\"Word导出为PDF/TXT\",\"version\":\"1.0.1\",\"src\":\"astronverse.word.docx.Docx().convert_format\",\"comment\":\"在Word文档对象 @{doc} 中,导出指定内容为PDF或TXT文件\",\"inputList\":[{\"types\":\"DocumentObject\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"doc\",\"title\":\"Word对象\",\"name\":\"doc\",\"tip\":\"本软件前序原子能力(如打开、新建)返回的Word对象\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[],\"file_type\":\"folder\"}},\"key\":\"output_path\",\"title\":\"导出路径\",\"name\":\"output_path\",\"tip\":\"选择要导出的文件夹\",\"default\":\"\",\"required\":true},{\"types\":\"Bool\",\"formType\":{\"type\":\"SWITCH\",\"params\":{}},\"key\":\"default_name\",\"title\":\"是否使用默认名称\",\"name\":\"default_name\",\"tip\":\"选择是否使用默认名称\",\"options\":[{\"label\":\"是\",\"value\":true},{\"label\":\"否\",\"value\":false}],\"default\":true,\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"output_name\",\"title\":\"导出文件名\",\"name\":\"output_name\",\"tip\":\"输入不含格式后缀的文件名\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.output_name.show\",\"expression\":\"return $this.default_name.value == False\"}],\"required\":true},{\"types\":\"FileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"output_file_type\",\"title\":\"导出文件格式\",\"name\":\"output_file_type\",\"tip\":\"选中导出为pdf还是txt\",\"options\":[{\"label\":\"PDF\",\"value\":\"pdf\"},{\"label\":\"TXT\",\"value\":\"txt\"}],\"default\":\"pdf\",\"required\":true},{\"types\":\"ConvertPageType\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"page_type\",\"title\":\"页面范围\",\"name\":\"page_type\",\"tip\":\"选择要导出全部页面、指定页面范围、当前页面、当前选中内容\",\"options\":[{\"label\":\"全部页面\",\"value\":0},{\"label\":\"当前页面\",\"value\":2},{\"label\":\"指定页面范围\",\"value\":3},{\"label\":\"选中内容\",\"value\":1}],\"default\":0,\"dynamics\":[{\"key\":\"$this.page_type.show\",\"expression\":\"return $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_start\",\"title\":\"起始页面\",\"name\":\"page_start\",\"tip\":\"输入导出的起始页面\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_start.show\",\"expression\":\"return $this.page_type.value == \'3\' && $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"page_end\",\"title\":\"结束页面\",\"name\":\"page_end\",\"tip\":\"输入要导出的结束页面\",\"default\":1,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"dynamics\":[{\"key\":\"$this.page_end.show\",\"expression\":\"return $this.page_type.value == \'3\' && $this.output_file_type.value == \'pdf\'\"}],\"required\":true},{\"types\":\"SaveFileType\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"save_type\",\"title\":\"重名保存方式\",\"name\":\"save_type\",\"tip\":\"选择存在重名文件时报错、覆盖、自动重命名\",\"options\":[{\"label\":\"重名提示\",\"value\":\"warn\"},{\"label\":\"自动生成名称\",\"value\":\"generate\"},{\"label\":\"覆盖同名文件\",\"value\":\"overwrite\"}],\"default\":\"warn\",\"required\":true}],\"outputList\":[],\"icon\":\"word-export-pdf-txt\",\"helpManual\":\"\"}',0,'1','2025-10-14 08:41:00',1,'2025-10-14 08:41:00','1.0.1',NULL,'1000001'),(280,'','Code.ElseIfEnd','{\"key\":\"Code.ElseIfEnd\",\"title\":\"判断结束\",\"version\":\"1\",\"comment\":\"判断结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(281,'','Code.ForDictEnd','{\"key\":\"Code.ForDictEnd\",\"title\":\"字典For循环结束\",\"version\":\"1\",\"comment\":\"字典For循环结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(282,'code/error','Code.Try','{\"key\":\"Code.Try\",\"title\":\"捕获异常(Try)\",\"version\":\"1\",\"comment\":\"可能发生异常的try流程,发生异常后执行catch流程,最终执行finally流程\",\"icon\":\"try-exception\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(283,'','Code.GroupEnd','{\"key\":\"Code.GroupEnd\",\"title\":\"编组结束\",\"version\":\"1\",\"comment\":\"编组结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(284,'','Code.ElseEnd','{\"key\":\"Code.ElseEnd\",\"title\":\"判断结束\",\"version\":\"1\",\"comment\":\"判断结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(285,'code/for','Code.ForDict','{\"key\":\"Code.ForDict\",\"title\":\"字典For循环\",\"version\":\"1\",\"comment\":\"用key值(@{key})和value值(@{value})遍历字典,进行循环操作,输出循环项键名至(@{key}), 输出循环项键值至(@{value})\",\"inputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"dicts\",\"title\":\"字典对象\",\"name\":\"dicts\",\"tip\":\"循环中遍历的字典,可自行创建或选择前面组件创建的字典\",\"default\":\"\"}],\"outputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"key\",\"title\":\"循环项位置\",\"tip\":\"循环项位置\",\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"value\",\"title\":\"循环项\",\"tip\":\"循环项\",\"required\":true}],\"icon\":\"dictionary-for-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(286,'','Code.CatchEnd','{\"key\":\"Code.CatchEnd\",\"title\":\"捕获结束\",\"version\":\"1\",\"comment\":\"捕获结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(287,'code/for','Code.While','{\"key\":\"Code.While\",\"title\":\"While循环\",\"version\":\"1.0.2\",\"comment\":\"如果(@{args1})(@{condition})(@{args2}),则执行以下操作\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args1\",\"title\":\"对象1\",\"name\":\"args1\",\"default\":\"\"},{\"types\":\"Str\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"condition\",\"title\":\"关系\",\"name\":\"condition\",\"options\":[{\"label\":\"等于\",\"value\":\"==\"},{\"label\":\"不等于\",\"value\":\"!=\"},{\"label\":\"大于\",\"value\":\">\"},{\"label\":\"大于等于\",\"value\":\">=\"},{\"label\":\"小于\",\"value\":\"<\"},{\"label\":\"小于等于\",\"value\":\"<=\"},{\"label\":\"包含\",\"value\":\"in\"},{\"label\":\"不包含\",\"value\":\"notin\"},{\"label\":\"为真\",\"value\":\"true\"},{\"label\":\"为假\",\"value\":\"false\"},{\"label\":\"为空\",\"value\":\"empty\"},{\"label\":\"不为空\",\"value\":\"notempty\"}],\"default\":\"==\"},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args2\",\"title\":\"对象2\",\"name\":\"args2\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.args2.show\",\"expression\":\"return [\'==\', \'!=\', \'>\', \'>=\', \'<\', \'<=\', \'in\', \'notin\'].includes($this.condition.value)\"}]}],\"icon\":\"while-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1.0.2',NULL,'1000002'),(288,'code/if','Code.ElseIf','{\"key\":\"Code.ElseIf\",\"title\":\"ELSE IF条件\",\"version\":\"1.0.1\",\"comment\":\"如果(@{args1})(@{condition})(@{args2}),则执行以下操作\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args1\",\"title\":\"对象1\",\"name\":\"args1\",\"default\":\"\"},{\"types\":\"Str\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"condition\",\"title\":\"关系\",\"name\":\"condition\",\"options\":[{\"label\":\"等于\",\"value\":\"==\"},{\"label\":\"不等于\",\"value\":\"!=\"},{\"label\":\"大于\",\"value\":\">\"},{\"label\":\"大于等于\",\"value\":\">=\"},{\"label\":\"小于\",\"value\":\"<\"},{\"label\":\"小于等于\",\"value\":\"<=\"},{\"label\":\"包含\",\"value\":\"in\"},{\"label\":\"不包含\",\"value\":\"notin\"},{\"label\":\"为真\",\"value\":\"true\"},{\"label\":\"为假\",\"value\":\"false\"},{\"label\":\"为空\",\"value\":\"empty\"},{\"label\":\"不为空\",\"value\":\"notempty\"}],\"default\":\"==\"},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args2\",\"title\":\"对象2\",\"name\":\"args2\",\"default\":\"\"}],\"icon\":\"else-if-condition\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1.0.1',NULL,'1000001'),(289,'process','Code.Process','{\"key\":\"Code.Process\",\"title\":\"运行子流程\",\"version\":\"1\",\"comment\":\"运行子流程(@{process:子流程})\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"SELECT\",\"params\":{\"filters\":[\"Process\"]}},\"key\":\"process\",\"title\":\"选择子流程\",\"name\":\"process\",\"tip\":\"选择要运行的子流程\",\"required\":true,\"default\":\"\"},{\"types\":\"Any\",\"formType\":{\"type\":\"PROCESSPARAM\",\"params\":{\"linkage\":\"process\"}},\"key\":\"process_param\",\"title\":\"输入参数\",\"name\":\"process_param\",\"tip\":\"\",\"need_parse\":\"str\",\"default\":\"\"}],\"outputList\":[{\"types\":\"Dict\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"process_res\",\"title\":\"保存流程输出结果至\",\"tip\":\"\"}],\"icon\":\"run-sub-process\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(290,'','Code.ForEnd','{\"key\":\"Code.ForEnd\",\"title\":\"循环结束\",\"version\":\"1\",\"comment\":\"循环结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(291,'','Code.TryEnd','{\"key\":\"Code.TryEnd\",\"title\":\"捕获结束\",\"version\":\"1\",\"comment\":\"捕获结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(292,'','Code.ForStepEnd','{\"key\":\"Code.ForStepEnd\",\"title\":\"计数For循环结束\",\"version\":\"1\",\"comment\":\"计数For循环结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(293,'','Code.IfEnd','{\"key\":\"Code.IfEnd\",\"title\":\"判断结束\",\"version\":\"1\",\"comment\":\"判断结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(294,'code/for','Code.Continue','{\"key\":\"Code.Continue\",\"title\":\"继续下次循环(Continue)\",\"version\":\"1\",\"comment\":\"继续下一次循环\",\"icon\":\"continue-next-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(295,'','Code.Group','{\"key\":\"Code.Group\",\"title\":\"编组开始\",\"version\":\"1\",\"comment\":\"编组开始\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(296,'','Code.FinallyEnd','{\"key\":\"Code.FinallyEnd\",\"title\":\"捕获结束\",\"version\":\"1\",\"comment\":\"捕获结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(297,'','Code.Catch','{\"key\":\"Code.Catch\",\"title\":\"捕获异常(Catch)\",\"version\":\"1\",\"icon\":\"catch-exception\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(298,'','Code.WhileEnd','{\"key\":\"Code.WhileEnd\",\"title\":\"循环结束\",\"version\":\"1\",\"comment\":\"循环结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(299,'code/for','Code.ForList','{\"key\":\"Code.ForList\",\"title\":\"列表For循环\",\"version\":\"1\",\"comment\":\"在列表(@{list})中通过循环变量(@{item})遍历列表,进行循环操作,输出列表循环至(@{item}), 输出循环项位置为(@{index})\",\"inputList\":[{\"types\":\"List\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"list\",\"title\":\"列表对象\",\"name\":\"list\",\"tip\":\"循环中遍历的列表,可自行创建或引用前面组件已创建的列表\",\"default\":\"\"}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"index\",\"title\":\"循环项位置\",\"tip\":\"默认变量可修改,用于遍历列表的变量索引数值\",\"required\":true},{\"types\":\"Any\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"item\",\"title\":\"循环项\",\"tip\":\"默认变量可修改,用于遍历列表的变量\",\"required\":true}],\"icon\":\"list-for-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(300,'code/error','Code.Finally','{\"key\":\"Code.Finally\",\"title\":\"捕获异常(Finally)\",\"version\":\"1\",\"icon\":\"finally-exception\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(301,'code/for','Code.ForStep','{\"key\":\"Code.ForStep\",\"title\":\"计数For循环\",\"version\":\"1\",\"comment\":\"从(@{start})开始到(@{end})结束,递增值为(@{step:1}),执行循环内操作,输出循环项列表至(@{index})\",\"inputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"start\",\"title\":\"起始值\",\"name\":\"start\",\"tip\":\"循环从该值开始\",\"default\":\"\"},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"end\",\"title\":\"结束值\",\"name\":\"end\",\"tip\":\"循环至该值结束\",\"default\":\"\"},{\"types\":\"Int\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"step\",\"title\":\"步长\",\"name\":\"step\",\"tip\":\"循环一次后的增加值\",\"default\":1}],\"outputList\":[{\"types\":\"Int\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"index\",\"title\":\"循环项索引\",\"tip\":\"用于计数且可修改,随着循环进行而增加的数值结果\",\"required\":true}],\"icon\":\"count-for-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(302,'code/for','Code.Break','{\"key\":\"Code.Break\",\"title\":\"退出循环(Break)\",\"version\":\"1\",\"comment\":\"退出该次循环\",\"icon\":\"break-loop\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(303,'code/if','Code.If','{\"key\":\"Code.If\",\"title\":\"IF条件\",\"version\":\"1.0.2\",\"comment\":\"如果(@{args1})(@{condition})(@{args2}),则执行以下操作\",\"inputList\":[{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args1\",\"title\":\"对象1\",\"name\":\"args1\",\"default\":\"\"},{\"types\":\"Str\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"condition\",\"title\":\"关系\",\"name\":\"condition\",\"options\":[{\"label\":\"等于\",\"value\":\"==\"},{\"label\":\"不等于\",\"value\":\"!=\"},{\"label\":\"大于\",\"value\":\">\"},{\"label\":\"大于等于\",\"value\":\">=\"},{\"label\":\"小于\",\"value\":\"<\"},{\"label\":\"小于等于\",\"value\":\"<=\"},{\"label\":\"包含\",\"value\":\"in\"},{\"label\":\"不包含\",\"value\":\"notin\"},{\"label\":\"为真\",\"value\":\"true\"},{\"label\":\"为假\",\"value\":\"false\"},{\"label\":\"为空\",\"value\":\"empty\"},{\"label\":\"不为空\",\"value\":\"notempty\"}],\"default\":\"==\"},{\"types\":\"Any\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"args2\",\"title\":\"对象2\",\"name\":\"args2\",\"default\":\"\",\"dynamics\":[{\"key\":\"$this.args2.show\",\"expression\":\"return [\'==\', \'!=\', \'>\', \'>=\', \'<\', \'<=\', \'in\', \'notin\'].includes($this.condition.value)\"}]}],\"icon\":\"if-condition\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1.0.2',NULL,'1000002'),(304,'code/if','Code.Else','{\"key\":\"Code.Else\",\"title\":\"Else条件\",\"version\":\"1\",\"comment\":\"Else条件\",\"icon\":\"else-condition\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(305,'','Code.ForListEnd','{\"key\":\"Code.ForListEnd\",\"title\":\"列表For循环结束\",\"version\":\"1\",\"comment\":\"列表For循环结束\",\"helpManual\":\"\",\"noAdvanced\":true}',0,'1','2025-10-14 08:56:48',1,'2025-10-14 08:56:48','1',NULL,'1000000'),(306,'web','BrowserElement.slider_hover','{\"key\":\"BrowserElement.slider_hover\",\"title\":\"拾取滑块拖拽(web)\",\"version\":\"1.0.1\",\"src\":\"astronverse.browser.browser_element.BrowserElement().slider_hover\",\"comment\":\"将滑块 @{element_slider} 从 @{drag_type} 向 @{drag_direction} 拖拽 @{percent_value} %\",\"inputList\":[{\"types\":\"Browser\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"browser_obj\",\"title\":\"浏览器对象\",\"name\":\"browser_obj\",\"tip\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"slider_element\",\"title\":\"拾取滑块\",\"name\":\"slider_element\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"WebPick\",\"formType\":{\"type\":\"PICK\",\"params\":{\"use\":\"WebPick\"}},\"key\":\"progress_element\",\"title\":\"拾取轨道\",\"name\":\"progress_element\",\"tip\":\"\",\"required\":true,\"noInput\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"percent_value\",\"title\":\"滑块移动比例\",\"name\":\"percent_value\",\"tip\":\"\",\"default\":0.0,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true},{\"types\":\"ElementDragDirectionTypeFlag\",\"formType\":{\"type\":\"SELECT\"},\"key\":\"drag_direction\",\"title\":\"拖拽方向\",\"name\":\"drag_direction\",\"tip\":\"\",\"options\":[{\"label\":\"左\",\"value\":\"left\"},{\"label\":\"右\",\"value\":\"right\"},{\"label\":\"上\",\"value\":\"up\"},{\"label\":\"下\",\"value\":\"down\"}],\"default\":\"left\",\"required\":true},{\"types\":\"ElementDragTypeFlag\",\"formType\":{\"type\":\"RADIO\"},\"key\":\"drag_type\",\"title\":\"拖拽类型\",\"name\":\"drag_type\",\"tip\":\"\",\"options\":[{\"label\":\"起始位置\",\"value\":\"start\"},{\"label\":\"当前位置\",\"value\":\"current\"}],\"default\":\"start\",\"required\":true},{\"types\":\"Float\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"duration\",\"title\":\"拖动的持续时间\",\"name\":\"duration\",\"tip\":\"\",\"default\":0.25,\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"required\":true}],\"outputList\":[],\"icon\":\"pick-slider-drag-web\",\"helpManual\":\"\"}',0,'1','2025-10-14 09:05:51',1,'2025-10-14 09:05:51','1.0.1',NULL,'1000001'),(307,'desktop','Software.open','{\"key\":\"Software.open\",\"title\":\"打开程序\",\"version\":\"1.0.1\",\"src\":\"astronverse.software.software.Software().open\",\"comment\":\"打开应用程序路径(@{app_absolute_path}),并设置运行参数为(@{app_arguments}),将结果输出为(@{software_open})\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[]}},\"key\":\"app_absolute_path\",\"title\":\"应用程序路径\",\"name\":\"app_absolute_path\",\"tip\":\"输入需要打开的应用程序文件路径\",\"default\":\"\",\"required\":true},{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON\"},\"key\":\"app_arguments\",\"title\":\"运行参数\",\"name\":\"app_arguments\",\"tip\":\"用于部分程序依赖的运行参数,例:程序环境变量\",\"default\":\"\",\"value\":[{\"type\":\"str\",\"value\":\"\"}],\"level\":\"advanced\",\"required\":false}],\"outputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"RESULT\"},\"key\":\"software_open\",\"title\":\"应用程序路径\",\"tip\":\"输出该被打开的应用程序路径\"}],\"icon\":\"open-program\",\"helpManual\":\"\"}',0,'1','2025-10-14 09:14:46',1,'2025-10-14 09:14:46','1.0.1',NULL,'1000001'),(308,'desktop','Software.close','{\"key\":\"Software.close\",\"title\":\"关闭程序\",\"version\":\"1.0.1\",\"src\":\"astronverse.software.software.Software().close\",\"comment\":\"关闭应用程序路径为(@{app_absolute_path})的程序\",\"inputList\":[{\"types\":\"Str\",\"formType\":{\"type\":\"INPUT_VARIABLE_PYTHON_FILE\",\"params\":{\"filters\":[]}},\"key\":\"app_absolute_path\",\"title\":\"应用程序路径\",\"name\":\"app_absolute_path\",\"tip\":\"需要被关闭的应用程序路径\",\"required\":true}],\"outputList\":[],\"icon\":\"close-program\",\"helpManual\":\"\"}',0,'1','2025-10-14 09:14:46',1,'2025-10-14 09:14:46','1.0.1',NULL,'1000001');
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/init_his_data_enum_data.sql b/docker/astronAgent/astronRPA/volumes/mysql/init_his_data_enum_data.sql
new file mode 100644
index 00000000..fc473e31
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/init_his_data_enum_data.sql
@@ -0,0 +1,28 @@
+INSERT INTO rpa.his_data_enum (parent_code,icon,field,text,num,unit,percent,tip,`order`) VALUES
+ ('sourceData','icon-usercount','userNum','用户数量',NULL,'个',NULL,'企业中注册的用户总数(不包括禁用状态下的用户)',0),
+ ('sourceData','icon-chanpin','terminal','终端数量',NULL,'个',NULL,'企业中总的终端数量',1),
+ ('sourceData','icon-jiqiren','robotNum','机器人数量',NULL,'个',NULL,'企业中所有已发版的机器人',2),
+ ('executeData','icon-zhenshikexin','executeSuccess','执行成功次数',NULL,'次',NULL,'企业中所有的机器人执行成功的总次数',0),
+ ('executeData','icon-zhanbi','executeSuccessRate','执行成功率',NULL,'%',NULL,'执行成功率=成功次数/(成功次数+失败次数+中止次数)',1),
+ ('executeData','icon-shijian','executeTimeTotalHour','累计执行时长',NULL,'小时',NULL,'机器人成功执行的总时长(不包括正在执行、失败、中止的时长)',2),
+ ('executeData','icon-renyuan','laborSaveHour','累计节省人力',NULL,'人/天',NULL,'成功执行时长(小时)/8(小时)8倍人的效率',3),
+ ('robotExecuteToday','icon-todaycount','executeTotal','今日执行次数',NULL,NULL,NULL,'今日执行次数',0),
+ ('robotExecuteToday','icon-running','executeRunning','今日正在执行',NULL,NULL,'占比{executeRunningRate}%','今日正在执行',1),
+ ('robotExecuteToday','icon-zhenshikexin','executeSuccess','今日执行成功',NULL,NULL,'占比{executeSuccessRate}%','今日执行成功',2),
+ ('robotExecuteToday','icon-shibai','executeFail','今日执行失败',NULL,NULL,'占比{executeFailRate}%','今日执行失败',3),
+ ('robotExecuteToday','icon-zanting','executeAbort','今日执行中止',NULL,NULL,'占比{executeAbortRate}%','今日执行中止',4),
+ ('terminalRealTime','icon-chanpin','TerminalTotal','终端总数',NULL,NULL,NULL,'总的终端数量',0),
+ ('terminalRealTime','icon-manglu','busyNum','忙碌数',NULL,NULL,NULL,'正在执行的终端数量',1),
+ ('terminalRealTime','icon-kongxian','freeNum','空闲数',NULL,NULL,NULL,'已经连接未执行的终端数量',2),
+ ('terminalRealTime','icon-lixian','offlineNum','离线数',NULL,NULL,NULL,'未连接的终端数量',3),
+ ('terminalDataOverview','icon-shijian','terminalTimeHour','执行时长',NULL,'h',NULL,'成功执行的时长',0),
+ ('terminalDataOverview','icon-zhenshikexin','terminalNum','执行成功次数',NULL,NULL,NULL,'成功执行的次数',1),
+ ('robotOverview','icon-todaycount','executeTotal','累计执行次数',NULL,NULL,NULL,'累计执行次数',0),
+ ('robotOverview','icon-zhenshikexin','executeSuccess','累计成功次数',NULL,'次','占比{executeSuccessRate}%','累计成功次数',1),
+ ('robotOverview','icon-shibai','executeFail','累计失败次数',NULL,'次','占比{executeFailRate}%','累计失败次数',2),
+ ('robotOverview','icon-zanting','executeAbort','累计中止次数',NULL,'次','占比{executeAbortRate}%','累计中止次数',3),
+ ('robotExecutionData','icon-todaycount','executeTotal','执行次数',NULL,NULL,NULL,'执行次数',0),
+ ('robotExecutionData','icon-shijian','executeTimeHour','执行时长',NULL,'小时',NULL,'执行时长(不包括正在执行、失败、中止的时长)',1),
+ ('robotExecutionData','icon-zhenshikexin','executeSuccess','执行成功',NULL,NULL,'占比{executeSuccessRate}%','执行成功次数',2),
+ ('robotExecutionData','icon-shibai','executeFail','执行失败',NULL,NULL,'占比{executeFailRate}%','执行失败次数',3),
+ ('robotExecutionData','icon-zanting','executeAbort','执行中止',NULL,NULL,'占比{executeAbortRate}%','执行中止次数',4);
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/init_robot_example_data.sql b/docker/astronAgent/astronRPA/volumes/mysql/init_robot_example_data.sql
new file mode 100644
index 00000000..c3903efa
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/init_robot_example_data.sql
@@ -0,0 +1,14 @@
+-- user id from casdoor.user
+SET @user_id = (SELECT id FROM casdoor.user WHERE name = 'example-user');
+
+-- ----------------------------
+-- Records of robot_design
+-- ----------------------------
+INSERT INTO rpa.robot_design (id, robot_id, name, creator_id, create_time, updater_id, update_time, deleted, tenant_id, app_id, app_version, market_id, resource_status, data_source, transform_status, edit_enable) VALUES (3483, '1978748427445473280', '示例机器人', @user_id, '2025-10-16 09:02:43', @user_id, '2025-10-16 09:03:14', 0, 'example-org', null, null, null, null, 'create', 'editing', '1');
+
+-- ----------------------------
+-- Records of c_process
+-- ----------------------------
+INSERT INTO rpa.c_process (id, project_id, process_id, process_content, process_name, deleted, creator_id, create_time, updater_id, update_time, robot_id, robot_version) VALUES (3571, null, '1978748427479027712', '[{"key":"Report.print","version":"1.0.0","id":"bh748620057231429","alias":"日志打印","inputList":[{"key":"report_type","value":"info"},{"key":"msg","value":[{"type":"other","value":"Hello world"}]}],"outputList":[],"advanced":[{"key":"__delay_before__","value":[{"type":"other","value":0}]},{"key":"__delay_after__","value":[{"type":"other","value":0}]}],"exception":[{"key":"__skip_err__","value":"exit"},{"key":"__retry_time__","value":[{"type":"other","value":0}],"show":false},{"key":"__retry_interval__","value":[{"type":"other","value":0}],"show":false}]}]', '主流程', 0, @user_id, '2025-10-16 09:02:43', @user_id, '2025-10-16 09:03:15', '1978748427445473280', 0);
+
+
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/my.cnf b/docker/astronAgent/astronRPA/volumes/mysql/my.cnf
new file mode 100644
index 00000000..f0288f8c
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/my.cnf
@@ -0,0 +1,8 @@
+[client]
+default-character-set=utf8mb4
+[mysql]
+default-character-set=utf8mb4
+[mysqld]
+init_connect='SET NAMES utf8mb4 COLLATE utf8mb4_general_ci'
+character-set-server=utf8mb4
+collation-server=utf8mb4_general_ci
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/volumes/mysql/schema.sql b/docker/astronAgent/astronRPA/volumes/mysql/schema.sql
new file mode 100644
index 00000000..fabe4f6a
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/mysql/schema.sql
@@ -0,0 +1,1343 @@
+-- global
+SET GLOBAL character_set_client = utf8mb4;
+SET GLOBAL character_set_connection = utf8mb4;
+SET GLOBAL character_set_results = utf8mb4;
+SET GLOBAL collation_connection = utf8mb4_general_ci;
+
+-- casdoor database init
+
+CREATE DATABASE IF NOT EXISTS casdoor COLLATE utf8mb4_general_ci;
+
+-- rpa database init
+
+CREATE DATABASE IF NOT EXISTS rpa COLLATE utf8mb4_general_ci;
+
+USE rpa;
+-- rpa.alarm_rule definition
+
+CREATE TABLE `alarm_rule` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `enable` tinyint(3) DEFAULT NULL COMMENT '是否启用',
+ `name` varchar(255) DEFAULT NULL COMMENT '规则名',
+ `condition` varchar(100) DEFAULT NULL COMMENT '条件JSON字符串:{"hours":23,"minutes":59,"count":10}',
+ `duration` char(17) DEFAULT NULL COMMENT 'HH:MM:SS-HH:MM:SS 时间段(开始-结束)',
+ `role_id` char(36) DEFAULT NULL COMMENT '操作者角色id',
+ `process_id_list` mediumtext COMMENT 'processId',
+ `event_module_code` int(11) DEFAULT NULL COMMENT '事件模块代码',
+ `event_module_name` varchar(255) DEFAULT NULL COMMENT '事件模块',
+ `event_type_code` int(11) DEFAULT NULL COMMENT '事件代码',
+ `event_type_name` varchar(255) DEFAULT NULL COMMENT '事件类型',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1966060594098135041 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.alarm_rule_user definition
+
+CREATE TABLE `alarm_rule_user` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `alarm_rule_id` bigint(20) DEFAULT NULL COMMENT 'alarm_rule表id',
+ `phone` varchar(200) DEFAULT NULL COMMENT '电话',
+ `name` varchar(100) DEFAULT NULL COMMENT '用户姓名',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1966061018251321345 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.app_application definition
+
+CREATE TABLE `app_application` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `robot_id` varchar(100) NOT NULL COMMENT '机器人ID',
+ `robot_version` int(11) NOT NULL COMMENT '机器人版本ID',
+ `status` varchar(20) NOT NULL COMMENT '状态: 待审核pending, 已通过approved, 未通过rejected, 已撤销canceled,作废nullify',
+ `application_type` varchar(20) NOT NULL COMMENT '申请类型: release(上架)/use(使用)',
+ `security_level` varchar(10) DEFAULT NULL COMMENT '审核设置的密级red,green,yellow',
+ `allowed_dept` varchar(5000) DEFAULT NULL COMMENT '允许使用的部门ID列表',
+ `expire_time` timestamp NULL DEFAULT NULL COMMENT '使用期限(截止日期)',
+ `audit_opinion` varchar(500) DEFAULT NULL COMMENT '审核意见',
+ `creator_id` char(36) DEFAULT NULL COMMENT '申请人ID',
+ `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者或审核者id',
+ `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `deleted` smallint(1) DEFAULT '0',
+ `tenant_id` char(36) CHARACTER SET utf8 DEFAULT NULL,
+ `client_deleted` smallint(1) DEFAULT '0' COMMENT '客户端的申请记录-是否删除',
+ `cloud_deleted` smallint(1) DEFAULT '0' COMMENT '卓越中心的申请记录-是否删除',
+ `default_pass` smallint(1) DEFAULT NULL COMMENT '选择绿色密级时,后续更新发版是否默认通过',
+ `market_info` varchar(500) DEFAULT NULL COMMENT '团队市场id等信息,用于第一次发起上架申请,审核通过后自动分享到该市场',
+ `publish_info` varchar(500) DEFAULT NULL COMMENT '发版JSON信息',
+ PRIMARY KEY (`id`),
+ KEY `idx_app_robot` (`robot_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1967839071050592259 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='上架/使用审核表';
+
+
+-- rpa.app_application_tenant definition
+
+CREATE TABLE `app_application_tenant` (
+ `tenant_id` varchar(36) NOT NULL,
+ `audit_enable` smallint(6) DEFAULT NULL COMMENT '是否开启审核,1开启,0不开启',
+ `audit_enable_time` timestamp NULL DEFAULT NULL,
+ `audit_enable_operator` char(36) DEFAULT NULL,
+ `audit_enable_reason` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='租户是否开启审核配置表';
+
+
+-- rpa.app_market definition
+
+CREATE TABLE `app_market` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL,
+ `market_id` varchar(20) DEFAULT NULL COMMENT '团队市场id',
+ `market_name` varchar(60) DEFAULT NULL COMMENT '市场名称',
+ `market_describe` varchar(800) DEFAULT NULL COMMENT '市场描述',
+ `market_type` varchar(10) DEFAULT NULL COMMENT '市场类型:team,official',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `app_market_creator_id_IDX` (`creator_id`),
+ KEY `app_market_market_id_IDX` (`market_id`),
+ KEY `app_market_tenant_id_IDX` (`tenant_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=134 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='团队市场-团队表';
+
+
+-- rpa.app_market_dict definition
+
+CREATE TABLE `app_market_dict` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `business_code` varchar(100) DEFAULT NULL COMMENT '业务编码:1、行业类型,2、角色功能marketRoleFunc',
+ `name` varchar(64) DEFAULT NULL COMMENT '行业名称,角色功能名称',
+ `dict_code` varchar(64) DEFAULT NULL COMMENT '行业编码,功能编码',
+ `dict_value` varchar(100) DEFAULT NULL COMMENT 'T有权限,F无权限',
+ `user_type` varchar(100) DEFAULT NULL COMMENT 'owner,admin,acquirer,author',
+ `description` varchar(256) DEFAULT NULL COMMENT '描述',
+ `seq` int(11) DEFAULT NULL COMMENT '排序',
+ `creator_id` char(36) DEFAULT '73',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT '73',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `deleted` smallint(1) DEFAULT '0',
+ PRIMARY KEY (`id`),
+ KEY `app_market_dict_dict_code_IDX` (`dict_code`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8;
+
+
+-- rpa.app_market_resource definition
+
+CREATE TABLE `app_market_resource` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `market_id` varchar(20) DEFAULT NULL COMMENT '团队市场id',
+ `app_id` varchar(50) DEFAULT NULL COMMENT '应用id,模板id,组件id',
+ `download_num` bigint(20) DEFAULT '0' COMMENT '下载次数',
+ `check_num` bigint(20) DEFAULT '0' COMMENT '查看次数',
+ `creator_id` char(36) DEFAULT NULL COMMENT '发布人',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `robot_id` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '机器人id',
+ `app_name` varchar(64) CHARACTER SET utf8 DEFAULT NULL COMMENT '资源名称',
+ PRIMARY KEY (`id`),
+ KEY `app_market_resource_app_id_IDX` (`app_id`),
+ KEY `app_market_resource_creator_id_IDX` (`creator_id`),
+ KEY `app_market_resource_market_id_IDX` (`market_id`),
+ KEY `app_market_resource_tenant_id_IDX` (`tenant_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=196 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='团队市场-资源映射表';
+
+
+-- rpa.app_market_user definition
+
+CREATE TABLE `app_market_user` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '根据id找在对应租户的信息',
+ `market_id` varchar(20) DEFAULT NULL COMMENT '团队市场id',
+ `user_type` varchar(10) DEFAULT NULL COMMENT '成员类型:owner,admin,acquirer,author',
+ `creator_id` char(36) DEFAULT NULL COMMENT '成员id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '加入时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `app_market_user_creator_id_IDX` (`creator_id`),
+ KEY `app_market_user_market_id_IDX` (`market_id`),
+ KEY `app_market_user_tenant_id_IDX` (`tenant_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='团队市场-人员表,n:n的关系';
+
+
+-- rpa.app_market_version definition
+
+CREATE TABLE `app_market_version` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `market_id` varchar(100) DEFAULT NULL COMMENT '团队市场id',
+ `app_id` varchar(50) DEFAULT NULL,
+ `app_version` int(11) DEFAULT NULL COMMENT '应用版本,同机器人版本',
+ `edit_flag` tinyint(1) DEFAULT '1' COMMENT '自己创建的分享到市场,是否支持编辑/开放源码;0不支持,1支持',
+ `category` varchar(100) DEFAULT NULL COMMENT '分享到市场的机器人行业:政务、医疗、商业等',
+ `creator_id` char(36) DEFAULT NULL COMMENT '发布人',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ PRIMARY KEY (`id`),
+ KEY `app_market_version_app_id_IDX` (`app_id`) USING BTREE,
+ KEY `app_market_version_market_id_IDX` (`market_id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=280 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='团队市场-应用版本表';
+
+
+-- rpa.atom_like definition
+
+CREATE TABLE `atom_like` (
+ `id` int(20) NOT NULL AUTO_INCREMENT,
+ `like_id` varchar(20) NOT NULL,
+ `atom_key` varchar(100) NOT NULL COMMENT '原子能力的key,全局唯一',
+ `creator_id` char(36) NOT NULL,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `is_deleted` smallint(1) NOT NULL DEFAULT '0',
+ `updater_id` char(36) DEFAULT NULL,
+ `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8 COMMENT='原子能力收藏';
+
+
+-- rpa.atom_meta_duplicate_log definition
+
+CREATE TABLE `atom_meta_duplicate_log` (
+ `id` bigint(20) NOT NULL DEFAULT '0',
+ `atom_key` varchar(100) DEFAULT NULL,
+ `version` varchar(20) DEFAULT NULL COMMENT '原子能力版本',
+ `request_body` mediumtext COMMENT '完整请求体',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除',
+ `creator_id` bigint(20) DEFAULT '73',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` bigint(20) DEFAULT '73',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.audit_checkpoint definition
+
+CREATE TABLE `audit_checkpoint` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `audit_object_type` varchar(36) DEFAULT NULL COMMENT 'robot,dept',
+ `last_processed_id` varchar(36) DEFAULT NULL,
+ `audit_status` varchar(20) DEFAULT NULL COMMENT '统计进度:counting, completed, pending,to_count',
+ `count_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '删除标识',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=324 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='监控管理统计断点表';
+
+
+-- rpa.audit_record definition
+
+CREATE TABLE `audit_record` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `event_module_code` int(11) DEFAULT NULL COMMENT '事件模块代码',
+ `event_module_name` varchar(255) DEFAULT NULL COMMENT '事件模块',
+ `event_type_code` int(11) DEFAULT NULL COMMENT '事件代码',
+ `event_type_name` varchar(255) DEFAULT NULL COMMENT '事件类型',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `creator_name` varchar(255) DEFAULT NULL COMMENT '创建者名称',
+ `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `event_detail` varchar(255) DEFAULT NULL COMMENT '事件详情',
+ `process_id_list` mediumtext COMMENT 'processId',
+ `role_id_list` mediumtext COMMENT '角色id列表',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=150 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.c_atom_meta definition
+
+CREATE TABLE `c_atom_meta` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `parent_key` varchar(100) DEFAULT NULL,
+ `atom_key` varchar(100) DEFAULT NULL,
+ `atom_content` mediumtext COMMENT '原子能力所有配置信息,json',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除',
+ `creator_id` char(36) DEFAULT '73',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` bigint(20) DEFAULT '73',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `version` varchar(20) DEFAULT NULL COMMENT '原子能力版本',
+ `sort` int(11) DEFAULT NULL COMMENT '原子能力展示顺序',
+ `version_num` varchar(100) DEFAULT NULL COMMENT '大版本',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=274 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.c_element definition
+
+CREATE TABLE `c_element` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `element_id` varchar(100) DEFAULT NULL COMMENT '元素id',
+ `element_name` varchar(100) DEFAULT NULL COMMENT '元素名称',
+ `icon` varchar(100) DEFAULT NULL COMMENT '图标',
+ `image_id` varchar(100) DEFAULT NULL COMMENT '图片下载地址',
+ `parent_image_id` varchar(100) DEFAULT NULL COMMENT '元素的父级图片下载地址',
+ `element_data` mediumtext COMMENT '元素内容',
+ `deleted` smallint(6) DEFAULT '0',
+ `creator_id` char(36) DEFAULT NULL,
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT NULL,
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ `group_id` varchar(30) DEFAULT NULL,
+ `common_sub_type` varchar(50) DEFAULT NULL COMMENT 'cv图像, sigle普通拾取,batch数据抓取',
+ `group_name` varchar(100) DEFAULT NULL,
+ `element_type` varchar(20) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6205 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户端,元素信息';
+
+
+-- rpa.c_global_var definition
+
+CREATE TABLE `c_global_var` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `project_id` varchar(100) DEFAULT NULL,
+ `global_id` varchar(100) DEFAULT NULL,
+ `var_name` varchar(100) DEFAULT NULL,
+ `var_type` varchar(100) DEFAULT NULL,
+ `var_value` varchar(100) DEFAULT NULL,
+ `var_describe` varchar(100) DEFAULT NULL,
+ `deleted` smallint(6) DEFAULT NULL,
+ `creator_id` char(36) DEFAULT NULL,
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT NULL,
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=506 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户端-全局变量';
+
+
+-- rpa.c_group definition
+
+CREATE TABLE `c_group` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(100) DEFAULT NULL,
+ `group_name` varchar(100) DEFAULT NULL,
+ `creator_id` char(36) DEFAULT NULL,
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT NULL,
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `deleted` smallint(6) DEFAULT '0',
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ `element_type` varchar(20) DEFAULT NULL COMMENT 'cv:cv拾取; common:普通元素拾取',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1505 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='元素或图像的分组';
+
+
+-- rpa.c_module definition
+
+CREATE TABLE `c_module` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `module_id` varchar(100) DEFAULT NULL COMMENT '流程id',
+ `module_content` mediumtext COMMENT '全量python代码数据',
+ `module_name` varchar(100) DEFAULT NULL COMMENT 'python文件名',
+ `deleted` smallint(6) DEFAULT '0',
+ `creator_id` char(36) DEFAULT '73',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT '73',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=285 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户端-python模块数据';
+
+
+-- rpa.c_param definition
+
+CREATE TABLE `c_param` (
+ `id` varchar(20) NOT NULL COMMENT '参数id',
+ `var_direction` int(11) DEFAULT NULL COMMENT '输入/输出',
+ `var_name` varchar(100) DEFAULT NULL COMMENT '参数名称',
+ `var_type` varchar(100) DEFAULT NULL COMMENT '参数类型',
+ `var_value` varchar(100) DEFAULT NULL COMMENT '参数内容',
+ `var_describe` varchar(100) DEFAULT NULL COMMENT '参数描述',
+ `process_id` varchar(100) DEFAULT NULL COMMENT '流程id',
+ `creator_id` char(36) DEFAULT NULL,
+ `updater_id` char(36) DEFAULT NULL,
+ `create_time` timestamp NULL DEFAULT NULL,
+ `update_time` timestamp NULL DEFAULT NULL,
+ `deleted` int(11) DEFAULT NULL,
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ KEY `c_param_id_IDX` (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.c_process definition
+
+CREATE TABLE `c_process` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `project_id` varchar(100) DEFAULT NULL COMMENT '工程id',
+ `process_id` varchar(100) DEFAULT NULL COMMENT '流程id',
+ `process_content` mediumtext COMMENT '全量流程数据',
+ `process_name` varchar(100) DEFAULT NULL COMMENT '流程名称',
+ `deleted` smallint(6) DEFAULT '0',
+ `creator_id` char(36) DEFAULT '73',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `updater_id` char(36) DEFAULT '73',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3568 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='客户端-流程数据';
+
+
+-- rpa.c_project definition
+
+CREATE TABLE `c_project` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `project_id` varchar(100) DEFAULT NULL,
+ `project_name` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '项目名称',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除 0:未删除 1:已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='工程表';
+
+
+-- rpa.c_require definition
+
+CREATE TABLE `c_require` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `project_id` varchar(100) DEFAULT NULL,
+ `package_name` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '项目名称',
+ `package_version` varchar(20) DEFAULT NULL,
+ `mirror` varchar(100) DEFAULT NULL,
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除 0:未删除 1:已删除',
+ `robot_id` varchar(100) DEFAULT NULL,
+ `robot_version` int(11) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=116 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='python依赖管理';
+
+
+-- rpa.cloud_terminal definition
+
+CREATE TABLE `cloud_terminal` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径id',
+ `name` varchar(100) DEFAULT NULL COMMENT '终端名称',
+ `terminal_mac` varchar(100) DEFAULT NULL COMMENT '设备号,终端唯一标识',
+ `terminal_ip` varchar(100) DEFAULT NULL COMMENT 'ip',
+ `terminal_status` varchar(100) DEFAULT NULL COMMENT '当前状态,忙碌busy,空闲free,离线offline',
+ `terminal_des` varchar(100) DEFAULT NULL COMMENT '终端描述',
+ `user_id` char(36) DEFAULT NULL COMMENT '最近登陆用户id',
+ `dept_name` varchar(100) DEFAULT NULL COMMENT '部门名称',
+ `account_last` varchar(100) DEFAULT NULL COMMENT '最近登陆账号',
+ `user_name_last` varchar(100) DEFAULT NULL COMMENT '最近登陆用户名',
+ `time_last` timestamp NULL DEFAULT NULL COMMENT '最近登陆时间',
+ `execute_time_total` bigint(20) DEFAULT '0' COMMENT '单个终端累计执行时长,用于终端列表展示,更新机器人执行记录表时同步更新该表',
+ `execute_num` bigint(20) DEFAULT '0' COMMENT '单个终端累计执行次数,更新机器人执行记录表时同步更新该表',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '终端记录创建时间',
+ `terminal_type` varchar(50) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `cloud_terminal_mac_tenant_index` (`terminal_mac`,`tenant_id`),
+ KEY `cloud_terminal_tenant_id_IDX` (`tenant_id`,`dept_id_path`),
+ KEY `cloud_terminal_terminal_mac_IDX` (`terminal_mac`),
+ KEY `cloud_terminal_user_id_IDX` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='终端表';
+
+
+-- rpa.component definition
+
+CREATE TABLE `component` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `component_id` varchar(100) NOT NULL COMMENT '机器人唯一id,获取的应用id',
+ `name` varchar(100) NOT NULL COMMENT '当前名字,用于列表展示',
+ `creator_id` char(36) NOT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) NOT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `is_shown` smallint(1) NOT NULL DEFAULT '1' COMMENT '是否在用户列表页显示 0:不显示,1:显示',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `app_id` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'appmarketResource中的应用id',
+ `app_version` int(11) DEFAULT NULL COMMENT '获取的应用:应用市场版本',
+ `market_id` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '获取的应用:市场id',
+ `resource_status` varchar(20) DEFAULT NULL COMMENT '资源状态:toObtain, obtained, toUpdate',
+ `data_source` varchar(20) DEFAULT NULL COMMENT '来源:create 自己创建 ; market 市场获取 ',
+ `transform_status` varchar(20) DEFAULT NULL COMMENT 'editing 编辑中,published 已发版,shared 已上架,locked锁定(无法编辑)',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8 COMMENT='组件表';
+
+
+-- rpa.component_robot_block definition
+
+CREATE TABLE `component_robot_block` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `robot_id` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '机器人id',
+ `robot_version` int(10) NOT NULL COMMENT '机器人版本号',
+ `component_id` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '组件id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='机器人对组件屏蔽表';
+
+
+-- rpa.component_robot_use definition
+
+CREATE TABLE `component_robot_use` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `robot_id` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '机器人id',
+ `robot_version` int(10) NOT NULL COMMENT '机器人版本号',
+ `component_id` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '组件id',
+ `component_version` int(10) NOT NULL COMMENT '组件版本号',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='机器人对组件引用表';
+
+
+-- rpa.component_version definition
+
+CREATE TABLE `component_version` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `component_id` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '机器人id',
+ `version` int(10) NOT NULL COMMENT '版本号',
+ `introduction` longtext CHARACTER SET utf8 COMMENT '简介',
+ `update_log` longtext CHARACTER SET utf8 COMMENT '更新日志',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `param` text CHARACTER SET utf8,
+ `param_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '发版时拖的表单参数信息',
+ `icon` varchar(30) NOT NULL COMMENT '图标',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='组件版本表';
+
+
+-- rpa.dispatch_day_task_info definition
+
+CREATE TABLE `dispatch_day_task_info` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `terminal_id` varchar(20) DEFAULT NULL COMMENT '终端id',
+ `task_id` varchar(30) DEFAULT NULL COMMENT '任务ID',
+ `task_name` varchar(30) DEFAULT NULL COMMENT '任务名',
+ `robot_id` varchar(30) DEFAULT NULL COMMENT '机器人ID',
+ `robot_name` varchar(30) DEFAULT NULL COMMENT '机器人名',
+ `status` varchar(10) DEFAULT NULL COMMENT '当前状态 待执行 todo /已执行 done /在执行 doing',
+ `execute_time` varchar(10) DEFAULT NULL COMMENT '任务执行时间',
+ `sort` int(11) DEFAULT NULL COMMENT '排序, 越小越靠前',
+ `tenant_id` bigint(20) DEFAULT NULL,
+ `creator_id` bigint(20) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` bigint(20) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_task_id` (`task_id`),
+ KEY `idx_robot_id` (`robot_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='调度模式:终端每日上传的任务情况信息';
+
+
+-- rpa.dispatch_task definition
+
+CREATE TABLE `dispatch_task` (
+ `dispatch_task_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '调度模式计划任务id',
+ `status` varchar(10) NOT NULL DEFAULT '0' COMMENT '任务状态:启用中active、关闭stop、已过期expired',
+ `name` varchar(50) DEFAULT NULL COMMENT '调度模式计划任务名称',
+ `cron_json` mediumtext COMMENT '构建调度计划任务的灵活参数;定时schedule存计划执行的对应JSON',
+ `type` varchar(10) DEFAULT NULL COMMENT '触发条件:手动触发manual、定时schedule、定时触发trigger',
+ `exceptional` varchar(20) NOT NULL DEFAULT 'stop' COMMENT '报错如何处理:跳过jump、停止stop、重试后跳过retry_jump、重试后停止retry_stop',
+ `retry_num` int(11) DEFAULT NULL COMMENT '只有exceptional为retry时,记录的重试次数',
+ `timeout_enable` smallint(6) DEFAULT NULL COMMENT '是否启用超时时间 1:启用 0:不启用',
+ `timeout` int(11) DEFAULT '9999' COMMENT '超时时间',
+ `queue_enable` smallint(6) DEFAULT '0' COMMENT '是否启用排队 1:启用 0:不启用',
+ `screen_record_enable` smallint(6) DEFAULT '0' COMMENT '是否开启录屏 1:启用 0:不启用',
+ `virtual_desktop_enable` smallint(6) DEFAULT '0' COMMENT '是否开启虚拟桌面 1:启用 0:不启用',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`dispatch_task_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1965712054394085377 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.dispatch_task_execute_record definition
+
+CREATE TABLE `dispatch_task_execute_record` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `dispatch_task_id` bigint(20) DEFAULT NULL COMMENT '调度模式计划任务id',
+ `dispatch_task_execute_id` bigint(20) DEFAULT NULL COMMENT '调度模式计划任务执行id',
+ `count` int(11) DEFAULT NULL COMMENT '执行批次,1,2,3....',
+ `dispatch_task_type` varchar(20) DEFAULT NULL COMMENT '触发条件:手动触发manual、定时schedule、定时触发trigger',
+ `result` varchar(20) DEFAULT NULL COMMENT '执行结果枚举:成功success、失败error、执行中executing、中止cancel、下发失败dispatch_error、执行失败exe_error',
+ `start_time` datetime DEFAULT NULL COMMENT '执行开始时间',
+ `end_time` datetime DEFAULT NULL COMMENT '执行结束时间',
+ `execute_time` bigint(20) DEFAULT NULL COMMENT '执行耗时 单位秒',
+ `terminal_id` char(36) DEFAULT NULL COMMENT '终端唯一标识,如设备mac地址',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `task_detail_json` mediumtext COMMENT '任务详情',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `idx_dispatch_task_teminal_task_id` (`dispatch_task_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=113 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.dispatch_task_robot definition
+
+CREATE TABLE `dispatch_task_robot` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `dispatch_task_id` bigint(20) DEFAULT NULL COMMENT '调度模式计划任务id',
+ `robot_id` varchar(30) DEFAULT NULL COMMENT '机器人ID',
+ `online` tinyint(3) DEFAULT NULL COMMENT '是否启用版本: 0:未启用,1:已启用',
+ `version` int(11) DEFAULT NULL COMMENT '机器人版本',
+ `param_json` mediumtext COMMENT '机器人配置参数',
+ `sort` int(11) DEFAULT NULL COMMENT '排序, 越小越靠前',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `idx_dispatch_task_teminal_task_id` (`dispatch_task_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=86 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.dispatch_task_robot_execute_record definition
+
+CREATE TABLE `dispatch_task_robot_execute_record` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `execute_id` bigint(20) DEFAULT NULL COMMENT '机器人执行id',
+ `dispatch_task_execute_id` bigint(20) DEFAULT NULL COMMENT '调度模式计划任务执行id',
+ `robot_id` varchar(100) DEFAULT NULL COMMENT '机器人id',
+ `robot_version` int(11) DEFAULT NULL COMMENT '机器人版本号',
+ `start_time` timestamp NULL DEFAULT NULL COMMENT '开始时间',
+ `end_time` timestamp NULL DEFAULT NULL COMMENT '结束时间',
+ `execute_time` bigint(20) DEFAULT NULL COMMENT '执行耗时 单位秒',
+ `result` varchar(20) DEFAULT NULL COMMENT '执行结果枚举::robotFail:失败, robotSuccess:成功,robotCancel:取消(中止),robotExecute:正在执行',
+ `param_json` mediumtext COMMENT '机器人执行参数',
+ `error_reason` varchar(255) DEFAULT NULL COMMENT '错误原因',
+ `execute_log` longtext COMMENT '日志内容',
+ `video_local_path` varchar(200) DEFAULT NULL COMMENT '视频记录的本地存储路径',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径编码',
+ `terminal_id` char(36) DEFAULT NULL COMMENT '终端唯一标识,如设备mac地址',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=85 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.dispatch_task_terminal definition
+
+CREATE TABLE `dispatch_task_terminal` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `dispatch_task_id` bigint(20) DEFAULT NULL COMMENT '调度模式计划任务id',
+ `terminal_or_group` varchar(10) DEFAULT NULL COMMENT '触发条件:终端teminal、终端分组group',
+ `execute_method` varchar(10) DEFAULT NULL COMMENT '执行方式:随机一台random_one、全部执行all',
+ `value` mediumtext COMMENT '具体值:存储 list ; 其中终端对应:terminal_id(表terminal) 分组对应:id (terminal_group_name)',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `idx_dispatch_task_teminal_task_id` (`dispatch_task_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=62 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.file definition
+
+CREATE TABLE `file` (
+ `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `file_id` varchar(50) DEFAULT NULL COMMENT '文件对应的uuid',
+ `path` varchar(100) DEFAULT NULL COMMENT '文件在s3上对应的路径',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ `deleted` int(11) DEFAULT 0 COMMENT '逻辑删除标志位',
+ `file_name` varchar(1000) DEFAULT NULL COMMENT '文件真实名称',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=4406 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文件表';
+
+
+-- rpa.his_base definition
+
+CREATE TABLE `his_base` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径编码',
+ `execute_success` bigint(20) DEFAULT NULL COMMENT '累计执行成功次数',
+ `execute_fail` bigint(20) DEFAULT NULL COMMENT '累计执行失败次数',
+ `execute_abort` bigint(20) DEFAULT NULL COMMENT '累计执行中止次数',
+ `robot_num` bigint(20) DEFAULT NULL COMMENT '累计机器人总数',
+ `execute_total` bigint(20) DEFAULT NULL COMMENT '机器人累计执行次数',
+ `execute_time_total` bigint(20) DEFAULT NULL COMMENT '全部机器人或全部终端累计执行时长,单位秒,只计算成功的',
+ `execute_success_rate` decimal(5,2) DEFAULT NULL COMMENT '累计执行成功率',
+ `user_num` bigint(20) DEFAULT NULL COMMENT '累计用户数量',
+ `count_time` timestamp NULL DEFAULT NULL COMMENT '统计时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `terminal` bigint(20) DEFAULT NULL COMMENT '终端数量',
+ `labor_save` bigint(20) DEFAULT NULL COMMENT '节省的人力',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=295 DEFAULT CHARSET=utf8 COMMENT='全部机器人和全部终端趋势表';
+
+
+-- rpa.his_data_enum definition
+
+CREATE TABLE `his_data_enum` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `parent_code` varchar(100) DEFAULT NULL,
+ `icon` varchar(100) DEFAULT NULL,
+ `field` varchar(100) DEFAULT NULL,
+ `text` varchar(100) DEFAULT NULL,
+ `num` varchar(100) DEFAULT NULL,
+ `unit` varchar(100) DEFAULT NULL,
+ `percent` varchar(100) DEFAULT NULL,
+ `tip` varchar(100) DEFAULT NULL,
+ `order` bigint(20) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COMMENT='监控管理数据概览卡片配置数据枚举';
+
+
+-- rpa.his_robot definition
+
+CREATE TABLE `his_robot` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `execute_num_total` bigint(20) DEFAULT NULL COMMENT '当日执行总次数',
+ `execute_success` bigint(20) DEFAULT NULL COMMENT '每日成功次数',
+ `execute_fail` bigint(20) DEFAULT NULL COMMENT '每日失败次数',
+ `execute_abort` bigint(20) DEFAULT NULL COMMENT '每日中止次数',
+ `execute_success_rate` decimal(5,2) DEFAULT NULL COMMENT '每日成功率',
+ `execute_time` bigint(20) DEFAULT NULL COMMENT '每日执行时长,单位秒',
+ `count_time` timestamp NULL DEFAULT NULL COMMENT '统计时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `robot_id` varchar(100) DEFAULT NULL,
+ `user_id` char(36) DEFAULT NULL COMMENT '用户id',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径id',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=147 DEFAULT CHARSET=utf8 COMMENT='单个机器人趋势表,当日数据';
+
+
+-- rpa.his_terminal definition
+
+CREATE TABLE `his_terminal` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `dept_id_path` varchar(36) DEFAULT NULL COMMENT '部门全路径id',
+ `terminal_id` varchar(100) DEFAULT NULL COMMENT '设备mac',
+ `execute_time` bigint(20) DEFAULT NULL COMMENT '每日执行时长',
+ `execute_num` bigint(20) DEFAULT NULL COMMENT '终端每日执行次数',
+ `count_time` timestamp NULL DEFAULT NULL COMMENT '统计时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`),
+ KEY `his_terminal_terminal_id_IDX` (`terminal_id`) USING BTREE,
+ KEY `his_terminal_tenant_id_IDX` (`tenant_id`,`dept_id_path`) USING BTREE,
+ KEY `his_terminal_count_time_IDX` (`count_time`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 COMMENT='单个终端趋势表';
+
+
+-- rpa.notify_send definition
+
+CREATE TABLE `notify_send` (
+ `tenant_id` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '接受者所在租户',
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `user_id` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '接收者',
+ `message_info` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '消息体id',
+ `message_type` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '消息类型:邀人消息teamMarketInvite,更新消息teamMarketUpdate',
+ `operate_result` smallint(2) DEFAULT NULL COMMENT '操作结果:未读1, 已读2,已加入3,已拒绝4',
+ `market_id` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '市场id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(2) DEFAULT '0' COMMENT '删除标识',
+ `user_type` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT '成员类型:owner,admin,consumer',
+ `app_name` varchar(200) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=202 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息通知-消息表';
+
+
+-- rpa.openapi_auth definition
+
+CREATE TABLE `openapi_auth` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `name` varchar(50) DEFAULT NULL,
+ `user_id` char(36) DEFAULT NULL COMMENT '用户id',
+ `api_key` varchar(100) DEFAULT NULL,
+ `prefix` varchar(10) DEFAULT NULL,
+ `created_at` datetime DEFAULT NULL,
+ `updated_at` datetime DEFAULT NULL,
+ `is_active` tinyint(1) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `UNIQUE` (`api_key`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='openapi鉴权储存';
+
+
+-- rpa.openai_workflows definition
+
+CREATE TABLE `openai_workflows` (
+ `project_id` varchar(100) NOT NULL COMMENT '项目ID(主键)',
+ `name` varchar(100) NOT NULL COMMENT '工作流名称',
+ `description` varchar(500) DEFAULT NULL COMMENT '工作流描述',
+ `version` int(11) NOT NULL DEFAULT '1' COMMENT '工作流版本号',
+ `status` int(11) NOT NULL DEFAULT '1' COMMENT '工作流状态(1=激活,0=禁用)',
+ `user_id` varchar(50) NOT NULL COMMENT '用户ID',
+ `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `english_name` varchar(100) DEFAULT NULL COMMENT '翻译后的英文名称',
+ `parameters` text COMMENT '存储JSON字符串格式的参数',
+ PRIMARY KEY (`project_id`),
+ KEY `idx_name` (`name`),
+ KEY `idx_user_id` (`user_id`),
+ KEY `idx_status` (`status`),
+ KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+-- rpa.openai_executions definition
+
+CREATE TABLE `openai_executions` (
+ `id` varchar(36) NOT NULL COMMENT '执行记录ID(UUID)',
+ `project_id` varchar(100) NOT NULL COMMENT '项目ID(关联工作流)',
+ `status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '执行状态(PENDING/RUNNING/COMPLETED/FAILED/CANCELLED)',
+ `parameters` text COMMENT '执行参数(JSON格式)',
+ `result` text COMMENT '执行结果(JSON格式)',
+ `error` text COMMENT '错误信息',
+ `user_id` varchar(50) NOT NULL COMMENT '用户ID',
+ `exec_position` varchar(50) NOT NULL DEFAULT 'EXECUTOR' COMMENT '执行位置',
+ `version` int(11) DEFAULT NULL COMMENT '工作流版本号',
+ `start_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '开始时间',
+ `end_time` datetime DEFAULT NULL COMMENT '结束时间',
+ PRIMARY KEY (`id`),
+ KEY `idx_project_id` (`project_id`),
+ KEY `idx_user_id` (`user_id`),
+ KEY `idx_status` (`status`),
+ KEY `idx_start_time` (`start_time`),
+ CONSTRAINT `openai_executions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `openai_workflows` (`project_id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+
+-- rpa.pypi_packages definition
+
+CREATE TABLE `pypi_packages` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `package_name` varchar(255) NOT NULL,
+ `oss_path` varchar(255) NOT NULL,
+ `visibility` tinyint(1) DEFAULT '1' COMMENT 'visibility 1:公共可见包 2:个人私有包 3:灰度包,部分人可见',
+ `user_id` char(36) DEFAULT '0' COMMENT '发布用户id',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+ `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `unique_key` (`package_name`,`visibility`,`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
+-- rpa.robot_design definition
+
+CREATE TABLE `robot_design` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `robot_id` varchar(100) DEFAULT NULL COMMENT '机器人唯一id,获取的应用id',
+ `name` varchar(100) DEFAULT NULL COMMENT '当前名字,用于列表展示',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `app_id` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'appmarketResource中的应用id',
+ `app_version` int(11) DEFAULT NULL COMMENT '获取的应用:应用市场版本',
+ `market_id` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '获取的应用:市场id',
+ `resource_status` varchar(20) DEFAULT NULL COMMENT '资源状态:toObtain, obtained, toUpdate',
+ `data_source` varchar(20) DEFAULT NULL COMMENT '来源:create 自己创建 ; market 市场获取 ',
+ `transform_status` varchar(20) DEFAULT NULL COMMENT 'editing 编辑中,published 已发版,shared 已上架,locked锁定(无法编辑)',
+ `edit_enable` varchar(100) DEFAULT NULL COMMENT '废弃',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3481 DEFAULT CHARSET=utf8 COMMENT='云端机器人表';
+
+
+-- rpa.robot_execute definition
+
+CREATE TABLE `robot_execute` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `robot_id` varchar(100) DEFAULT NULL COMMENT '机器人唯一id,获取的应用id',
+ `name` varchar(100) DEFAULT NULL COMMENT '当前名字,用于列表展示',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `app_id` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'appmarketResource中的应用id',
+ `app_version` int(11) DEFAULT NULL COMMENT '获取的应用:应用市场版本',
+ `market_id` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '获取的应用:市场id',
+ `resource_status` varchar(20) DEFAULT NULL COMMENT '资源状态:toObtain, obtained, toUpdate',
+ `data_source` varchar(20) DEFAULT NULL COMMENT '来源:create 自己创建 ; market 市场获取 ;deploy卓越中心部署',
+ `param_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '运行前用户自定义的表单参数',
+ `dept_id_path` varchar(200) DEFAULT NULL COMMENT '部门全路径',
+ `type` varchar(10) DEFAULT NULL COMMENT '最新版本机器人的类型,web,other',
+ `latest_release_time` timestamp NULL DEFAULT NULL COMMENT '最新版本发版时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2667 DEFAULT CHARSET=utf8 COMMENT='云端机器人表';
+
+
+-- rpa.robot_execute_record definition
+
+CREATE TABLE `robot_execute_record` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `execute_id` varchar(30) DEFAULT NULL COMMENT '执行id',
+ `robot_id` varchar(100) DEFAULT NULL COMMENT '机器人id',
+ `robot_version` int(10) DEFAULT NULL COMMENT '机器人版本号',
+ `start_time` timestamp NULL DEFAULT NULL COMMENT '开始时间',
+ `end_time` timestamp NULL DEFAULT NULL COMMENT '结束时间',
+ `execute_time` bigint(20) DEFAULT NULL COMMENT '执行耗时 单位秒',
+ `mode` varchar(60) DEFAULT NULL COMMENT '运行位置:工程列表页PROJECT_LIST ; 工程编辑页EDIT_PAGE; 计划任务CRONTAB ; 执行器运行 EXECUTOR',
+ `task_execute_id` varchar(30) DEFAULT NULL COMMENT '计划任务执行id,即schedule_task_execute的execute_id',
+ `result` varchar(20) DEFAULT NULL COMMENT '执行结果:robotFail:失败, robotSuccess:成功,robotCancel:取消(中止),robotExecute:正在执行',
+ `error_reason` varchar(255) DEFAULT NULL COMMENT '错误原因',
+ `execute_log` longtext COMMENT '日志内容',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `video_local_path` varchar(200) DEFAULT NULL COMMENT '视频记录的本地存储路径',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径编码',
+ `terminal_id` char(36) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '终端唯一标识,如设备mac地址',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=57946 DEFAULT CHARSET=utf8 COMMENT='云端机器人执行记录表';
+
+
+-- rpa.robot_version definition
+
+CREATE TABLE `robot_version` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
+ `robot_id` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '机器人id',
+ `version` int(10) DEFAULT NULL COMMENT '版本号',
+ `introduction` longtext CHARACTER SET utf8 COMMENT '简介',
+ `update_log` longtext CHARACTER SET utf8 COMMENT '更新日志',
+ `use_description` longtext CHARACTER SET utf8 COMMENT '使用说明',
+ `online` smallint(2) DEFAULT '0' COMMENT '是否启用 0:未启用,1:已启用',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL,
+ `param` text CHARACTER SET utf8,
+ `param_detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '发版时拖的表单参数信息',
+ `video_id` varchar(100) DEFAULT NULL COMMENT '视频地址id',
+ `appendix_id` varchar(100) DEFAULT NULL COMMENT '附件地址id',
+ `icon` varchar(100) DEFAULT NULL COMMENT '图标',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2083 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='云端机器人版本表';
+
+
+-- rpa.schedule_task definition
+
+CREATE TABLE `schedule_task` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `task_id` varchar(100) DEFAULT NULL COMMENT '计划任务id',
+ `name` varchar(64) DEFAULT NULL COMMENT '任务名称',
+ `description` varchar(255) DEFAULT NULL COMMENT '描述',
+ `exception_handle_way` varchar(64) DEFAULT NULL COMMENT '异常处理方式:stop停止 skip跳过',
+ `run_mode` varchar(64) DEFAULT NULL COMMENT '执行模式,循环cycle, 定时fixed,自定义custom',
+ `cycle_frequency` varchar(10) DEFAULT NULL COMMENT '循环频率,单位秒,-1为只有一次,3600,,,custom',
+ `cycle_num` varchar(64) DEFAULT NULL COMMENT '自定义循环,循环类型,每1小时,每3小时,,自定义',
+ `cycle_unit` varchar(20) DEFAULT NULL COMMENT '自定义循环,循环单位:minutes, hour',
+ `status` varchar(64) DEFAULT NULL COMMENT '状态:doing执行中 close已结束 ready待执行',
+ `enable` tinyint(1) DEFAULT NULL COMMENT '启/禁用',
+ `schedule_type` varchar(64) DEFAULT NULL COMMENT '定时方式,day,month,week',
+ `schedule_rule` varchar(255) DEFAULT NULL COMMENT '定时配置(配置对象)',
+ `start_at` datetime DEFAULT NULL COMMENT '开始时间',
+ `end_at` datetime DEFAULT NULL COMMENT '结束时间',
+ `tenant_id` char(36) DEFAULT NULL,
+ `enable_queue_execution` tinyint(1) DEFAULT NULL COMMENT '是否排队执行',
+ `cron_expression` varchar(50) DEFAULT NULL COMMENT 'cron表达式',
+ `last_time` timestamp NULL DEFAULT NULL COMMENT '上次拉取时的nextTime',
+ `next_time` timestamp NULL DEFAULT NULL COMMENT '下次执行时间',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建人ID',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(6) DEFAULT NULL,
+ `pull_time` timestamp NULL DEFAULT NULL COMMENT '上次拉取时间',
+ `log_enable` varchar(5) CHARACTER SET utf8mb4 DEFAULT 'F' COMMENT '是否开启日志记录',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='调度任务';
+
+
+-- rpa.schedule_task_execute definition
+
+CREATE TABLE `schedule_task_execute` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `task_id` varchar(20) DEFAULT NULL COMMENT '任务ID',
+ `task_execute_id` varchar(20) DEFAULT NULL COMMENT '计划任务执行id',
+ `count` int(11) DEFAULT NULL COMMENT '执行批次,1,2,3....',
+ `result` varchar(20) DEFAULT NULL COMMENT '任务状态枚举 成功 "success" # 启动失败 "start_error" # 执行失败 "exe_error" # 取消 CANCEL = "cancel" # 执行中 "executing"',
+ `start_time` datetime DEFAULT NULL COMMENT '执行开始时间',
+ `end_time` datetime DEFAULT NULL COMMENT '执行结束时间',
+ `tenant_id` char(36) DEFAULT NULL,
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=894952 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='计划任务执行记录';
+
+
+-- rpa.schedule_task_pull_log definition
+
+CREATE TABLE `schedule_task_pull_log` (
+ `id` bigint(20) DEFAULT NULL,
+ `task_id` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '计划任务id',
+ `pull_time` timestamp NULL DEFAULT NULL COMMENT '上次拉取时间',
+ `last_time` timestamp NULL DEFAULT NULL COMMENT '上次拉取时的nextTime',
+ `next_time` timestamp NULL DEFAULT NULL COMMENT '下次执行时间',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建人ID',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.schedule_task_robot definition
+
+CREATE TABLE `schedule_task_robot` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `task_id` varchar(30) DEFAULT NULL COMMENT '任务ID',
+ `robot_id` varchar(30) DEFAULT NULL COMMENT '机器人ID',
+ `sort` int(11) DEFAULT NULL COMMENT '排序, 越小越靠前',
+ `tenant_id` char(36) DEFAULT NULL,
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `param_json` mediumtext COMMENT '计划任务相关参数',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=1070 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='计划任务机器人列表';
+
+
+-- rpa.shared_file definition
+
+CREATE TABLE `shared_file` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+ `file_id` bigint(20) DEFAULT NULL COMMENT '文件对应的uuid',
+ `path` varchar(500) DEFAULT NULL COMMENT '文件在s3上对应的路径',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `file_name` varchar(1000) DEFAULT NULL COMMENT '文件真实名称',
+ `tags` varchar(512) DEFAULT NULL COMMENT '文件标签名称集合',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者ID',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `file_type` tinyint(4) DEFAULT NULL COMMENT '文件类型: 0-位置类型 1-文本 2-WORD 3-PDF',
+ `file_index_status` tinyint(4) DEFAULT NULL COMMENT '文件向量化状态:1-初始化 2-完成 3-失败',
+ `dept_id` varchar(100) DEFAULT NULL COMMENT '部门id',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='共享文件表';
+
+
+-- rpa.shared_file_tag definition
+
+CREATE TABLE `shared_file_tag` (
+ `tag_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '标签id',
+ `tag_name` varchar(255) DEFAULT NULL COMMENT '标签真实名称',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者ID',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者ID',
+ PRIMARY KEY (`tag_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1963072518379114497 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='共享文件标签表';
+
+
+-- rpa.shared_sub_var definition
+
+CREATE TABLE `shared_sub_var` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '子变量id',
+ `shared_var_id` bigint(20) unsigned NOT NULL COMMENT '共享变量id',
+ `var_name` varchar(255) DEFAULT NULL COMMENT '子变量名',
+ `var_type` varchar(20) DEFAULT NULL COMMENT '变量类型:text/password/array',
+ `var_value` varchar(750) DEFAULT NULL COMMENT '变量具体值,加密则为密文,否则为明文',
+ `encrypt` tinyint(1) DEFAULT NULL COMMENT '是否加密:1-加密',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_shared_var_id` (`shared_var_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='共享变量-子变量';
+
+
+-- rpa.shared_var definition
+
+CREATE TABLE `shared_var` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `shared_var_name` varchar(255) DEFAULT NULL COMMENT '共享变量名',
+ `status` tinyint(1) DEFAULT NULL COMMENT '启用状态:1启用',
+ `remark` varchar(255) DEFAULT NULL COMMENT '变量说明',
+ `dept_id` char(36) DEFAULT NULL COMMENT '所屬部门ID',
+ `usage_type` varchar(10) DEFAULT NULL COMMENT '可使用账号类别(all/dept/select):所有人:all、所属部门所有人:dept、指定人:select',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `shared_var_type` varchar(20) DEFAULT NULL COMMENT '共享变量类型:text/password/array/group',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_dept_id_path` (`dept_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='共享变量信息';
+
+
+-- rpa.shared_var_key_tenant definition
+
+CREATE TABLE `shared_var_key_tenant` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `tenant_id` varchar(36) NOT NULL,
+ `key` varchar(500) DEFAULT NULL COMMENT '共享变量租户密钥',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1967981807948963842 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='共享变量租户密钥表';
+
+
+-- rpa.shared_var_user definition
+
+CREATE TABLE `shared_var_user` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `shared_var_id` bigint(20) unsigned NOT NULL COMMENT '共享变量id',
+ `user_id` char(36) DEFAULT NULL COMMENT '用户id',
+ `user_name` varchar(100) DEFAULT NULL COMMENT '用户姓名',
+ `user_phone` varchar(100) DEFAULT NULL COMMENT '用户手机号',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_shared_var_id` (`shared_var_id`),
+ KEY `idx_user_id` (`user_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='共享变量与用户的映射表;N:N映射';
+
+
+-- rpa.sms_record definition
+
+CREATE TABLE `sms_record` (
+ `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `receiver` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '短信接收者',
+ `send_type` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '短信类型',
+ `send_result` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '发送结果',
+ `fail_reason` varchar(3000) CHARACTER SET utf8 DEFAULT NULL COMMENT '失败原因',
+ `create_by` int(11) DEFAULT NULL COMMENT '创建者',
+ `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+ `update_by` int(11) DEFAULT NULL COMMENT '更新者',
+ `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+ `deleted` int(11) DEFAULT NULL COMMENT '是否已删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.task_mail definition
+
+CREATE TABLE `task_mail` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `user_id` char(36) DEFAULT NULL,
+ `tenant_id` char(36) DEFAULT NULL,
+ `resource_id` varchar(255) DEFAULT NULL,
+ `email_service` varchar(50) DEFAULT NULL COMMENT '邮箱服务器,163Email、126Email、qqEmail、customEmail',
+ `email_protocol` varchar(50) DEFAULT NULL COMMENT '使用协议,POP3,IMAP',
+ `email_service_address` varchar(255) DEFAULT NULL COMMENT '邮箱服务器地址',
+ `port` varchar(50) DEFAULT NULL COMMENT '邮箱服务器端口',
+ `enable_ssl` tinyint(1) DEFAULT NULL COMMENT '是否使用SSL',
+ `email_account` varchar(255) DEFAULT NULL COMMENT '邮箱账号',
+ `authorization_code` varchar(255) DEFAULT NULL COMMENT '邮箱授权码',
+ `deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
+-- rpa.terminal definition
+
+CREATE TABLE `terminal` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id,用于数据定时统计的进度管理',
+ `terminal_id` char(36) NOT NULL COMMENT '终端唯一标识,如设备mac地址',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `dept_id` varchar(100) DEFAULT NULL COMMENT '部门id',
+ `dept_id_path` varchar(100) DEFAULT NULL COMMENT '部门全路径id',
+ `name` varchar(200) DEFAULT NULL COMMENT '终端名称',
+ `account` varchar(100) DEFAULT NULL COMMENT '设备账号',
+ `os` varchar(50) DEFAULT NULL COMMENT '操作系统',
+ `ip` varchar(200) DEFAULT NULL COMMENT 'ip列表',
+ `actual_client_ip` varchar(100) DEFAULT NULL COMMENT '实际连接源IP,服务端检测后的推荐ip',
+ `custom_ip` varchar(20) DEFAULT NULL COMMENT '用户自定义ip',
+ `port` int(11) DEFAULT NULL COMMENT '端口',
+ `status` varchar(20) DEFAULT NULL COMMENT '当前状态,运行中busy,空闲free,离线offline,单机中standalone',
+ `remark` varchar(100) DEFAULT NULL COMMENT '终端描述',
+ `user_id` varchar(100) DEFAULT NULL COMMENT '最后登录的用户的id,用于根据姓名筛选',
+ `os_name` char(36) DEFAULT NULL COMMENT '信息维护:电脑设备用户名',
+ `os_pwd` varchar(200) DEFAULT NULL COMMENT '信息维护:电脑设备用户密码',
+ `is_dispatch` smallint(6) DEFAULT NULL COMMENT '是否调度模式',
+ `monitor_url` varchar(100) DEFAULT NULL COMMENT '视频监控url',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '终端记录创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `custom_port` int(11) DEFAULT NULL COMMENT '自定义端口',
+ PRIMARY KEY (`id`),
+ KEY `cloud_terminal_mac_tenant_index` (`tenant_id`),
+ KEY `cloud_terminal_tenant_id_IDX` (`tenant_id`,`dept_id_path`),
+ KEY `cloud_terminal_user_id_IDX` (`os_name`)
+) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='终端表';
+
+
+-- rpa.terminal_group definition
+
+CREATE TABLE `terminal_group` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `group_id` bigint(20) DEFAULT NULL COMMENT '分组名',
+ `terminal_id` bigint(20) DEFAULT NULL COMMENT '终端id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_group_id` (`group_id`),
+ KEY `idx_terminal_id` (`terminal_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='终端分组-分组与终端的映射表;N:N映射';
+
+
+-- rpa.terminal_group_info definition
+
+CREATE TABLE `terminal_group_info` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `group_name` varchar(100) DEFAULT NULL COMMENT '分组名',
+ `terminal_id` varchar(20) DEFAULT NULL COMMENT '终端id',
+ `dept_id` char(36) DEFAULT NULL COMMENT '所屬部门ID',
+ `usage_type` varchar(10) DEFAULT NULL COMMENT '可使用账号类别(all/dept/select):所有人:all、所属部门所有人:dept、指定人:select',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_terminal_id` (`terminal_id`),
+ KEY `idx_dept_id_path` (`dept_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=49 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='终端分组';
+
+
+-- rpa.terminal_group_user definition
+
+CREATE TABLE `terminal_group_user` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `group_id` varchar(20) DEFAULT NULL COMMENT '分组名',
+ `user_id` char(36) DEFAULT NULL COMMENT '用户id',
+ `creator_id` char(36) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` char(36) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ `user_name` varchar(100) DEFAULT NULL COMMENT '用户姓名',
+ `user_phone` varchar(100) DEFAULT NULL COMMENT '用户手机号',
+ PRIMARY KEY (`id`) USING BTREE,
+ KEY `idx_group_id` (`group_id`),
+ KEY `idx_user_id` (`user_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='终端分组-分组与用户的映射表;N:N映射';
+
+
+-- rpa.terminal_login_history definition
+
+CREATE TABLE `terminal_login_history` (
+ `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `terminal_id` varchar(20) DEFAULT NULL COMMENT '终端id',
+ `account` varchar(100) DEFAULT NULL COMMENT '账号',
+ `user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
+ `login_time` timestamp NULL DEFAULT NULL COMMENT '登录时间',
+ `logout_time` timestamp NULL DEFAULT NULL COMMENT '登出时间',
+ `creator_id` bigint(20) DEFAULT NULL COMMENT '创建者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater_id` bigint(20) DEFAULT NULL COMMENT '更新者id',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='终端登录账号历史记录';
+
+
+-- rpa.terminal_login_record definition
+
+CREATE TABLE `terminal_login_record` (
+ `id` char(36) COLLATE utf8mb4_bin NOT NULL,
+ `login_time` timestamp NULL DEFAULT NULL COMMENT '登录时间',
+ `logout_time` timestamp NULL DEFAULT NULL COMMENT '登出时间',
+ `terminal_id` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '终端id',
+ `dept_id` char(36) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '部门id',
+ `dept_id_path` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '部门全路径id',
+ `ip` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
+ `login_status` int(11) NOT NULL COMMENT '是否登录成功{0:登录失败,1:登录成功}',
+ `remark` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作描述',
+ `creator_id` char(36) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建者id',
+ `updater_id` char(36) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新者id',
+ `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` smallint(1) DEFAULT '0' COMMENT '是否删除 0:未删除,1:已删除',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COLLATE=utf8mb4_bin COMMENT='终端登录账号历史记录';
+
+
+-- rpa.trigger_task definition
+
+CREATE TABLE `trigger_task` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `task_id` bigint(20) DEFAULT NULL COMMENT '触发器计划任务id',
+ `name` varchar(50) DEFAULT NULL COMMENT '触发器计划任务名称',
+ `task_json` mediumtext COMMENT '构建计划任务的灵活参数',
+ `creator_id` char(36) DEFAULT NULL,
+ `updater_id` char(36) DEFAULT NULL,
+ `deleted` smallint(1) NOT NULL DEFAULT '0',
+ `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
+ `task_type` varchar(20) DEFAULT NULL COMMENT '任务类型:定时schedule、邮件mail、文件file、热键hotKey:',
+ `enable` smallint(1) NOT NULL DEFAULT '0' COMMENT '是否启用',
+ `exceptional` varchar(20) NOT NULL DEFAULT 'stop' COMMENT '报错如何处理:跳过jump、停止stop',
+ `timeout` int(10) DEFAULT '9999' COMMENT '超时时间',
+ `tenant_id` char(36) DEFAULT NULL COMMENT '租户id',
+ `queue_enable` smallint(6) DEFAULT '0' COMMENT '是否启用排队 1:启用 0:不启用',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=194 DEFAULT CHARSET=utf8 COMMENT='触发器计划任务';
\ No newline at end of file
diff --git a/docker/astronAgent/astronRPA/volumes/nginx/default.conf b/docker/astronAgent/astronRPA/volumes/nginx/default.conf
new file mode 100644
index 00000000..ec1b5de8
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/nginx/default.conf
@@ -0,0 +1,150 @@
+# nginx.conf
+resolver 127.0.0.11 valid=5s;
+
+# 模块加载路径,确保 OpenResty 能找到 resty.* 模块
+lua_package_path "/usr/local/openresty/lualib/?.lua;;;/usr/local/openresty/nginx/lua/?.lua;;";
+# 启用 LuaJIT 内存共享,用于存储一些全局配置或缓存(如果需要的话)
+lua_shared_dict my_cache 10m;
+
+# 错误日志级别设置为 debug,以便查看 Lua 脚本的详细调试信息
+# 在生产环境中,可以切换到 info 或 warn
+error_log /usr/local/openresty/nginx/logs/error.log error; # <-- !!! 调试关键 !!!
+
+# 上游服务定义,保持不变
+upstream resource-service {
+ server resource-service:8030;
+ keepalive 32;
+}
+
+upstream robot-service {
+ server robot-service:8040;
+ keepalive 32;
+}
+
+upstream ai-service {
+ server ai-service:8010;
+ keepalive 32;
+}
+
+upstream openapi-service {
+ server openapi-service:8020;
+ keepalive 32;
+}
+
+upstream casdoor {
+ server casdoor:8000;
+ keepalive 32;
+}
+
+server {
+ listen 80;
+ server_name localhost;
+
+ # 通用配置
+ client_max_body_size 100M;
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 60s;
+ proxy_read_timeout 60s;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # 定义一个方便在 Lua 脚本中判断当前上下文类型的变量
+ set $context_type "HTTP";
+
+ # resource-service 路由
+ location /api/resource/ {
+ access_by_lua_file lua/auth_handler.lua; # 调用外部 Lua 认证脚本
+
+ proxy_pass http://resource-service;
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_connect_timeout 10s;
+ proxy_send_timeout 10s;
+ proxy_read_timeout 10s;
+ }
+
+ # robot-service 路由
+ # 默认不对此路由进行认证,以避免认证服务自身的循环调用
+ # 如果需要保护 robot-service 的其他接口(排除 /user/info),则需要更细的粒度控制
+ location /api/robot/ {
+ # 如果需要认证 /api/robot/ 其他接口,但排除 /user/info
+ # if ($request_uri != "/api/robot/user/info") {
+ # access_by_lua_file lua/auth_handler.lua;
+ # }
+
+ proxy_pass http://robot-service;
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 60s;
+ proxy_read_timeout 60s;
+ }
+
+ # ai-service 路由
+ location /api/rpa-ai-service/ {
+ access_by_lua_file lua/auth_handler.lua; # 调用外部 Lua 认证脚本
+
+ rewrite ^/api/rpa-ai-service/(.*)$ /$1 break;
+ proxy_pass http://ai-service;
+ proxy_http_version 1.1;
+ proxy_set_header Connection "";
+ proxy_connect_timeout 600s;
+ proxy_send_timeout 600s;
+ proxy_read_timeout 600s;
+ }
+
+ # openapi-service 路由
+ # WebSocket 专用 location
+ location /api/rpa-openapi/ws {
+ # 为 WebSocket 认证设置上下文类型
+ set $context_type "WebSocket";
+ access_by_lua_file lua/auth_handler.lua; # 调用外部 Lua 认证脚本
+
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade"; # WebSocket 升级需要
+ proxy_http_version 1.1;
+ proxy_read_timeout 60s;
+
+ rewrite ^/api/rpa-openapi/(.*)$ /$1 break;
+ proxy_pass http://openapi-service;
+ }
+ # 通用 API location
+ location /api/rpa-openapi/ {
+ access_by_lua_file lua/auth_handler.lua; # 调用外部 Lua 认证脚本
+
+ rewrite ^/api/rpa-openapi/(.*)$ /$1 break;
+ proxy_pass http://openapi-service;
+ proxy_connect_timeout 600s;
+ proxy_send_timeout 600s;
+ proxy_read_timeout 600s;
+ }
+
+ # casdoor 路由
+ location /api/casdoor/ {
+ rewrite ^/api/casdoor/(.*)$ /$1 break;
+ proxy_pass http://casdoor;
+ proxy_connect_timeout 600s;
+ proxy_send_timeout 600s;
+ proxy_read_timeout 600s;
+ }
+
+ # 健康检查
+ location /health {
+ access_log off;
+ return 200 "healthy\n";
+ add_header Content-Type text/plain;
+ }
+
+ # 处理 favicon.ico 请求
+ location /favicon.ico {
+ access_log off;
+ return 204;
+ }
+
+ # 默认路由
+ location / {
+ return 404 "Not Found";
+ }
+}
diff --git a/docker/astronAgent/astronRPA/volumes/nginx/lua/auth_handler.lua b/docker/astronAgent/astronRPA/volumes/nginx/lua/auth_handler.lua
new file mode 100644
index 00000000..ef19db0c
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/nginx/lua/auth_handler.lua
@@ -0,0 +1,165 @@
+-- lua/auth_handler.lua
+
+local http = require("resty.http")
+local json = require("cjson")
+local ngx_log = ngx.log
+local ngx_DEBUG = ngx.DEBUG -- 用于详细调试
+local ngx_ERR = ngx.ERR
+local ngx_WARN = ngx.WARN
+local ngx_HTTP_OK = ngx.HTTP_OK
+local ngx_HTTP_UNAUTHORIZED = ngx.HTTP_UNAUTHORIZED
+local ngx_HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
+
+-- 定义一个函数来处理认证逻辑
+local function authenticate_user()
+ local ctx_type = ngx.var.context_type or "HTTP"
+ ngx_log(ngx_DEBUG, "Starting authentication for " .. ctx_type .. " request. URI: " .. ngx.var.request_uri)
+
+ local session_token = nil
+
+ -- 1. 尝试从 Authorization header 获取 Bearer Token
+ local authorization_header = ngx.req.get_headers()["authorization"] -- 注意,headers 都是小写键
+ if authorization_header then
+ ngx_log(ngx_DEBUG, "Found Authorization header: " .. authorization_header)
+ local _, _, token_type, token_value = string.find(authorization_header, "^(%S+)%s+(.+)$")
+ if token_type and token_type:lower() == "bearer" then
+ -- session_token = token_value
+ -- ngx_log(ngx_DEBUG, "Extracted Bearer Token from Authorization header: " .. session_token)
+ return
+ else
+ ngx_log(ngx_DEBUG, "Authorization header is present but not Bearer type, type: " .. (token_type or "nil"))
+ end
+ else
+ ngx_log(ngx_DEBUG, "No Authorization header found.")
+ end
+
+ -- 2. 如果 Authorization header 没有,尝试从自定义 'token' header 获取 (例如 X-Token 或 Token)
+ -- 假设你的 'http_token' 对应的是名为 'Token' 的自定义头
+ if not session_token then
+ local custom_token_header = ngx.req.get_headers()["token"]
+ if custom_token_header then
+ session_token = custom_token_header
+ ngx_log(ngx_DEBUG, "Extracted Token from custom 'Token' header: " .. session_token)
+ else
+ ngx_log(ngx_DEBUG, "No custom 'Token' header found.")
+ end
+ end
+
+ -- 3. 如果还没有token,尝试从Cookie中获取 JSESSIONID
+ if not session_token then
+ local cookie_header = ngx.var.http_cookie
+ if cookie_header then
+ ngx_log(ngx_DEBUG, "Found Cookie header: " .. cookie_header)
+ -- 解析Cookie,查找JSESSIONID
+ for cookie_pair in string.gmatch(cookie_header, "[^;]+") do
+ local cookie_name, cookie_value = string.match(cookie_pair, "^%s*(.-)%s*=%s*(.-)%s*$")
+ if cookie_name == "JSESSIONID" then
+ session_token = cookie_value
+ ngx_log(ngx_DEBUG, "Extracted Token from Cookie JSESSIONID: " .. session_token)
+ break
+ end
+ end
+ if not session_token then
+ ngx_log(ngx_DEBUG, "Cookie header present but no JSESSIONID found.")
+ end
+ else
+ ngx_log(ngx_DEBUG, "No Cookie header found.")
+ end
+ end
+
+ if not session_token or session_token == "" or session_token == " " then
+ ngx_log(ngx_ERR, "Missing SESSION/Token in " .. ctx_type .. " request after trying all sources.")
+ ngx.status = ngx_HTTP_UNAUTHORIZED
+ ngx.say(json.encode({code = "4001", msg = "Missing SESSION/Token in request"}))
+ return ngx.exit(ngx_HTTP_UNAUTHORIZED)
+ end
+
+ ngx_log(ngx_DEBUG, "Successfully extracted session_token: '" .. session_token .. "'")
+
+ -- 调用 robot-service 进行认证
+ local getUserUrl = "http://robot-service:8040/api/robot/user/now/userinfo"
+ local httpc = http.new()
+
+ -- 准备发送给 robot-service 的 Headers
+ -- 使用Cookie方式传递JSESSIONID给robot-service
+ local headers_to_robot_service = {
+ ["Content-Type"] = "application/json",
+ -- 示例:如果 robot-service 期望 Authorization 头
+ -- ["Authorization"] = "Bearer " .. session_token,
+ -- 或者,如果 robot-service 期望 Token 在 Cookie 里:
+ -- ["Cookie"] = "SESSION=" .. session_token
+ ["Cookie"] = "JSESSIONID=" .. session_token
+ }
+
+ ngx_log(ngx_DEBUG, "Calling robot-service (" .. getUserUrl .. ") with headers: " .. json.encode(headers_to_robot_service))
+
+ local res, err = httpc:request_uri(getUserUrl, {
+ method = "GET",
+ headers = headers_to_robot_service,
+ ssl_verify_host = false, -- 内部通信通常不需要 SSL 验证
+ ssl_verify_peer = false,
+ read_timeout = 5000,
+ connect_timeout = 5000
+ })
+
+ if err then
+ ngx_log(ngx_ERR, "Failed to connect to robot-service for " .. ctx_type .. " auth: " .. err .. ", URL: " .. getUserUrl)
+ ngx.status = ngx_HTTP_INTERNAL_SERVER_ERROR
+ ngx.say(json.encode({code = "5000", message = "Internal Server Error: Auth service unavailable"}))
+ return ngx.exit(ngx_HTTP_INTERNAL_SERVER_ERROR)
+ end
+
+ ngx_log(ngx_DEBUG, "robot-service response status: " .. res.status .. ", body (first 200 chars): " .. (res.body and string.sub(res.body, 1, 200) or "No body"))
+
+ if res.status ~= ngx_HTTP_OK then
+ ngx_log(ngx_ERR, "robot-service returned unexpected status " .. res.status .. " for " .. ctx_type .. " auth, full body: " .. (res.body or "No body"))
+ ngx.status = res.status
+ ngx.say(res.body) -- 将 robot-service 的错误响应直接返回
+ return ngx.exit(res.status)
+ end
+
+ local userResponse, json_err = json.decode(res.body)
+ if json_err then
+ ngx_log(ngx_ERR, "Failed to decode robot-service response for " .. ctx_type .. " auth: " .. json_err .. ", full body: " .. (res.body or "No body"))
+ ngx.status = ngx_HTTP_INTERNAL_SERVER_ERROR
+ ngx.say(json.encode({code = "5000", message = "Internal Server Error: Invalid auth service response"}))
+ return ngx.exit(ngx_HTTP_INTERNAL_SERVER_ERROR)
+ end
+
+ ngx_log(ngx_DEBUG, "Decoded robot-service response: " .. json.encode(userResponse))
+
+ if userResponse.code ~= 200 then
+ ngx_log(ngx_ERR, "robot-service returned error code: " .. (userResponse.code or "nil") .. ", message: " .. (userResponse.message or "nil") .. " for " .. ctx_type .. " auth. Full response: " .. json.encode(userResponse))
+ ngx.status = ngx_HTTP_UNAUTHORIZED
+ ngx.say(json.encode({
+ code = userResponse.code or "U_AUTH_FAIL",
+ data = userResponse.data,
+ message = userResponse.status or "Authentication failed by robot-service"
+ }))
+ return ngx.exit(ngx_HTTP_UNAUTHORIZED)
+ end
+
+ local user_id = userResponse.data and userResponse.data["id"]
+ if not user_id then
+ ngx_log(ngx_ERR, "robot-service response missing 'id' in 'data' field for " .. ctx_type .. " auth: " .. json.encode(userResponse))
+ ngx.status = ngx_HTTP_INTERNAL_SERVER_ERROR
+ ngx.say(json.encode({code = "5000", message = "Internal Server Error: Auth service response missing user_id"}))
+ return ngx.exit(ngx_HTTP_INTERNAL_SERVER_ERROR)
+ end
+
+ ngx_log(ngx_WARN, "User authenticated successfully. user_id: " .. user_id .. " in " .. ctx_type .. " context. Setting headers.")
+ ngx.req.set_header("user_id", user_id)
+ ngx.req.set_header("user-info", json.encode({id = user_id}))
+
+ return true -- 认证成功
+end
+
+-- 在 access_by_lua_file 执行时,脚本会直接运行。
+-- 所以我们需要直接调用 authenticate_user 函数。
+local _M = {
+ authenticate_user = authenticate_user
+}
+
+_M.authenticate_user()
+
+return _M
diff --git a/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http.lua b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http.lua
new file mode 100644
index 00000000..a85f85af
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http.lua
@@ -0,0 +1,1185 @@
+local http_headers = require "resty.http_headers"
+
+local ngx = ngx
+local ngx_socket_tcp = ngx.socket.tcp
+local ngx_req = ngx.req
+local ngx_req_socket = ngx_req.socket
+local ngx_req_get_headers = ngx_req.get_headers
+local ngx_req_get_method = ngx_req.get_method
+local str_lower = string.lower
+local str_upper = string.upper
+local str_find = string.find
+local str_sub = string.sub
+local tbl_concat = table.concat
+local tbl_insert = table.insert
+local ngx_encode_args = ngx.encode_args
+local ngx_re_match = ngx.re.match
+local ngx_re_gmatch = ngx.re.gmatch
+local ngx_re_sub = ngx.re.sub
+local ngx_re_gsub = ngx.re.gsub
+local ngx_re_find = ngx.re.find
+local ngx_log = ngx.log
+local ngx_DEBUG = ngx.DEBUG
+local ngx_ERR = ngx.ERR
+local ngx_var = ngx.var
+local ngx_print = ngx.print
+local ngx_header = ngx.header
+local co_yield = coroutine.yield
+local co_create = coroutine.create
+local co_status = coroutine.status
+local co_resume = coroutine.resume
+local setmetatable = setmetatable
+local tonumber = tonumber
+local tostring = tostring
+local unpack = unpack
+local rawget = rawget
+local select = select
+local ipairs = ipairs
+local pairs = pairs
+local pcall = pcall
+local type = type
+
+
+-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
+local HOP_BY_HOP_HEADERS = {
+ ["connection"] = true,
+ ["keep-alive"] = true,
+ ["proxy-authenticate"] = true,
+ ["proxy-authorization"] = true,
+ ["te"] = true,
+ ["trailers"] = true,
+ ["transfer-encoding"] = true,
+ ["upgrade"] = true,
+ ["content-length"] = true, -- Not strictly hop-by-hop, but Nginx will deal
+ -- with this (may send chunked for example).
+}
+
+
+local EXPECTING_BODY = {
+ POST = true,
+ PUT = true,
+ PATCH = true,
+}
+
+
+-- Reimplemented coroutine.wrap, returning "nil, err" if the coroutine cannot
+-- be resumed. This protects user code from infinite loops when doing things like
+-- repeat
+-- local chunk, err = res.body_reader()
+-- if chunk then -- <-- This could be a string msg in the core wrap function.
+-- ...
+-- end
+-- until not chunk
+local co_wrap = function(func)
+ local co = co_create(func)
+ if not co then
+ return nil, "could not create coroutine"
+ else
+ return function(...)
+ if co_status(co) == "suspended" then
+ return select(2, co_resume(co, ...))
+ else
+ return nil, "can't resume a " .. co_status(co) .. " coroutine"
+ end
+ end
+ end
+end
+
+
+-- Returns a new table, recursively copied from the one given.
+--
+-- @param table table to be copied
+-- @return table
+local function tbl_copy(orig)
+ local orig_type = type(orig)
+ local copy
+ if orig_type == "table" then
+ copy = {}
+ for orig_key, orig_value in next, orig, nil do
+ copy[tbl_copy(orig_key)] = tbl_copy(orig_value)
+ end
+ else -- number, string, boolean, etc
+ copy = orig
+ end
+ return copy
+end
+
+
+local _M = {
+ _VERSION = '0.17.2',
+}
+_M._USER_AGENT = "lua-resty-http/" .. _M._VERSION .. " (Lua) ngx_lua/" .. ngx.config.ngx_lua_version
+
+local mt = { __index = _M }
+
+
+local HTTP = {
+ [1.0] = " HTTP/1.0\r\n",
+ [1.1] = " HTTP/1.1\r\n",
+}
+
+
+local DEFAULT_PARAMS = {
+ method = "GET",
+ path = "/",
+ version = 1.1,
+}
+
+
+local DEBUG = false
+
+
+function _M.new(_)
+ local sock, err = ngx_socket_tcp()
+ if not sock then
+ return nil, err
+ end
+ return setmetatable({ sock = sock, keepalive = true }, mt)
+end
+
+
+function _M.debug(d)
+ DEBUG = (d == true)
+end
+
+
+function _M.set_timeout(self, timeout)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ return sock:settimeout(timeout)
+end
+
+
+function _M.set_timeouts(self, connect_timeout, send_timeout, read_timeout)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ return sock:settimeouts(connect_timeout, send_timeout, read_timeout)
+end
+
+do
+ local aio_connect = require "resty.http_connect"
+ -- Function signatures to support:
+ -- ok, err, ssl_session = httpc:connect(options_table)
+ -- ok, err = httpc:connect(host, port, options_table?)
+ -- ok, err = httpc:connect("unix:/path/to/unix.sock", options_table?)
+ function _M.connect(self, options, ...)
+ if type(options) == "table" then
+ -- all-in-one interface
+ return aio_connect(self, options)
+ else
+ -- backward compatible
+ return self:tcp_only_connect(options, ...)
+ end
+ end
+end
+
+function _M.tcp_only_connect(self, ...)
+ ngx_log(ngx_DEBUG, "Use of deprecated `connect` method signature")
+
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ self.host = select(1, ...)
+ self.port = select(2, ...)
+
+ -- If port is not a number, this is likely a unix domain socket connection.
+ if type(self.port) ~= "number" then
+ self.port = nil
+ end
+
+ self.keepalive = true
+ self.ssl = false
+
+ return sock:connect(...)
+end
+
+
+function _M.set_keepalive(self, ...)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ if self.keepalive == true then
+ return sock:setkeepalive(...)
+ else
+ -- The server said we must close the connection, so we cannot setkeepalive.
+ -- If close() succeeds we return 2 instead of 1, to differentiate between
+ -- a normal setkeepalive() failure and an intentional close().
+ local res, err = sock:close()
+ if res then
+ return 2, "connection must be closed"
+ else
+ return res, err
+ end
+ end
+end
+
+
+function _M.get_reused_times(self)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ return sock:getreusedtimes()
+end
+
+
+function _M.close(self)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ return sock:close()
+end
+
+
+local function _should_receive_body(method, code)
+ if method == "HEAD" then return nil end
+ if code == 204 or code == 304 then return nil end
+ if code >= 100 and code < 200 then return nil end
+ return true
+end
+
+
+function _M.parse_uri(_, uri, query_in_path)
+ if query_in_path == nil then query_in_path = true end
+
+ local m, err = ngx_re_match(
+ uri,
+ [[^(?:(http[s]?):)?//((?:[^\[\]:/\?]+)|(?:\[.+\]))(?::(\d+))?([^\?]*)\??(.*)]],
+ "jo"
+ )
+
+ if not m then
+ if err then
+ return nil, "failed to match the uri: " .. uri .. ", " .. err
+ end
+
+ return nil, "bad uri: " .. uri
+ else
+ -- If the URI is schemaless (i.e. //example.com) try to use our current
+ -- request scheme.
+ if not m[1] then
+ -- Schema-less URIs can occur in client side code, implying "inherit
+ -- the schema from the current request". We support it for a fairly
+ -- specific case; if for example you are using the ESI parser in
+ -- ledge (https://github.com/ledgetech/ledge) to perform in-flight
+ -- sub requests on the edge based on instructions found in markup,
+ -- those URIs may also be schemaless with the intention that the
+ -- subrequest would inherit the schema just like JavaScript would.
+ local scheme = ngx_var.scheme
+ if scheme == "http" or scheme == "https" then
+ m[1] = scheme
+ else
+ return nil, "schemaless URIs require a request context: " .. uri
+ end
+ end
+
+ if m[3] then
+ m[3] = tonumber(m[3])
+ else
+ if m[1] == "https" then
+ m[3] = 443
+ else
+ m[3] = 80
+ end
+ end
+ if not m[4] or "" == m[4] then m[4] = "/" end
+
+ if query_in_path and m[5] and m[5] ~= "" then
+ m[4] = m[4] .. "?" .. m[5]
+ m[5] = nil
+ end
+
+ return m, nil
+ end
+end
+
+
+local function _format_request(self, params)
+ local version = params.version
+ local headers = params.headers or {}
+
+ local query = params.query or ""
+ if type(query) == "table" then
+ query = ngx_encode_args(query)
+ end
+
+ if query ~= "" and str_sub(query, 1, 1) ~= "?" then
+ query = "?" .. query
+ end
+
+ -- Initialize request
+ local req = {
+ str_upper(params.method),
+ " ",
+ self.path_prefix or "",
+ params.path,
+ query,
+ HTTP[version],
+ -- Pre-allocate slots for minimum headers and carriage return.
+ true,
+ true,
+ true,
+ }
+ local c = 7 -- req table index it's faster to do this inline vs table.insert
+
+ -- Append headers
+ for key, values in pairs(headers) do
+ key = tostring(key)
+
+ if type(values) == "table" then
+ for _, value in pairs(values) do
+ req[c] = key .. ": " .. tostring(value) .. "\r\n"
+ c = c + 1
+ end
+
+ else
+ req[c] = key .. ": " .. tostring(values) .. "\r\n"
+ c = c + 1
+ end
+ end
+
+ -- Close headers
+ req[c] = "\r\n"
+
+ return tbl_concat(req)
+end
+
+
+local function _receive_status(sock)
+ local line, err = sock:receive("*l")
+ if not line then
+ return nil, nil, nil, err
+ end
+
+ local version = tonumber(str_sub(line, 6, 8))
+ if not version then
+ return nil, nil, nil,
+ "couldn't parse HTTP version from response status line: " .. line
+ end
+
+ local status = tonumber(str_sub(line, 10, 12))
+ if not status then
+ return nil, nil, nil,
+ "couldn't parse status code from response status line: " .. line
+ end
+
+ local reason = str_sub(line, 14)
+
+ return status, version, reason
+end
+
+
+local function _receive_headers(sock)
+ local headers = http_headers.new()
+
+ repeat
+ local line, err = sock:receive("*l")
+ if not line then
+ return nil, err
+ end
+
+ local m, err = ngx_re_match(line, "([^:\\s]+):\\s*(.*)", "jo")
+ if err then ngx_log(ngx_ERR, err) end
+
+ if not m then
+ break
+ end
+
+ local key = m[1]
+ local val = m[2]
+ if headers[key] then
+ if type(headers[key]) ~= "table" then
+ headers[key] = { headers[key] }
+ end
+ tbl_insert(headers[key], tostring(val))
+ else
+ headers[key] = tostring(val)
+ end
+ until ngx_re_find(line, "^\\s*$", "jo")
+
+ return headers, nil
+end
+
+
+local function transfer_encoding_is_chunked(headers)
+ local te = headers["Transfer-Encoding"]
+ if not te then
+ return false
+ end
+
+ -- Handle duplicate headers
+ -- This shouldn't happen but can in the real world
+ if type(te) ~= "string" then
+ te = tbl_concat(te, ",")
+ end
+
+ return str_find(str_lower(te), "chunked", 1, true) ~= nil
+end
+_M.transfer_encoding_is_chunked = transfer_encoding_is_chunked
+
+
+local function _chunked_body_reader(sock, default_chunk_size)
+ return co_wrap(function(max_chunk_size)
+ local remaining = 0
+ local length
+ max_chunk_size = max_chunk_size or default_chunk_size
+
+ repeat
+ -- If we still have data on this chunk
+ if max_chunk_size and remaining > 0 then
+
+ if remaining > max_chunk_size then
+ -- Consume up to max_chunk_size
+ length = max_chunk_size
+ remaining = remaining - max_chunk_size
+ else
+ -- Consume all remaining
+ length = remaining
+ remaining = 0
+ end
+ else -- This is a fresh chunk
+
+ -- Receive the chunk size
+ local str, err = sock:receive("*l")
+ if not str then
+ co_yield(nil, err)
+ end
+
+ length = tonumber(str, 16)
+
+ if not length then
+ co_yield(nil, "unable to read chunksize")
+ end
+
+ if max_chunk_size and length > max_chunk_size then
+ -- Consume up to max_chunk_size
+ remaining = length - max_chunk_size
+ length = max_chunk_size
+ end
+ end
+
+ if length > 0 then
+ local str, err = sock:receive(length)
+ if not str then
+ co_yield(nil, err)
+ end
+
+ max_chunk_size = co_yield(str) or default_chunk_size
+
+ -- If we're finished with this chunk, read the carriage return.
+ if remaining == 0 then
+ sock:receive(2) -- read \r\n
+ end
+ else
+ -- Read the last (zero length) chunk's carriage return
+ sock:receive(2) -- read \r\n
+ end
+
+ until length == 0
+ end)
+end
+
+
+local function _body_reader(sock, content_length, default_chunk_size)
+ return co_wrap(function(max_chunk_size)
+ max_chunk_size = max_chunk_size or default_chunk_size
+
+ if not content_length and max_chunk_size then
+ -- We have no length, but wish to stream.
+ -- HTTP 1.0 with no length will close connection, so read chunks to the end.
+ repeat
+ local str, err, partial = sock:receive(max_chunk_size)
+ if not str and err == "closed" then
+ co_yield(partial, err)
+ end
+
+ max_chunk_size = tonumber(co_yield(str) or default_chunk_size)
+ if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end
+
+ if not max_chunk_size then
+ ngx_log(ngx_ERR, "Buffer size not specified, bailing")
+ break
+ end
+ until not str
+
+ elseif not content_length then
+ -- We have no length but don't wish to stream.
+ -- HTTP 1.0 with no length will close connection, so read to the end.
+ co_yield(sock:receive("*a"))
+
+ elseif not max_chunk_size then
+ -- We have a length and potentially keep-alive, but want everything.
+ co_yield(sock:receive(content_length))
+
+ else
+ -- We have a length and potentially a keep-alive, and wish to stream
+ -- the response.
+ local received = 0
+ repeat
+ local length = max_chunk_size
+ if received + length > content_length then
+ length = content_length - received
+ end
+
+ if length > 0 then
+ local str, err = sock:receive(length)
+ if not str then
+ co_yield(nil, err)
+ end
+ received = received + length
+
+ max_chunk_size = tonumber(co_yield(str) or default_chunk_size)
+ if max_chunk_size and max_chunk_size < 0 then max_chunk_size = nil end
+
+ if not max_chunk_size then
+ ngx_log(ngx_ERR, "Buffer size not specified, bailing")
+ break
+ end
+ end
+
+ until length == 0
+ end
+ end)
+end
+
+
+local function _no_body_reader()
+ return nil
+end
+
+
+local function _read_body(res)
+ local reader = res.body_reader
+
+ if not reader then
+ -- Most likely HEAD or 304 etc.
+ return nil, "no body to be read"
+ end
+
+ local chunks = {}
+ local c = 1
+
+ local chunk, err
+ repeat
+ chunk, err = reader()
+
+ if err then
+ return nil, err, tbl_concat(chunks) -- Return any data so far.
+ end
+ if chunk then
+ chunks[c] = chunk
+ c = c + 1
+ end
+ until not chunk
+
+ return tbl_concat(chunks)
+end
+
+
+local function _trailer_reader(sock)
+ return co_wrap(function()
+ co_yield(_receive_headers(sock))
+ end)
+end
+
+
+local function _read_trailers(res)
+ local reader = res.trailer_reader
+ if not reader then
+ return nil, "no trailers"
+ end
+
+ local trailers = reader()
+ setmetatable(res.headers, { __index = trailers })
+end
+
+
+local function _send_body(sock, body)
+ if type(body) == "function" then
+ repeat
+ local chunk, err, partial = body()
+
+ if chunk then
+ local ok, err = sock:send(chunk)
+
+ if not ok then
+ return nil, err
+ end
+ elseif err ~= nil then
+ return nil, err, partial
+ end
+
+ until chunk == nil
+ elseif body ~= nil then
+ local bytes, err = sock:send(body)
+
+ if not bytes then
+ return nil, err
+ end
+ end
+ return true, nil
+end
+
+
+local function _handle_continue(sock, body)
+ local status, version, reason, err = _receive_status(sock) --luacheck: no unused
+ if not status then
+ return nil, nil, nil, err
+ end
+
+ -- Only send body if we receive a 100 Continue
+ if status == 100 then
+ -- Read headers
+ local headers, err = _receive_headers(sock)
+ if not headers then
+ return nil, nil, nil, err
+ end
+
+ local ok, err = _send_body(sock, body)
+ if not ok then
+ return nil, nil, nil, err
+ end
+ end
+ return status, version, reason, err
+end
+
+
+function _M.send_request(self, params)
+ -- Apply defaults
+ setmetatable(params, { __index = DEFAULT_PARAMS })
+
+ local sock = self.sock
+ local body = params.body
+ local headers = http_headers.new()
+
+ -- We assign one-by-one so that the metatable can handle case insensitivity
+ -- for us. You can blame the spec for this inefficiency.
+ local params_headers = params.headers or {}
+ for k, v in pairs(params_headers) do
+ headers[k] = v
+ end
+
+ if not headers["Proxy-Authorization"] then
+ -- TODO: next major, change this to always override the provided
+ -- header. Can't do that yet because it would be breaking.
+ -- The connect method uses self.http_proxy_auth in the poolname so
+ -- that should be leading.
+ headers["Proxy-Authorization"] = self.http_proxy_auth
+ end
+
+ -- Ensure we have appropriate message length or encoding.
+ do
+ local is_chunked = transfer_encoding_is_chunked(headers)
+
+ if is_chunked then
+ -- If we have both Transfer-Encoding and Content-Length we MUST
+ -- drop the Content-Length, to help prevent request smuggling.
+ -- https://tools.ietf.org/html/rfc7230#section-3.3.3
+ headers["Content-Length"] = nil
+
+ elseif not headers["Content-Length"] then
+ -- A length was not given, try to calculate one.
+
+ local body_type = type(body)
+
+ if body_type == "function" then
+ return nil, "Request body is a function but a length or chunked encoding is not specified"
+
+ elseif body_type == "table" then
+ local length = 0
+ for _, v in ipairs(body) do
+ length = length + #tostring(v)
+ end
+ headers["Content-Length"] = length
+
+ elseif body == nil and EXPECTING_BODY[str_upper(params.method)] then
+ headers["Content-Length"] = 0
+
+ elseif body ~= nil then
+ headers["Content-Length"] = #tostring(body)
+ end
+ end
+ end
+
+ if not headers["Host"] then
+ if (str_sub(self.host, 1, 5) == "unix:") then
+ return nil, "Unable to generate a useful Host header for a unix domain socket. Please provide one."
+ end
+ -- If we have a port (i.e. not connected to a unix domain socket), and this
+ -- port is non-standard, append it to the Host header.
+ if self.port then
+ if self.ssl and self.port ~= 443 then
+ headers["Host"] = self.host .. ":" .. self.port
+ elseif not self.ssl and self.port ~= 80 then
+ headers["Host"] = self.host .. ":" .. self.port
+ else
+ headers["Host"] = self.host
+ end
+ else
+ headers["Host"] = self.host
+ end
+ end
+ if not headers["User-Agent"] then
+ headers["User-Agent"] = _M._USER_AGENT
+ end
+ if params.version == 1.0 and not headers["Connection"] then
+ headers["Connection"] = "Keep-Alive"
+ end
+
+ params.headers = headers
+
+ -- Format and send request
+ local req = _format_request(self, params)
+ if DEBUG then ngx_log(ngx_DEBUG, "\n", req) end
+ local bytes, err = sock:send(req)
+
+ if not bytes then
+ return nil, err
+ end
+
+ -- Send the request body, unless we expect: continue, in which case
+ -- we handle this as part of reading the response.
+ if headers["Expect"] ~= "100-continue" then
+ local ok, err, partial = _send_body(sock, body)
+ if not ok then
+ return nil, err, partial
+ end
+ end
+
+ return true
+end
+
+
+function _M.read_response(self, params)
+ local sock = self.sock
+
+ local status, version, reason, err
+
+ -- If we expect: continue, we need to handle this, sending the body if allowed.
+ -- If we don't get 100 back, then status is the actual status.
+ if params.headers["Expect"] == "100-continue" then
+ local _status, _version, _reason, _err = _handle_continue(sock, params.body)
+ if not _status then
+ return nil, _err
+ elseif _status ~= 100 then
+ status, version, reason, err = _status, _version, _reason, _err -- luacheck: no unused
+ end
+ end
+
+ -- Just read the status as normal.
+ if not status then
+ status, version, reason, err = _receive_status(sock)
+ if not status then
+ return nil, err
+ end
+ end
+
+
+ local res_headers, err = _receive_headers(sock)
+ if not res_headers then
+ return nil, err
+ end
+
+ -- keepalive is true by default. Determine if this is correct or not.
+ local ok, connection = pcall(str_lower, res_headers["Connection"])
+ if ok then
+ if (version == 1.1 and str_find(connection, "close", 1, true)) or
+ (version == 1.0 and not str_find(connection, "keep-alive", 1, true)) then
+ self.keepalive = false
+ end
+ else
+ -- no connection header
+ if version == 1.0 then
+ self.keepalive = false
+ end
+ end
+
+ local body_reader = _no_body_reader
+ local trailer_reader, err
+ local has_body = false
+
+ -- Receive the body_reader
+ if _should_receive_body(params.method, status) then
+ has_body = true
+
+ if version == 1.1 and transfer_encoding_is_chunked(res_headers) then
+ body_reader, err = _chunked_body_reader(sock)
+ else
+ local ok, length = pcall(tonumber, res_headers["Content-Length"])
+ if not ok then
+ -- No content-length header, read until connection is closed by server
+ length = nil
+ end
+
+ body_reader, err = _body_reader(sock, length)
+ end
+ end
+
+ if res_headers["Trailer"] then
+ trailer_reader, err = _trailer_reader(sock)
+ end
+
+ if err then
+ return nil, err
+ else
+ return {
+ status = status,
+ reason = reason,
+ headers = res_headers,
+ has_body = has_body,
+ body_reader = body_reader,
+ read_body = _read_body,
+ trailer_reader = trailer_reader,
+ read_trailers = _read_trailers,
+ }
+ end
+end
+
+
+function _M.request(self, params)
+ params = tbl_copy(params) -- Take by value
+ local res, err = self:send_request(params)
+ if not res then
+ return res, err
+ else
+ return self:read_response(params)
+ end
+end
+
+
+function _M.request_pipeline(self, requests)
+ requests = tbl_copy(requests) -- Take by value
+
+ for _, params in ipairs(requests) do
+ if params.headers and params.headers["Expect"] == "100-continue" then
+ return nil, "Cannot pipeline request specifying Expect: 100-continue"
+ end
+
+ local res, err = self:send_request(params)
+ if not res then
+ return res, err
+ end
+ end
+
+ local responses = {}
+ for i, params in ipairs(requests) do
+ responses[i] = setmetatable({
+ params = params,
+ response_read = false,
+ }, {
+ -- Read each actual response lazily, at the point the user tries
+ -- to access any of the fields.
+ __index = function(t, k)
+ local res, err
+ if t.response_read == false then
+ res, err = _M.read_response(self, t.params)
+ t.response_read = true
+
+ if not res then
+ ngx_log(ngx_ERR, err)
+ else
+ for rk, rv in pairs(res) do
+ t[rk] = rv
+ end
+ end
+ end
+ return rawget(t, k)
+ end,
+ })
+ end
+ return responses
+end
+
+
+function _M.request_uri(self, uri, params)
+ params = tbl_copy(params or {}) -- Take by value
+ if self.proxy_opts then
+ params.proxy_opts = tbl_copy(self.proxy_opts or {})
+ end
+
+ do
+ local parsed_uri, err = self:parse_uri(uri, false)
+ if not parsed_uri then
+ return nil, err
+ end
+
+ local path, query
+ params.scheme, params.host, params.port, path, query = unpack(parsed_uri)
+ params.path = params.path or path
+ params.query = params.query or query
+ params.ssl_server_name = params.ssl_server_name or params.host
+ end
+
+ do
+ local proxy_auth = (params.headers or {})["Proxy-Authorization"]
+ if proxy_auth and params.proxy_opts then
+ params.proxy_opts.https_proxy_authorization = proxy_auth
+ params.proxy_opts.http_proxy_authorization = proxy_auth
+ end
+ end
+
+ local ok, err = self:connect(params)
+ if not ok then
+ return nil, err
+ end
+
+ local res, err = self:request(params)
+ if not res then
+ self:close()
+ return nil, err
+ end
+
+ local body, err = res:read_body()
+ if not body then
+ self:close()
+ return nil, err
+ end
+
+ res.body = body
+
+ if params.keepalive == false then
+ local ok, err = self:close()
+ if not ok then
+ ngx_log(ngx_ERR, err)
+ end
+
+ else
+ local ok, err = self:set_keepalive(params.keepalive_timeout, params.keepalive_pool)
+ if not ok then
+ ngx_log(ngx_ERR, err)
+ end
+
+ end
+
+ return res, nil
+end
+
+
+function _M.get_client_body_reader(_, chunksize, sock)
+ chunksize = chunksize or 65536
+
+ if not sock then
+ local ok, err
+ ok, sock, err = pcall(ngx_req_socket)
+
+ if not ok then
+ return nil, sock -- pcall err
+ end
+
+ if not sock then
+ if err == "no body" then
+ return nil
+ else
+ return nil, err
+ end
+ end
+ end
+
+ local headers = ngx_req_get_headers()
+ local length = headers.content_length
+ if length then
+ return _body_reader(sock, tonumber(length), chunksize)
+ elseif transfer_encoding_is_chunked(headers) then
+ -- Not yet supported by ngx_lua but should just work...
+ return _chunked_body_reader(sock, chunksize)
+ else
+ return nil
+ end
+end
+
+
+function _M.set_proxy_options(self, opts)
+ -- TODO: parse and cache these options, instead of parsing them
+ -- on each request over and over again (lru-cache on module level)
+ self.proxy_opts = tbl_copy(opts) -- Take by value
+end
+
+
+function _M.get_proxy_uri(self, scheme, host)
+ if not self.proxy_opts then
+ return nil
+ end
+
+ -- Check if the no_proxy option matches this host. Implementation adapted
+ -- from lua-http library (https://github.com/daurnimator/lua-http)
+ if self.proxy_opts.no_proxy then
+ if self.proxy_opts.no_proxy == "*" then
+ -- all hosts are excluded
+ return nil
+ end
+
+ local no_proxy_set = {}
+ -- wget allows domains in no_proxy list to be prefixed by "."
+ -- e.g. no_proxy=.mit.edu
+ for host_suffix in ngx_re_gmatch(self.proxy_opts.no_proxy, "\\.?([^,]+)", "jo") do
+ no_proxy_set[host_suffix[1]] = true
+ end
+
+ -- From curl docs:
+ -- matched as either a domain which contains the hostname, or the
+ -- hostname itself. For example local.com would match local.com,
+ -- local.com:80, and www.local.com, but not www.notlocal.com.
+ --
+ -- Therefore, we keep stripping subdomains from the host, compare
+ -- them to the ones in the no_proxy list and continue until we find
+ -- a match or until there's only the TLD left
+ repeat
+ if no_proxy_set[host] then
+ return nil
+ end
+
+ -- Strip the next level from the domain and check if that one
+ -- is on the list
+ host = ngx_re_sub(host, "^[^.]+\\.", "", "jo")
+ until not ngx_re_find(host, "\\.", "jo")
+ end
+
+ if scheme == "http" and self.proxy_opts.http_proxy then
+ return self.proxy_opts.http_proxy
+ end
+
+ if scheme == "https" and self.proxy_opts.https_proxy then
+ return self.proxy_opts.https_proxy
+ end
+
+ return nil
+end
+
+
+-- ----------------------------------------------------------------------------
+-- The following functions are considered DEPRECATED and may be REMOVED in
+-- future releases. Please see the notes in `README.md`.
+-- ----------------------------------------------------------------------------
+
+function _M.ssl_handshake(self, ...)
+ ngx_log(ngx_DEBUG, "Use of deprecated function `ssl_handshake`")
+
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ self.ssl = true
+
+ return sock:sslhandshake(...)
+end
+
+
+function _M.connect_proxy(self, proxy_uri, scheme, host, port, proxy_authorization)
+ ngx_log(ngx_DEBUG, "Use of deprecated function `connect_proxy`")
+
+ -- Parse the provided proxy URI
+ local parsed_proxy_uri, err = self:parse_uri(proxy_uri, false)
+ if not parsed_proxy_uri then
+ return nil, err
+ end
+
+ -- Check that the scheme is http (https is not supported for
+ -- connections between the client and the proxy)
+ local proxy_scheme = parsed_proxy_uri[1]
+ if proxy_scheme ~= "http" then
+ return nil, "protocol " .. proxy_scheme .. " not supported for proxy connections"
+ end
+
+ -- Make the connection to the given proxy
+ local proxy_host, proxy_port = parsed_proxy_uri[2], parsed_proxy_uri[3]
+ local c, err = self:tcp_only_connect(proxy_host, proxy_port)
+ if not c then
+ return nil, err
+ end
+
+ if scheme == "https" then
+ -- Make a CONNECT request to create a tunnel to the destination through
+ -- the proxy. The request-target and the Host header must be in the
+ -- authority-form of RFC 7230 Section 5.3.3. See also RFC 7231 Section
+ -- 4.3.6 for more details about the CONNECT request
+ local destination = host .. ":" .. port
+ local res, err = self:request({
+ method = "CONNECT",
+ path = destination,
+ headers = {
+ ["Host"] = destination,
+ ["Proxy-Authorization"] = proxy_authorization,
+ }
+ })
+
+ if not res then
+ return nil, err
+ end
+
+ if res.status < 200 or res.status > 299 then
+ return nil, "failed to establish a tunnel through a proxy: " .. res.status
+ end
+ end
+
+ return c, nil
+end
+
+
+function _M.proxy_request(self, chunksize)
+ ngx_log(ngx_DEBUG, "Use of deprecated function `proxy_request`")
+
+ return self:request({
+ method = ngx_req_get_method(),
+ path = ngx_re_gsub(ngx_var.uri, "\\s", "%20", "jo") .. ngx_var.is_args .. (ngx_var.query_string or ""),
+ body = self:get_client_body_reader(chunksize),
+ headers = ngx_req_get_headers(),
+ })
+end
+
+
+function _M.proxy_response(_, response, chunksize)
+ ngx_log(ngx_DEBUG, "Use of deprecated function `proxy_response`")
+
+ if not response then
+ ngx_log(ngx_ERR, "no response provided")
+ return
+ end
+
+ ngx.status = response.status
+
+ -- Filter out hop-by-hop headeres
+ for k, v in pairs(response.headers) do
+ if not HOP_BY_HOP_HEADERS[str_lower(k)] then
+ ngx_header[k] = v
+ end
+ end
+
+ local reader = response.body_reader
+
+ repeat
+ local chunk, ok, read_err, print_err
+
+ chunk, read_err = reader(chunksize)
+ if read_err then
+ ngx_log(ngx_ERR, read_err)
+ end
+
+ if chunk then
+ ok, print_err = ngx_print(chunk)
+ if not ok then
+ ngx_log(ngx_ERR, print_err)
+ end
+ end
+
+ if read_err or print_err then
+ break
+ end
+ until not chunk
+end
+
+
+return _M
diff --git a/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_connect.lua b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_connect.lua
new file mode 100644
index 00000000..83b6d2f4
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_connect.lua
@@ -0,0 +1,341 @@
+local ffi = require "ffi"
+local ngx_re_gmatch = ngx.re.gmatch
+local ngx_re_sub = ngx.re.sub
+local ngx_re_find = ngx.re.find
+local ngx_log = ngx.log
+local ngx_WARN = ngx.WARN
+local ngx_DEBUG = ngx.DEBUG
+local to_hex = require("resty.string").to_hex
+local ffi_gc = ffi.gc
+local ffi_cast = ffi.cast
+local type = type
+
+local lib_chain, lib_x509, lib_pkey
+local openssl_available, res = xpcall(function()
+ lib_chain = require("resty.openssl.x509.chain")
+ lib_x509 = require("resty.openssl.x509")
+ lib_pkey = require("resty.openssl.pkey")
+end, debug.traceback)
+
+if not openssl_available then
+ ngx_log(ngx_WARN, "failed to load module `resty.openssl.*`, \z
+ mTLS isn't supported without lua-resty-openssl:\n", res)
+end
+
+--[[
+A connection function that incorporates:
+ - tcp connect
+ - ssl handshake
+ - http proxy
+Due to this it will be better at setting up a socket pool where connections can
+be kept alive.
+
+
+Call it with a single options table as follows:
+
+client:connect {
+ scheme = "https" -- scheme to use, or nil for unix domain socket
+ host = "myhost.com", -- target machine, or a unix domain socket
+ port = nil, -- port on target machine, will default to 80/443 based on scheme
+ pool = nil, -- connection pool name, leave blank! this function knows best!
+ pool_size = nil, -- options as per: https://github.com/openresty/lua-nginx-module#tcpsockconnect
+ backlog = nil,
+
+ -- ssl options as per: https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake
+ ssl_reused_session = nil
+ ssl_server_name = nil,
+ ssl_send_status_req = nil,
+ ssl_verify = true, -- NOTE: defaults to true
+ ctx = nil, -- NOTE: not supported
+
+ -- mTLS options: These require support for mTLS in cosockets, which first
+ -- appeared in `ngx_http_lua_module` v0.10.23.
+ ssl_client_cert = nil,
+ ssl_client_priv_key = nil,
+
+ proxy_opts, -- proxy opts, defaults to global proxy options
+}
+]]
+local function connect(self, options)
+ local sock = self.sock
+ if not sock then
+ return nil, "not initialized"
+ end
+
+ local ok, err
+
+ local request_scheme = options.scheme
+ local request_host = options.host
+ local request_port = options.port
+
+ local poolname = options.pool
+ local pool_size = options.pool_size
+ local backlog = options.backlog
+
+ if request_scheme and not request_port then
+ request_port = (request_scheme == "https" and 443 or 80)
+ elseif request_port and not request_scheme then
+ return nil, "'scheme' is required when providing a port"
+ end
+
+ -- ssl settings
+ local ssl, ssl_reused_session, ssl_server_name
+ local ssl_verify, ssl_send_status_req, ssl_client_cert, ssl_client_priv_key
+ if request_scheme == "https" then
+ ssl = true
+ ssl_reused_session = options.ssl_reused_session
+ ssl_server_name = options.ssl_server_name
+ ssl_send_status_req = options.ssl_send_status_req
+ ssl_verify = true -- default
+ if options.ssl_verify == false then
+ ssl_verify = false
+ end
+ ssl_client_cert = options.ssl_client_cert
+ ssl_client_priv_key = options.ssl_client_priv_key
+ end
+
+ -- proxy related settings
+ local proxy, proxy_uri, proxy_authorization, proxy_host, proxy_port, path_prefix
+ proxy = options.proxy_opts or self.proxy_opts
+
+ if proxy then
+ if request_scheme == "https" then
+ proxy_uri = proxy.https_proxy
+ proxy_authorization = proxy.https_proxy_authorization
+ else
+ proxy_uri = proxy.http_proxy
+ proxy_authorization = proxy.http_proxy_authorization
+ -- When a proxy is used, the target URI must be in absolute-form
+ -- (RFC 7230, Section 5.3.2.). That is, it must be an absolute URI
+ -- to the remote resource with the scheme, host and an optional port
+ -- in place.
+ --
+ -- Since _format_request() constructs the request line by concatenating
+ -- params.path and params.query together, we need to modify the path
+ -- to also include the scheme, host and port so that the final form
+ -- in conformant to RFC 7230.
+ path_prefix = "http://" .. request_host .. (request_port == 80 and "" or (":" .. request_port))
+ end
+ if not proxy_uri then
+ proxy = nil
+ proxy_authorization = nil
+ path_prefix = nil
+ end
+ end
+
+ if proxy and proxy.no_proxy then
+ -- Check if the no_proxy option matches this host. Implementation adapted
+ -- from lua-http library (https://github.com/daurnimator/lua-http)
+ if proxy.no_proxy == "*" then
+ -- all hosts are excluded
+ proxy = nil
+
+ else
+ local host = request_host
+ local no_proxy_set = {}
+ -- wget allows domains in no_proxy list to be prefixed by "."
+ -- e.g. no_proxy=.mit.edu
+ for host_suffix in ngx_re_gmatch(proxy.no_proxy, "\\.?([^,]+)") do
+ no_proxy_set[host_suffix[1]] = true
+ end
+
+ -- From curl docs:
+ -- matched as either a domain which contains the hostname, or the
+ -- hostname itself. For example local.com would match local.com,
+ -- local.com:80, and www.local.com, but not www.notlocal.com.
+ --
+ -- Therefore, we keep stripping subdomains from the host, compare
+ -- them to the ones in the no_proxy list and continue until we find
+ -- a match or until there's only the TLD left
+ repeat
+ if no_proxy_set[host] then
+ proxy = nil
+ proxy_uri = nil
+ proxy_authorization = nil
+ break
+ end
+
+ -- Strip the next level from the domain and check if that one
+ -- is on the list
+ host = ngx_re_sub(host, "^[^.]+\\.", "")
+ until not ngx_re_find(host, "\\.")
+ end
+ end
+
+ if proxy then
+ local proxy_uri_t
+ proxy_uri_t, err = self:parse_uri(proxy_uri)
+ if not proxy_uri_t then
+ return nil, "uri parse error: " .. err
+ end
+
+ local proxy_scheme = proxy_uri_t[1]
+ if proxy_scheme ~= "http" then
+ return nil, "protocol " .. tostring(proxy_scheme) ..
+ " not supported for proxy connections"
+ end
+ proxy_host = proxy_uri_t[2]
+ proxy_port = proxy_uri_t[3]
+ end
+
+ local cert_hash
+ if ssl and ssl_client_cert and ssl_client_priv_key then
+ local cert_type = type(ssl_client_cert)
+ local key_type = type(ssl_client_priv_key)
+
+ if cert_type ~= "cdata" then
+ return nil, "bad ssl_client_cert: cdata expected, got " .. cert_type
+ end
+
+ if key_type ~= "cdata" then
+ return nil, "bad ssl_client_priv_key: cdata expected, got " .. key_type
+ end
+
+ if not openssl_available then
+ return nil, "module `resty.openssl.*` not available, mTLS isn't supported without lua-resty-openssl"
+ end
+
+ -- convert from `void*` to `OPENSSL_STACK*`
+ local cert_chain, err = lib_chain.dup(ffi_cast("OPENSSL_STACK*", ssl_client_cert))
+ if not cert_chain then
+ return nil, "failed to dup the ssl_client_cert: " .. err
+ end
+
+ if #cert_chain < 1 then
+ return nil, "no cert in ssl_client_cert"
+ end
+
+ local cert, err = lib_x509.dup(cert_chain[1].ctx)
+ if not cert then
+ return nil, "failed to dup the x509: " .. err
+ end
+
+ -- convert from `void*` to `EVP_PKEY*`
+ local key, err = lib_pkey.new(ffi_cast("EVP_PKEY*", ssl_client_priv_key))
+ if not key then
+ return nil, "failed to new the pkey: " .. err
+ end
+
+ -- should not free the cdata passed in
+ ffi_gc(key.ctx, nil)
+
+ -- check the private key in order to make sure the caller is indeed the holder of the cert
+ ok, err = cert:check_private_key(key)
+ if not ok then
+ return nil, "the private key doesn't match the cert: " .. err
+ end
+
+ cert_hash, err = cert:digest("sha256")
+ if not cert_hash then
+ return nil, "failed to calculate the digest of the cert: " .. err
+ end
+
+ cert_hash = to_hex(cert_hash) -- convert to hex so that it's printable
+ end
+
+ -- construct a poolname unique within proxy and ssl info
+ if not poolname then
+ poolname = (request_scheme or "")
+ .. ":" .. request_host
+ .. ":" .. tostring(request_port)
+ .. ":" .. tostring(ssl)
+ .. ":" .. (ssl_server_name or "")
+ .. ":" .. tostring(ssl_verify)
+ .. ":" .. (proxy_uri or "")
+ .. ":" .. (request_scheme == "https" and proxy_authorization or "")
+ .. ":" .. (cert_hash or "")
+ -- in the above we only add the 'proxy_authorization' as part of the poolname
+ -- when the request is https. Because in that case the CONNECT request (which
+ -- carries the authorization header) is part of the connect procedure, whereas
+ -- with a plain http request the authorization is part of the actual request.
+ end
+
+ ngx_log(ngx_DEBUG, "poolname: ", poolname)
+
+ -- do TCP level connection
+ local tcp_opts = { pool = poolname, pool_size = pool_size, backlog = backlog }
+ if proxy then
+ -- proxy based connection
+ ok, err = sock:connect(proxy_host, proxy_port, tcp_opts)
+ if not ok then
+ return nil, "failed to connect to: " .. (proxy_host or "") ..
+ ":" .. (proxy_port or "") ..
+ ": " .. err
+ end
+
+ if ssl and sock:getreusedtimes() == 0 then
+ -- Make a CONNECT request to create a tunnel to the destination through
+ -- the proxy. The request-target and the Host header must be in the
+ -- authority-form of RFC 7230 Section 5.3.3. See also RFC 7231 Section
+ -- 4.3.6 for more details about the CONNECT request
+ local destination = request_host .. ":" .. request_port
+ local res
+ res, err = self:request({
+ method = "CONNECT",
+ path = destination,
+ headers = {
+ ["Host"] = destination,
+ ["Proxy-Authorization"] = proxy_authorization,
+ }
+ })
+
+ if not res then
+ return nil, "failed to issue CONNECT to proxy: " .. err
+ end
+
+ if res.status < 200 or res.status > 299 then
+ return nil, "failed to establish a tunnel through a proxy: " .. res.status
+ end
+ end
+
+ elseif not request_port then
+ -- non-proxy, without port -> unix domain socket
+ ok, err = sock:connect(request_host, tcp_opts)
+ if not ok then
+ return nil, err
+ end
+
+ else
+ -- non-proxy, regular network tcp
+ ok, err = sock:connect(request_host, request_port, tcp_opts)
+ if not ok then
+ return nil, err
+ end
+ end
+
+ local ssl_session
+ -- Now do the ssl handshake
+ if ssl and sock:getreusedtimes() == 0 then
+
+ -- Experimental mTLS support
+ if ssl_client_cert and ssl_client_priv_key then
+ if type(sock.setclientcert) ~= "function" then
+ return nil, "cannot use SSL client cert and key without mTLS support"
+
+ else
+ ok, err = sock:setclientcert(ssl_client_cert, ssl_client_priv_key)
+ if not ok then
+ return nil, "could not set client certificate: " .. err
+ end
+ end
+ end
+
+ ssl_session, err = sock:sslhandshake(ssl_reused_session, ssl_server_name, ssl_verify, ssl_send_status_req)
+ if not ssl_session then
+ self:close()
+ return nil, err
+ end
+ end
+
+ self.host = request_host
+ self.port = request_port
+ self.keepalive = true
+ self.ssl = ssl
+ -- set only for http, https has already been handled
+ self.http_proxy_auth = request_scheme ~= "https" and proxy_authorization or nil
+ self.path_prefix = path_prefix
+
+ return true, nil, ssl_session
+end
+
+return connect
diff --git a/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_headers.lua b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_headers.lua
new file mode 100644
index 00000000..6394e619
--- /dev/null
+++ b/docker/astronAgent/astronRPA/volumes/nginx/lua/resty/http_headers.lua
@@ -0,0 +1,44 @@
+local rawget, rawset, setmetatable =
+ rawget, rawset, setmetatable
+
+local str_lower = string.lower
+
+local _M = {
+ _VERSION = '0.17.2',
+}
+
+
+-- Returns an empty headers table with internalised case normalisation.
+function _M.new()
+ local mt = {
+ normalised = {},
+ }
+
+ mt.__index = function(t, k)
+ return rawget(t, mt.normalised[str_lower(k)])
+ end
+
+ mt.__newindex = function(t, k, v)
+ local k_normalised = str_lower(k)
+
+ -- First time seeing this header field?
+ if not mt.normalised[k_normalised] then
+ -- Create a lowercased entry in the metatable proxy, with the value
+ -- of the given field case
+ mt.normalised[k_normalised] = k
+
+ -- Set the header using the given field case
+ rawset(t, k, v)
+ else
+ -- We're being updated just with a different field case. Use the
+ -- normalised metatable proxy to give us the original key case, and
+ -- perorm a rawset() to update the value.
+ rawset(t, mt.normalised[k_normalised], v)
+ end
+ end
+
+ return setmetatable({}, mt)
+end
+
+
+return _M
diff --git a/docker/astronAgent/docker-compose-with-auth.yml b/docker/astronAgent/docker-compose-auth.yml
similarity index 82%
rename from docker/astronAgent/docker-compose-with-auth.yml
rename to docker/astronAgent/docker-compose-auth.yml
index aee70bc1..672ab356 100644
--- a/docker/astronAgent/docker-compose-with-auth.yml
+++ b/docker/astronAgent/docker-compose-auth.yml
@@ -38,6 +38,13 @@ services:
timeout: 20s
retries: 10
+# ============================================================================
+# Network Configuration
+# ============================================================================
+networks:
+ astron-agent-network:
+ driver: bridge
+
volumes:
casdoor-logs:
casdoor-mysql-data:
\ No newline at end of file
diff --git a/docker/astronAgent/docker-compose-with-auth.yaml b/docker/astronAgent/docker-compose-with-auth.yaml
new file mode 100644
index 00000000..4cacf2e8
--- /dev/null
+++ b/docker/astronAgent/docker-compose-with-auth.yaml
@@ -0,0 +1,607 @@
+include:
+ - docker-compose-auth.yml
+
+services:
+ # ============================================================================
+ # Infrastructure Services
+ # ============================================================================
+
+ # PostgreSQL Database
+ postgres:
+ image: postgres:14
+ container_name: astron-agent-postgres
+ environment:
+ POSTGRES_DB: sparkdb_manager
+ POSTGRES_USER: ${POSTGRES_USER:-spark}
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-spark123}
+ PGDATA: /var/lib/postgresql/data/pgdata
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ - ./pgsql/:/docker-entrypoint-initdb.d/
+ networks:
+ - astron-agent-network
+ restart: always
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-spark} -d sparkdb_manager"]
+ interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # MySQL Database
+ mysql:
+ image: mysql:8.4
+ container_name: astron-agent-mysql
+ environment:
+ MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
+ volumes:
+ - mysql_data:/var/lib/mysql
+ - ./mysql/:/docker-entrypoint-initdb.d/
+ networks:
+ - astron-agent-network
+ restart: always
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
+ interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # Redis Cache
+ redis:
+ image: redis:7
+ container_name: astron-agent-redis
+ volumes:
+ - redis_data:/data
+ networks:
+ - astron-agent-network
+ restart: always
+ command: redis-server ${REDIS_PASSWORD:+--requirepass} ${REDIS_PASSWORD}
+ healthcheck:
+ test: ["CMD-SHELL", "redis-cli ${REDIS_PASSWORD:+-a \"$REDIS_PASSWORD\"} ping | grep PONG"]
+ interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # Elasticsearch Search Engine (Disabled by default, uncomment to enable)
+ # elasticsearch:
+ # image: elasticsearch:7.16.2
+ # container_name: astron-agent-elasticsearch
+ # environment:
+ # - discovery.type=single-node
+ # - "ES_JAVA_OPTS=${ES_JAVA_OPTS:--Xms512m -Xmx512m}"
+ # - xpack.security.enabled=${ELASTICSEARCH_SECURITY_ENABLED:-false}
+ # - cluster.name=astron-agent-cluster
+ # volumes:
+ # - elasticsearch_data:/usr/share/elasticsearch/data
+ # networks:
+ # - astron-agent-network
+ # restart: always
+ # healthcheck:
+ # test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
+ # interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ # timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ # retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # Kafka Message Queue (Disabled by default, uncomment to enable)
+ # kafka:
+ # image: apache/kafka:3.7.0
+ # container_name: astron-agent-kafka
+ # environment:
+ # KAFKA_NODE_ID: 1
+ # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
+ # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092
+ # KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:29092,CONTROLLER://0.0.0.0:29093
+ # KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
+ # KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
+ # KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
+ # KAFKA_PROCESS_ROLES: broker,controller
+ # KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
+ # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
+ # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${KAFKA_REPLICATION_FACTOR:-1}
+ # CLUSTER_ID: ${KAFKA_CLUSTER_ID:-MkU3OEVBNTcwNTJENDM2Qk}
+ # volumes:
+ # - kafka_data:/var/lib/kafka/data
+ # networks:
+ # - astron-agent-network
+ # restart: always
+ # healthcheck:
+ # test: ["CMD-SHELL", "netstat -tulpn | grep 29092 || exit 1"]
+ # interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ # timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ # retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # MinIO Object Storage
+ minio:
+ image: minio/minio:RELEASE.2025-07-23T15-54-02Z
+ container_name: astron-agent-minio
+ environment:
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin123}
+ ports:
+ - "${EXPOSE_MINIO_PORT:-9000}:9000"
+ - "${EXPOSE_MINIO_CONSOLE_PORT:-9001}:9001"
+ volumes:
+ - minio_data:/data
+ networks:
+ - astron-agent-network
+ restart: always
+ command: server /data --console-address ":9001"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
+ interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # ============================================================================
+ # astron-agent Core Services
+ # ============================================================================
+
+ # Tenant Service
+ core-tenant:
+ image: ghcr.io/iflytek/astron-agent/core-tenant:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-tenant
+ environment:
+ SERVICE_PORT: "${CORE_TENANT_PORT:-5052}"
+ SERVICE_LOCATION: "${SERVICE_LOCATION:-hf}"
+ DATABASE_DB_TYPE: "${DATABASE_DB_TYPE:-mysql}"
+ DATABASE_USERNAME: "${DATABASE_USERNAME:-root}"
+ DATABASE_PASSWORD: "${DATABASE_PASSWORD:-root123}"
+ DATABASE_URL: "${DATABASE_URL:-(localhost:3306)/tenant}"
+ DATABASE_MAX_OPEN_CONNS: "${DATABASE_MAX_OPEN_CONNS:-5}"
+ DATABASE_MAX_IDLE_CONNS: "${DATABASE_MAX_IDLE_CONNS:-5}"
+ LOG_PATH: "${LOG_PATH:-log.txt}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/tenant/logs:/opt/tenant/logs
+ - ./config/tenant/config.toml:/opt/tenant/config/config.toml
+ - /etc/localtime:/etc/localtime
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # Memory Database Service
+
+ core-database:
+ image: ghcr.io/iflytek/astron-agent/core-database:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-database
+ environment:
+ SERVICE_PORT: "${CORE_DATABASE_PORT:-7990}"
+ PGSQL_HOST: "${POSTGRES_HOST:-postgres}"
+ PGSQL_PORT: "${POSTGRES_PORT:-5432}"
+ PGSQL_USER: "${POSTGRES_USER:-spark}"
+ PGSQL_PASSWORD: "${POSTGRES_PASSWORD:-spark123}"
+ PGSQL_DATABASE: "${DATABASE_POSTGRES_DATABASE:-sparkdb_manager}"
+ OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/database/config.env:/opt/core/memory/database/config.env
+ - ./config/database/logs/:/opt/core/memory/database/logs
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # RPA Plugin Service
+ core-rpa:
+ image: ghcr.io/iflytek/astron-agent/core-rpa:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-rpa
+ environment:
+ SERVICE_PORT: "${CORE_RPA_PORT:-17198}"
+ OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
+ KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
+ XIAOWU_RPA_TASK_CREATE_URL: "${XIAOWU_RPA_TASK_CREATE_URL:-https://newapi.iflyrpa.com/api/rpa-openapi/workflows/execute-async}"
+ XIAOWU_RPA_TASK_QUERY_URL: "${XIAOWU_RPA_TASK_QUERY_URL:-https://newapi.iflyrpa.com/api/rpa-openapi/executions}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/rpa/config.env:/opt/core/plugin/rpa/config.env
+ - ./config/rpa/logs/:/opt/core/logs/
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # Link Plugin Service
+ core-link:
+ image: ghcr.io/iflytek/astron-agent/core-link:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-link
+ environment:
+ SERVICE_PORT: "${CORE_LINK_PORT:-18888}"
+ MYSQL_HOST: "${MYSQL_HOST:-mysql}"
+ MYSQL_PORT: "${MYSQL_PORT:-3306}"
+ MYSQL_USER: "${MYSQL_USER:-root}"
+ MYSQL_PASSWORD: "${MYSQL_PASSWORD:-root123}"
+ MYSQL_DB: "${LINK_MYSQL_DB:-spark-link}"
+ REDIS_IS_CLUSTER: "${REDIS_IS_CLUSTER:-false}"
+ REDIS_ADDR: "${REDIS_ADDR:-redis:6379}"
+ REDIS_CLUSTER_ADDR: "${REDIS_CLUSTER_ADDR}"
+ REDIS_PASSWORD: "${REDIS_PASSWORD}"
+ OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
+ KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/link/config.env:/opt/core/plugin/link/config.env
+ - ./config/link/logs/:/opt/core/plugin/link/logs/
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # AI Tools Plugin Service
+ core-aitools:
+ image: ghcr.io/iflytek/astron-agent/core-aitools:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-aitools
+ environment:
+ SERVICE_PORT: "${CORE_AITOOLS_PORT:-18668}"
+ OSS_TYPE: "${OSS_TYPE:-s3}"
+ OSS_ENDPOINT: "${OSS_ENDPOINT:-http://minio:9000}"
+ OSS_ACCESS_KEY_ID: "${OSS_ACCESS_KEY_ID:-minioadmin}"
+ OSS_ACCESS_KEY_SECRET: "${OSS_ACCESS_KEY_SECRET:-minioadmin123}"
+ OSS_BUCKET_NAME: "${OSS_BUCKET_NAME}"
+ OSS_DOWNLOAD_HOST: "${OSS_REMOTE_ENDPOINT}"
+ OSS_TTL: "${OSS_TTL:-157788000}"
+ KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
+ AI_APP_ID: "${PLATFORM_APP_ID}"
+ AI_API_KEY: "${PLATFORM_API_KEY}"
+ AI_API_SECRET: "${PLATFORM_API_SECRET}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/aitools/config.env:/opt/core/plugin/aitools/config.env
+ - ./config/aitools/logs/:/opt/core/logs/
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # Agent Service
+ core-agent:
+ image: ghcr.io/iflytek/astron-agent/core-agent:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-agent
+ environment:
+ SERVICE_LOCATION: "${SERVICE_LOCATION:-hf}"
+ SERVICE_HOST: "${SERVICE_HOST:-0.0.0.0}"
+ SERVICE_PORT: "${CORE_AGENT_PORT:-17870}"
+ SERVICE_WORKERS: "${SERVICE_WORKERS:-1}"
+ SERVICE_RELOAD: "${SERVICE_RELOAD:-false}"
+ SERVICE_WS_PING_INTERVAL: "${SERVICE_WS_PING_INTERVAL:-false}"
+ SERVICE_WS_PING_TIMEOUT: "${SERVICE_WS_PING_TIMEOUT:-false}"
+ REDIS_CLUSTER_ADDR: "${REDIS_CLUSTER_ADDR}"
+ REDIS_ADDR: "${REDIS_ADDR:-redis:6379}"
+ REDIS_PASSWORD: "${REDIS_PASSWORD}"
+ REDIS_EXPIRE: "${REDIS_EXPIRE:-3600}"
+ MYSQL_HOST: "${MYSQL_HOST:-mysql}"
+ MYSQL_PORT: "${MYSQL_PORT:-3306}"
+ MYSQL_USER: "${MYSQL_USER:-root}"
+ MYSQL_PASSWORD: "${MYSQL_PASSWORD:-root123}"
+ MYSQL_DB: "${AGENT_MYSQL_DB:-agent}"
+ OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_METRIC_TIMEOUT: "${OTLP_METRIC_TIMEOUT:-3000}"
+ OTLP_METRIC_EXPORT_INTERVAL_MILLIS: "${OTLP_METRIC_EXPORT_INTERVAL_MILLIS:-3000}"
+ OTLP_METRIC_EXPORT_TIMEOUT_MILLIS: "${OTLP_METRIC_EXPORT_TIMEOUT_MILLIS:-3000}"
+ UPLOAD_NODE_TRACE: "${UPLOAD_NODE_TRACE:-true}"
+ UPLOAD_METRICS: "${UPLOAD_METRICS:-true}"
+ OTLP_TRACE_TIMEOUT: "${OTLP_TRACE_TIMEOUT:-3000}"
+ OTLP_TRACE_MAX_QUEUE_SIZE: "${OTLP_TRACE_MAX_QUEUE_SIZE:-2048}"
+ OTLP_TRACE_SCHEDULE_DELAY_MILLIS: "${OTLP_TRACE_SCHEDULE_DELAY_MILLIS:-3000}"
+ OTLP_TRACE_MAX_EXPORT_BATCH_SIZE: "${OTLP_TRACE_MAX_EXPORT_BATCH_SIZE:-2048}"
+ OTLP_TRACE_EXPORT_TIMEOUT_MILLIS: "${OTLP_TRACE_EXPORT_TIMEOUT_MILLIS:-3000}"
+ KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
+ KAFKA_TIMEOUT: "${KAFKA_TIMEOUT:-60}"
+ KAFKA_TOPIC: "${AGENT_KAFKA_TOPIC:-spark-agent-builder}"
+ GET_LINK_URL: "${GET_LINK_URL:-http://core-link:18888/api/v1/tools}"
+ VERSIONS_LINK_URL: "${VERSIONS_LINK_URL:-http://core-link:18888/api/v1/tools/versions}"
+ RUN_LINK_URL: "${RUN_LINK_URL:-http://core-link:18888/api/v1/tools/http_run}"
+ GET_WORKFLOWS_URL: "${GET_WORKFLOWS_URL:-http://core-workflow:7880/sparkflow/v1/protocol/get}"
+ WORKFLOW_SSE_BASE_URL: "${WORKFLOW_SSE_BASE_URL:-http://core-workflow:7880/workflow/v1}"
+ CHUNK_QUERY_URL: "${CHUNK_QUERY_URL:-http://core-knowledge:20010/knowledge/v1/chunk/query}"
+ LIST_MCP_PLUGIN_URL: "${LIST_MCP_PLUGIN_URL:-http://core-link:18888/api/v1/mcp/tool_list}"
+ RUN_MCP_PLUGIN_URL: "${RUN_MCP_PLUGIN_URL:-http://core-link:18888/api/v1/mcp/call_tool}"
+ APP_AUTH_HOST: "${APP_AUTH_HOST:-core-tenant}"
+ APP_AUTH_PROT: "${APP_AUTH_PROT:-http}"
+ APP_AUTH_API_KEY: "${APP_AUTH_API_KEY:-YOUR_APP_AUTH_API_KEY}"
+ APP_AUTH_SECRET: "${APP_AUTH_SECRET:-YOUR_APP_AUTH_SECRET}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/agent/config.env:/opt/core/agent/config.env
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # Knowledge Base Service
+ core-knowledge:
+ image: ghcr.io/iflytek/astron-agent/core-knowledge:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-knowledge
+ environment:
+ SERVICE_PORT: "${CORE_KNOWLEDGE_PORT:-20010}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
+ RAGFLOW_BASE_URL: "${RAGFLOW_BASE_URL}"
+ RAGFLOW_API_TOKEN: "${RAGFLOW_API_TOKEN}"
+ RAGFLOW_TIMEOUT: "${RAGFLOW_TIMEOUT:-60}"
+ RAGFLOW_DEFAULT_GROUP: "${RAGFLOW_DEFAULT_GROUP}"
+ XINGHUO_DATASET_ID: "${XINGHUO_DATASET_ID:-}"
+ XINGHUO_APP_ID: "${PLATFORM_APP_ID}"
+ XINGHUO_APP_SECRET: "${PLATFORM_API_SECRET}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/knowledge/config.env:/opt/core/knowledge/config.env
+ - ./config/knowledge/logs/:/opt/core/logs
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # Workflow Service
+ core-workflow:
+ image: ghcr.io/iflytek/astron-agent/core-workflow:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-core-workflow
+ environment:
+ RUNTIME_ENV: "${RUNTIME_ENV:-dev}"
+ SERVICE_PORT: "${CORE_WORKFLOW_PORT:-7880}"
+ MYSQL_HOST: "${MYSQL_HOST:-mysql}"
+ MYSQL_PORT: "${MYSQL_PORT:-3306}"
+ MYSQL_USER: "${MYSQL_USER:-root}"
+ MYSQL_PASSWORD: "${MYSQL_PASSWORD:-root123}"
+ MYSQL_DB: "${WORKFLOW_MYSQL_DB:-workflow}"
+ REDIS_CLUSTER_ADDR: "${REDIS_CLUSTER_ADDR}"
+ REDIS_ADDR: "${REDIS_ADDR:-redis:6379}"
+ REDIS_PASSWORD: "${REDIS_PASSWORD}"
+ REDIS_EXPIRE: "${REDIS_EXPIRE:-3600}"
+ OTLP_ENDPOINT: "${OTLP_ENDPOINT:-127.0.0.1:4317}"
+ OTLP_ENABLE: "${OTLP_ENABLE:-0}"
+ OTLP_METRIC_EXPORT_INTERVAL_MILLIS: "${OTLP_METRIC_EXPORT_INTERVAL_MILLIS:-3000}"
+ OTLP_METRIC_EXPORT_TIMEOUT_MILLIS: "${OTLP_METRIC_EXPORT_TIMEOUT_MILLIS:-3000}"
+ OTLP_METRIC_TIMEOUT: "${OTLP_METRIC_TIMEOUT:-3000}"
+ OTLP_TRACE_TIMEOUT: "${OTLP_TRACE_TIMEOUT:-3000}"
+ OTLP_TRACE_MAX_QUEUE_SIZE: "${OTLP_TRACE_MAX_QUEUE_SIZE:-2048}"
+ OTLP_TRACE_SCHEDULE_DELAY_MILLIS: "${OTLP_TRACE_SCHEDULE_DELAY_MILLIS:-3000}"
+ OTLP_TRACE_MAX_EXPORT_BATCH_SIZE: "${OTLP_TRACE_MAX_EXPORT_BATCH_SIZE:-500}"
+ OTLP_TRACE_EXPORT_TIMEOUT_MILLIS: "${OTLP_TRACE_EXPORT_TIMEOUT_MILLIS:-3000}"
+ OSS_TYPE: "${OSS_TYPE:-s3}"
+ OSS_ENDPOINT: "${OSS_ENDPOINT:-http://minio:9000}"
+ OSS_ACCESS_KEY_ID: "${OSS_ACCESS_KEY_ID:-minioadmin}"
+ OSS_ACCESS_KEY_SECRET: "${OSS_ACCESS_KEY_SECRET:-minioadmin123}"
+ OSS_BUCKET_NAME: "${OSS_BUCKET_NAME}"
+ OSS_DOWNLOAD_HOST: "${OSS_REMOTE_ENDPOINT}"
+ OSS_TTL: "${OSS_TTL:-157788000}"
+ KAFKA_SERVERS: "${KAFKA_SERVERS:-kafka:29092}"
+ KAFKA_TIMEOUT: "${KAFKA_TIMEOUT:-60}"
+ KAFKA_TOPIC: "${WORKFLOW_KAFKA_TOPIC:-spark-agent-builder}"
+ KNOWLEDGE_BASE_URL: "${KNOWLEDGE_BASE_URL:-http://core-knowledge:${CORE_KNOWLEDGE_PORT}}"
+ KNOWLEDGE_PRO_BASE_URL: "${KNOWLEDGE_PRO_BASE_URL:-http://core-knowledge:${CORE_KNOWLEDGE_PORT}}"
+ PLUGIN_BASE_URL: "${PLUGIN_BASE_URL:-http://core-link:${CORE_LINK_PORT}}"
+ WORKFLOW_BASE_URL: "${WORKFLOW_BASE_URL:-http://core-workflow:${CORE_WORKFLOW_PORT}}"
+ APP_MANAGE_PLAT_BASE_URL: "${APP_MANAGE_PLAT_BASE_URL:-http://core-tenant:${CORE_TENANT_PORT}}"
+ AGENT_BASE_URL: "${AGENT_BASE_URL:-http://core-agent:${CORE_AGENT_PORT}}"
+ PGSQL_BASE_URL: "${PGSQL_BASE_URL:-http://core-database:${CORE_DATABASE_PORT}}"
+ RPA_BASE_URL: "${RPA_BASE_URL:-http://core-rpa:${CORE_RPA_PORT}}"
+ depends_on:
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ volumes:
+ - ./config/workflow/config.env:/opt/core/workflow/config.env
+ - ./config/workflow/logs/:/opt/core/logs
+ networks:
+ - astron-agent-network
+ restart: always
+
+ # ============================================================================
+ # astron-agent Console Services
+ # ============================================================================
+
+ # Nginx Reverse Proxy
+ nginx:
+ image: nginx:1.25-alpine
+ container_name: astron-agent-nginx
+ ports:
+ - "${EXPOSE_NGINX_PORT:-80}:80"
+ volumes:
+ - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
+ - nginx_logs:/var/log/nginx
+ depends_on:
+ - console-frontend
+ - console-hub
+ networks:
+ - astron-agent-network
+ restart: always
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/nginx-health"]
+ interval: ${HEALTH_CHECK_INTERVAL:-30s}
+ timeout: ${HEALTH_CHECK_TIMEOUT:-10s}
+ retries: ${HEALTH_CHECK_RETRIES:-60}
+
+ # Console Frontend
+ console-frontend:
+ image: ghcr.io/iflytek/astron-agent/console-frontend:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-console-frontend
+ environment:
+ CONSOLE_CASDOOR_URL: "${CONSOLE_CASDOOR_URL:-}"
+ CONSOLE_CASDOOR_ID: "${CONSOLE_CASDOOR_ID:-}"
+ CONSOLE_CASDOOR_APP: "${CONSOLE_CASDOOR_APP:-}"
+ CONSOLE_CASDOOR_ORG: "${CONSOLE_CASDOOR_ORG:-}"
+ expose:
+ - "1881"
+ depends_on:
+ - casdoor
+ networks:
+ - astron-agent-network
+ restart: always
+
+# Console Hub Service
+ console-hub:
+ image: ghcr.io/iflytek/astron-agent/console-hub:${ASTRON_AGENT_VERSION:-latest}
+ container_name: astron-agent-console-hub
+ environment:
+ CONSOLE_CASDOOR_URL: "${CONSOLE_CASDOOR_URL:-}"
+ CONSOLE_CASDOOR_ID: "${CONSOLE_CASDOOR_ID:-}"
+ CONSOLE_CASDOOR_APP: "${CONSOLE_CASDOOR_APP:-}"
+ CONSOLE_CASDOOR_ORG: "${CONSOLE_CASDOOR_ORG:-}"
+ CONSOLE_DOMAIN: "${CONSOLE_DOMAIN:-https://your.deployment.domain}"
+ MYSQL_URL: "${MYSQL_URL:-jdbc:mysql://mysql:3306/astron_console}"
+ MYSQL_USER: "${MYSQL_USER:-root}"
+ MYSQL_PASSWORD: "${MYSQL_PASSWORD:-root123}"
+ REDIS_HOST: "${REDIS_HOST:-redis}"
+ REDIS_PORT: "${REDIS_PORT:-6379}"
+ REDIS_DATABASE_CONSOLE: "${REDIS_DATABASE_CONSOLE:-0}"
+ OSS_ENDPOINT: "${OSS_ENDPOINT:-http://minio:9000}"
+ OSS_REMOTE_ENDPOINT: "${OSS_REMOTE_ENDPOINT:-http://minio:9000}"
+ OSS_ACCESS_KEY_ID: "${OSS_ACCESS_KEY_ID:-minioadmin}"
+ OSS_ACCESS_KEY_SECRET: "${OSS_ACCESS_KEY_SECRET:-minioadmin123}"
+ OSS_BUCKET_CONSOLE: "${OSS_BUCKET_CONSOLE:-console}"
+ OSS_PRESIGN_EXPIRY_SECONDS_CONSOLE: "${OSS_PRESIGN_EXPIRY_SECONDS_CONSOLE:-600}"
+ OAUTH2_ISSUER_URI: "${OAUTH2_ISSUER_URI:-http://auth-server:8000}"
+ OAUTH2_JWK_SET_URI: "${OAUTH2_JWK_SET_URI:-http://auth-server:8000/.well-known/jwks}"
+ OAUTH2_AUDIENCE: "${OAUTH2_AUDIENCE:-your-oauth2-client-id}"
+ PLATFORM_APP_ID: "${PLATFORM_APP_ID:-your-app-id}"
+ PLATFORM_API_KEY: "${PLATFORM_API_KEY:-your-api-key}"
+ PLATFORM_API_SECRET: "${PLATFORM_API_SECRET:-your-api-secret}"
+ SPARK_APP_ID: "${SPARK_APP_ID:-your-spark-app-id}"
+ SPARK_API_KEY: "${SPARK_API_KEY:-your-spark-api-key}"
+ SPARK_API_SECRET: "${SPARK_API_SECRET:-your-spark-api-secret}"
+ SPARK_API_PASSWORD: "${SPARK_API_PASSWORD:-your-spark-api-password}"
+ SPARK_RTASR_KEY: "${SPARK_RTASR_KEY:-your-spark-rtasr-key}"
+ SPARK_RTASR_APPID: "${SPARK_RTASR_APPID:-your-spark-rtasr-appid}"
+ SPARK_IMAGE_APP_ID: "${SPARK_IMAGE_APP_ID:-your-image-appid}"
+ SPARK_IMAGE_API_KEY: "${SPARK_IMAGE_API_KEY:-your-image-api-key}"
+ SPARK_IMAGE_API_SECRET: "${SPARK_IMAGE_API_SECRET:-your-image-api-secret}"
+ WECHAT_COMPONENT_APPID: "${WECHAT_COMPONENT_APPID:-your-wechat-component-appid}"
+ WECHAT_COMPONENT_SECRET: "${WECHAT_COMPONENT_SECRET:-your-wechat-component-secret}"
+ WECHAT_TOKEN: "${WECHAT_TOKEN:-your-wechat-token}"
+ WECHAT_ENCODING_AES_KEY: "${WECHAT_ENCODING_AES_KEY:-your-wechat-encoding-aes-key}"
+ WORKFLOW_CHAT_URL: "${WORKFLOW_CHAT_URL:-http://core-workflow:7880/workflow/v1/chat/completions}"
+ WORKFLOW_DEBUG_URL: "${WORKFLOW_DEBUG_URL:-http://core-workflow:7880/workflow/v1/debug/chat/completions}"
+ WORKFLOW_RESUME_URL: "${WORKFLOW_RESUME_URL:-http://core-workflow:7880/workflow/v1/resume}"
+ COMMON_APPID: "${COMMON_APPID:-appid}"
+ COMMON_APIKEY: "${COMMON_APIKEY:-apiKey}"
+ COMMON_API_SECRET: "${COMMON_API_SECRET:-apiSecret}"
+ TENANT_ID: "${TENANT_ID:-tenantId}"
+ TENANT_KEY: "${TENANT_KEY:-tenantKey}"
+ TENANT_SECRET: "${TENANT_SECRET:-tenantSecret}"
+ ADMIN_UID: "${ADMIN_UID:-9999}"
+ APP_URL: "${APP_URL:-}"
+ KNOWLEDGE_URL: "${KNOWLEDGE_URL:-}"
+ TOOL_URL: "${TOOL_URL:-}"
+ WORKFLOW_URL: "${WORKFLOW_URL:-}"
+ SPARK_DB_URL: "${SPARK_DB_URL:-}"
+ LOCAL_MODEL_URL: "${LOCAL_MODEL_URL:-}"
+ RPA_URL: "${RPA_URL:-}"
+ MAAS_APP_ID: "${MAAS_APP_ID:-your-maas-app-id}"
+ MAAS_API_KEY: "${MAAS_API_KEY:-your-maas-api-key}"
+ MAAS_API_SECRET: "${MAAS_API_SECRET:-your-maas-api-secret}"
+ MAAS_CONSUMER_ID: "${MAAS_CONSUMER_ID:-your-maas-consumer-id}"
+ MAAS_CONSUMER_SECRET: "${MAAS_CONSUMER_SECRET:-your-maas-consumer-secret}"
+ MAAS_CONSUMER_KEY: "${MAAS_CONSUMER_KEY:-your-maas-consumer-key}"
+ MAAS_WORKFLOW_VERSION: "${MAAS_WORKFLOW_VERSION:-}"
+ MAAS_SYNCHRONIZE_WORK_FLOW: "${MAAS_SYNCHRONIZE_WORK_FLOW:-}"
+ MAAS_PUBLISH: "${MAAS_PUBLISH:-}"
+ MAAS_CLONE_WORK_FLOW: "${MAAS_CLONE_WORK_FLOW:-}"
+ MAAS_GET_INPUTS: "${MAAS_GET_INPUTS:-}"
+ MAAS_CAN_PUBLISH_URL: "${MAAS_CAN_PUBLISH_URL:-}"
+ MAAS_PUBLISH_API: "${MAAS_PUBLISH_API:-}"
+ MAAS_AUTH_API: "${MAAS_AUTH_API:-}"
+ MAAS_MCP_REGISTER: "${MAAS_MCP_REGISTER:-}"
+ MAAS_WORKFLOW_CONFIG: "${MAAS_WORKFLOW_CONFIG:-}"
+ BOT_API_CBM_BASE_URL: "${BOT_API_CBM_BASE_URL:-}"
+ BOT_API_MAAS_BASE_URL: "${BOT_API_MAAS_BASE_URL:-}"
+ TENANT_CREATE_APP: "${TENANT_CREATE_APP:-}"
+ TENANT_GET_APP_DETAIL: "${TENANT_GET_APP_DETAIL:-}"
+
+ expose:
+ - "8080"
+ depends_on:
+ casdoor:
+ condition: service_started
+ postgres:
+ condition: service_healthy
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ minio:
+ condition: service_healthy
+ networks:
+ - astron-agent-network
+ restart: always
+
+# ============================================================================
+# Network Configuration
+# ============================================================================
+networks:
+ astron-agent-network:
+ driver: bridge
+
+# ============================================================================
+# Volume Configuration
+# ============================================================================
+volumes:
+ postgres_data:
+ driver: local
+ mysql_data:
+ driver: local
+ redis_data:
+ driver: local
+ elasticsearch_data:
+ driver: local
+ kafka_data:
+ driver: local
+ minio_data:
+ driver: local
+ nginx_logs:
+ driver: local
diff --git a/docker/astronAgent/docker-compose.yaml b/docker/astronAgent/docker-compose.yaml
index 4e8d3dae..6a46bfe7 100644
--- a/docker/astronAgent/docker-compose.yaml
+++ b/docker/astronAgent/docker-compose.yaml
@@ -1,6 +1,3 @@
-include:
- - docker-compose-with-auth.yml
-
services:
# ============================================================================
# Infrastructure Services
@@ -478,8 +475,6 @@ services:
CONSOLE_CASDOOR_ORG: "${CONSOLE_CASDOOR_ORG:-}"
expose:
- "1881"
- depends_on:
- - casdoor
networks:
- astron-agent-network
restart: always
@@ -566,8 +561,6 @@ services:
expose:
- "8080"
depends_on:
- casdoor:
- condition: service_started
postgres:
condition: service_healthy
mysql:
--
Gitee
From 28992345880f451ce8606617d4a011b23f05e5db Mon Sep 17 00:00:00 2001
From: duanming
Date: Fri, 24 Oct 2025 10:56:06 +0800
Subject: [PATCH 80/90] feat: add common kafka global trigger
---
core/common/.env.example | 59 +++++++++++-----------
core/common/service/kafka/factory.py | 9 +++-
core/common/service/kafka/kafka_service.py | 13 ++++-
3 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/core/common/.env.example b/core/common/.env.example
index 2a85a150..3493ac8d 100644
--- a/core/common/.env.example
+++ b/core/common/.env.example
@@ -6,19 +6,20 @@ SERVICE_LOCATION=hf
SERVICE_PORT=7880
# CACHE
-REDIS_CLUSTER_ADDR=node-a.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000,node-b.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000,node-c.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000,node-d.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000,node-e.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000,node-f.redis-hf04-aldav8.svc.hfb.ipaas.cn:9000
-REDIS_PASSWORD=NPTzHSR8
+REDIS_CLUSTER_ADDR=
+REDIS_PASSWORD=
REDIS_EXPIRE=60
# DATABASE
-MYSQL_HOST=mysql-Nu8weB0lBq.mysql.zone-hfyc-1.dbaas.private
-MYSQL_PORT=28290
-MYSQL_USER=admin
-MYSQL_PASSWORD=EdgeAIGo!
-MYSQL_DB=langflow_dev
+MYSQL_HOST=
+MYSQL_PORT=
+MYSQL_USER=
+MYSQL_PASSWORD=
+MYSQL_DB=
# KAFKA
-KAFKA_SERVERS=kafka-0.kafka-hf04-1quuxb.svc.hfb.ipaas.cn:9092,kafka-1.kafka-hf04-1quuxb.svc.hfb.ipaas.cn:9092,kafka-2.kafka-hf04-1quuxb.svc.hfb.ipaas.cn:9092
+KAFKA_ENABLE=0
+KAFKA_SERVERS=
KAFKA_TIMEOUT=10
# LOG
@@ -28,13 +29,13 @@ LOG_LEVEL="ERROR"
# MASDK (Model as SDK) Configuration
# Service discovery and model integration settings for distributed AI services
# MASDK_SWITCH=1
-MASDK_POLARIS_URL=http://companion-test.iflyaicloud.com:9080
-MASDK_POLARIS_PROJECT=AIaaS
-MASDK_POLARIS_GROUP=madx
-MASDK_POLARIS_SERVICE=janus-client
-MASDK_POLARIS_VERSION=3.1.2
-MASDK_CHANNEL=xingchen_agent_workflow
-MASDK_FUNCTION=business.total
+MASDK_POLARIS_URL=
+MASDK_POLARIS_PROJECT=
+MASDK_POLARIS_GROUP=
+MASDK_POLARIS_SERVICE=
+MASDK_POLARIS_VERSION=
+MASDK_CHANNEL=
+MASDK_FUNCTION=
# OSS
# =============================================================================
@@ -43,19 +44,19 @@ MASDK_FUNCTION=business.total
# Object Storage Service Settings
# Storage type options: ifly_gateway_storage (iFlytek), s3 (Amazon S3 compatible)
-OSS_TYPE=ifly_gateway_storage
-OSS_ENDPOINT=http://172.30.209.27:8211
-OSS_ACCESS_KEY_ID=sjliu777
-OSS_ACCESS_KEY_SECRET=12345678
-OSS_BUCKET_NAME=sjliu
+OSS_TYPE=
+OSS_ENDPOINT=
+OSS_ACCESS_KEY_ID=
+OSS_ACCESS_KEY_SECRET=
+OSS_BUCKET_NAME=
# Download domain for S3, required when using S3 storage type
-OSS_DOWNLOAD_HOST=https://oss-beijing-m8.openstorage.cn
+OSS_DOWNLOAD_HOST=
# File validity period for iFlytek object storage (in seconds)
OSS_TTL=157788000
# OTLP
# 上报地址
-OTLP_ENDPOINT=172.30.209.27:4317
+OTLP_ENDPOINT=
# 上报开关
OTLP_ENABLE=1
@@ -77,14 +78,14 @@ OTLP_TRACE_EXPORT_TIMEOUT_MILLIS=3000
# CONFIG - 需要配置在容器环境中
CONFIG_ENV_PATH=./.env
POLARIS_ENABLED=false
-POLARIS_BASE_URL=http://172.30.209.27:8090/
-POLARIS_USERNAME=mingduan
-POLARIS_PASSWORD=123456l
-POLARIS_PROJECT=hy-spark-agent-builder
-POLARIS_CLUSTER=dev
-POLARIS_SERVICE=spark-agent
+POLARIS_BASE_URL=
+POLARIS_USERNAME=
+POLARIS_PASSWORD=
+POLARIS_PROJECT=
+POLARIS_CLUSTER=
+POLARIS_SERVICE=
POLARIS_VERSION=1.0.0
-POLARIS_CONFIG_FILE=spark-agent.env
+POLARIS_CONFIG_FILE=
POLARIS_RETRY_COUNT=3
POLARIS_RETRY_INTERVAL=10
diff --git a/core/common/service/kafka/factory.py b/core/common/service/kafka/factory.py
index 843024fd..605c8f5c 100644
--- a/core/common/service/kafka/factory.py
+++ b/core/common/service/kafka/factory.py
@@ -16,7 +16,14 @@ class KafkaProducerServiceFactory(ServiceFactory):
:return: KafkaProducerService 实例
"""
servers = servers or os.getenv("KAFKA_SERVERS")
- if not servers:
+ enable = os.getenv("KAFKA_ENABLE", "false").lower() in (
+ "true",
+ "1",
+ "yes",
+ "on",
+ )
+
+ if enable and not servers:
raise ValueError("KAFKA_SERVERS 环境变量未配置")
config = {"bootstrap.servers": servers, **kwargs}
diff --git a/core/common/service/kafka/kafka_service.py b/core/common/service/kafka/kafka_service.py
index 747dcfdf..4541c2c6 100644
--- a/core/common/service/kafka/kafka_service.py
+++ b/core/common/service/kafka/kafka_service.py
@@ -23,7 +23,7 @@ class KafkaProducerService(Service):
topic: str,
value: str,
callback: Optional[Any] = None,
- timeout: int = int(os.getenv("KAFKA_TIMEOUT", 10)),
+ timeout: Optional[int] = None,
) -> None:
"""
发送 Kafka 消息
@@ -32,6 +32,17 @@ class KafkaProducerService(Service):
:param callback: 回调函数
:param timeout: poll timeout (秒)
"""
+ if os.getenv("KAFKA_ENABLE", "false").lower() not in (
+ "true",
+ "1",
+ "yes",
+ "on",
+ ):
+ return
+
+ if not timeout:
+ timeout = int(os.getenv("KAFKA_TIMEOUT", 10))
+
if not callback:
callback = self._delivery_report
try:
--
Gitee
From 46138b88d14fef3959e0fc389ebd55a798fe9825 Mon Sep 17 00:00:00 2001
From: Xudong Sun
Date: Fri, 24 Oct 2025 11:26:18 +0800
Subject: [PATCH 81/90] fix: Adjust JVM memory parameters in Dockerfile (#401)
Updated JVM parameters for memory usage.
Signed-off-by: Xudong Sun