Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e2c9073
fix(dataset): 修复定时同步任务 JobDataMap key 不一致导致 NPE
qianmoQ May 18, 2026
e4f3a48
feat(executor-local): 支持流式同步并修复 OOM / SQL 注入 / NULL 问题
qianmoQ May 20, 2026
73856dc
fix(executor-local): 从 input 读取目标库 / 表,与 DataSetServiceImpl 约定对齐
qianmoQ May 20, 2026
90d77e6
feat(executor-local): 输出独立任务日志并加进度统计
qianmoQ May 20, 2026
2bc5dd9
feat(executor-spi): 增加 ExecutorService.name() 并用于 workHome 路径
qianmoQ May 20, 2026
28777f8
refactor(executor): unify executor identifier via SPI ExecutorPlugin …
qianmoQ May 20, 2026
b2699e8
feat(dataset): 同步历史增加总数 / 已完成 / 进度,同步期间实时刷数据集 totalRows / totalSize
qianmoQ May 21, 2026
4442ac8
feat(dataset): 同步历史支持查看运行日志(实时 + 已结束)
qianmoQ May 21, 2026
d35d850
feat(dataset): 支持手动停止正在同步的任务
qianmoQ May 21, 2026
5203067
feat(dataset): 服务重启后恢复未完结同步历史为 INTERRUPTED
qianmoQ May 21, 2026
05263f2
i18n(dataset): 国际化数据集列表的状态显示(TABLE_SUCCESS 等)
qianmoQ May 21, 2026
68899af
feat(plugin): 在 Plugin 基类增加 configures() 声明可配置字段(所有插件类型通用)
qianmoQ May 21, 2026
b538579
feat(service): 新增通用配置表 datacap_configure(Entity + Repository)
qianmoQ May 21, 2026
1e8153f
feat(service): 新增 RuntimeConfigureService 读写 datacap_configure
qianmoQ May 21, 2026
53fd550
refactor(service): 切换 executor / dataset 配置数据源到 datacap_configure 表
qianmoQ May 21, 2026
12a099e
feat(dataset): 同步历史记录本次生效的 executor 配置 JSON
qianmoQ May 22, 2026
ab294c5
feat(dataset): 同步对话框支持临时覆盖 tunable executor 字段
qianmoQ May 22, 2026
e6d37df
feat(configure): 管理员系统配置页面(datacap_configure CRUD)
qianmoQ May 22, 2026
5686d0f
chore(configure): 删除 properties 中已迁库的 executor / dataset 配置 + 同步文档
qianmoQ May 22, 2026
4bfd6e4
chore(schema): 2026.0.0 schema 加入 datacap_configure + dataset_history…
qianmoQ May 22, 2026
0a6c12f
chore(schema): 新增系统配置菜单入口(运行时配置)
qianmoQ May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 7 additions & 35 deletions configure/docker/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,33 +37,10 @@ spring.redis.database=0
# spring.redis.password=

################################ Executor configure #################################
# If this directory is not set
# the system will get the project root directory to build the data subdirectory

# ------ Apache Seatunnel for Spark ------ #
datacap.executor.data=
datacap.executor.way=LOCAL
datacap.executor.mode=CLIENT
datacap.executor.engine=SPARK
datacap.executor.startScript=start-seatunnel-spark-connector-v2.sh
datacap.executor.seatunnel.home=/opt/lib/seatunnel

# ------ Apache Seatunnel for Flink ------ #
# datacap.executor.data=
# datacap.executor.way=LOCAL
# datacap.executor.mode=CLIENT
# datacap.executor.engine=FLINK
# datacap.executor.startScript=start-seatunnel-flink-13-connector-v2.sh
# datacap.executor.seatunnel.home=/opt/lib/seatunnel

# ------ Apache Seatunnel for seatunnel ------ #
# datacap.executor.data=
## Only support LOCAL
# datacap.executor.way=LOCAL
# datacap.executor.mode=CLIENT
# datacap.executor.engine=SEATUNNEL
# datacap.executor.startScript=seatunnel.sh
# datacap.executor.seatunnel.home=/opt/lib/seatunnel
# Executor configuration (formerly datacap.executor.*) is now stored in the database table
# datacap_configure (category=EXECUTOR, name=<Local|Seatunnel|...>). Defaults come from each
# ExecutorPlugin's configures() SPI declaration and are auto-seeded on first startup.
# Edit via the admin "Runtime Configuration" page at /system/configure.

################################ Upload configure #################################
datacap.config.data=
Expand Down Expand Up @@ -110,14 +87,9 @@ datacap.pipeline.maxQueue=200
datacap.pipeline.reset=STOPPED

################################# DataSet configure #################################
datacap.dataset.type=ClickHouse
datacap.dataset.host=app-clickhouse
datacap.dataset.port=8123
datacap.dataset.username=default
datacap.dataset.password=da39a3ee5e6b4b0d3255bfef95601890afd80709
datacap.dataset.database=datacap
datacap.dataset.tablePrefix=datacap_
datacap.dataset.tableDefaultEngine=MergeTree
# Dataset target storage configuration (formerly datacap.dataset.*) is now stored in
# datacap_configure (category=DATASET, name=Default). Defaults come from DatasetTargetSchema
# and are auto-seeded on first startup. Edit via /system/configure.

################################# Plugin configure #################################
datacap.parser.sql.defaultEngine=Trino
Expand Down
42 changes: 7 additions & 35 deletions configure/etc/conf/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,33 +37,10 @@ spring.redis.database=0
# spring.redis.password=

################################ Executor configure #################################
# If this directory is not set
# the system will get the project root directory to build the data subdirectory

# ------ Apache Seatunnel for Spark ------ #
datacap.executor.data=
datacap.executor.way=LOCAL
datacap.executor.mode=CLIENT
datacap.executor.engine=SPARK
datacap.executor.startScript=start-seatunnel-spark-connector-v2.sh
datacap.executor.seatunnel.home=/opt/lib/seatunnel

# ------ Apache Seatunnel for Flink ------ #
# datacap.executor.data=
# datacap.executor.way=LOCAL
# datacap.executor.mode=CLIENT
# datacap.executor.engine=FLINK
# datacap.executor.startScript=start-seatunnel-flink-13-connector-v2.sh
# datacap.executor.seatunnel.home=/opt/lib/seatunnel

# ------ Apache Seatunnel for seatunnel ------ #
# datacap.executor.data=
## Only support LOCAL
# datacap.executor.way=LOCAL
# datacap.executor.mode=CLIENT
# datacap.executor.engine=SEATUNNEL
# datacap.executor.startScript=seatunnel.sh
# datacap.executor.seatunnel.home=/opt/lib/seatunnel
# Executor configuration (formerly datacap.executor.*) is now stored in the database table
# datacap_configure (category=EXECUTOR, name=<Local|Seatunnel|...>). Defaults come from each
# ExecutorPlugin's configures() SPI declaration and are auto-seeded on first startup.
# Edit via the admin "Runtime Configuration" page at /system/configure.

################################ Upload configure #################################
datacap.config.data=
Expand Down Expand Up @@ -110,14 +87,9 @@ datacap.pipeline.maxQueue=200
datacap.pipeline.reset=STOPPED

################################# DataSet configure #################################
datacap.dataset.type=ClickHouse
datacap.dataset.host=localhost
datacap.dataset.port=8123
datacap.dataset.username=
datacap.dataset.password=
datacap.dataset.database=datacap
datacap.dataset.tablePrefix=datacap_
datacap.dataset.tableDefaultEngine=MergeTree
# Dataset target storage configuration (formerly datacap.dataset.*) is now stored in
# datacap_configure (category=DATASET, name=Default). Defaults come from DatasetTargetSchema
# and are auto-seeded on first startup. Edit via /system/configure.

################################# Plugin configure #################################
datacap.parser.sql.defaultEngine=Trino
Expand Down
6 changes: 3 additions & 3 deletions configure/etc/conf/executor/category.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
- label: Input
value: source
supportExecutors:
- SeatunnelExecutor
- Seatunnel
- label: Transform
value: transform
supportExecutors:
- SeatunnelExecutor
- Seatunnel
- label: Output
value: sink
supportExecutors:
- SeatunnelExecutor
- Seatunnel
31 changes: 31 additions & 0 deletions configure/etc/conf/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ state.common.running=Running
state.common.success=Run Successful
state.common.failure=Run Failed
state.common.stop=Stopped
state.common.stopping=Stopping
state.common.interrupted=Interrupted
state.common.timeout=Run Timed Out
state.common.queue=Queued
## Query i18n
Expand Down Expand Up @@ -594,6 +596,17 @@ dataset.common.stateOfStarted=Started
dataset.common.stateOfMetadata=Metadata State
dataset.common.stateOfMetadataStarted=Metadata Started
dataset.common.stateOfCreateTable=Create Table State
dataset.state.metadataStart=Metadata Start
dataset.state.metadataFailed=Metadata Failed
dataset.state.metadataSuccess=Metadata Success
dataset.state.tableStart=Table Start
dataset.state.tableFailed=Table Failed
dataset.state.tableSuccess=Table Success
dataset.state.dataStart=Data Start
dataset.state.dataFailed=Data Failed
dataset.state.dataSuccess=Data Success
dataset.state.completeFailed=Complete Failed
dataset.state.completeSuccess=Complete Success
dataset.common.syncData=Sync Data
dataset.common.visual=Visualization
dataset.common.visualType=Visualization Type
Expand Down Expand Up @@ -670,6 +683,24 @@ dataset.common.lifeCycleDay=Day
dataset.common.lifeCycleHour=Hour
dataset.common.notSpecifiedTitle=Not Specified
dataset.common.history=Sync History
dataset.history.totalCount=Total
dataset.history.processedCount=Processed
dataset.history.progress=Progress
dataset.history.viewLog=View Log
dataset.history.logger=Sync Log
dataset.history.autoRefresh=Auto Refresh
dataset.history.refreshInterval=refresh every {seconds}s
dataset.history.stop=Stop
dataset.history.stopRequested=Stop requested. The task will exit at the next row checkpoint.
dataset.history.viewConfigure=View Configure
dataset.history.configureTitle=Effective Executor Configuration
dataset.sync.overrideTitle=Per-run Override (tunable fields)
configure.runtime.title=Runtime Configuration
configure.runtime.categoryExecutor=Executor
configure.runtime.categoryDataset=Dataset Target
configure.runtime.adminOnly=admin-only
configure.runtime.empty=(no entries yet, will be seeded on next startup)
configure.runtime.selectHint=Select a configuration entry on the left to edit.
dataset.common.clearData=Clear Data
dataset.common.error=View Errors
dataset.common.info=View Details
Expand Down
31 changes: 31 additions & 0 deletions configure/etc/conf/i18n/messages_zh-cn.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ state.common.running=\u8FD0\u884C\u4E2D
state.common.success=\u8FD0\u884C\u6210\u529F
state.common.failure=\u8FD0\u884C\u5931\u8D25
state.common.stop=\u5DF2\u505C\u6B62
state.common.stopping=\u505C\u6B62\u4E2D
state.common.interrupted=\u5F02\u5E38\u4E2D\u65AD
state.common.timeout=\u8FD0\u884C\u8D85\u65F6
state.common.queue=\u6392\u961F\u4E2D
## Query i18n
Expand Down Expand Up @@ -594,6 +596,17 @@ dataset.common.stateOfStarted=\u5DF2\u542F\u52A8
dataset.common.stateOfMetadata=\u5143\u6570\u636E\u72B6\u6001
dataset.common.stateOfMetadataStarted=\u5143\u6570\u636E\u5DF2\u542F\u52A8
dataset.common.stateOfCreateTable=\u521B\u5EFA\u8868\u72B6\u6001
dataset.state.metadataStart=\u5143\u6570\u636E\u5F00\u59CB
dataset.state.metadataFailed=\u5143\u6570\u636E\u5931\u8D25
dataset.state.metadataSuccess=\u5143\u6570\u636E\u6210\u529F
dataset.state.tableStart=\u5EFA\u8868\u5F00\u59CB
dataset.state.tableFailed=\u5EFA\u8868\u5931\u8D25
dataset.state.tableSuccess=\u5EFA\u8868\u6210\u529F
dataset.state.dataStart=\u6570\u636E\u540C\u6B65\u5F00\u59CB
dataset.state.dataFailed=\u6570\u636E\u540C\u6B65\u5931\u8D25
dataset.state.dataSuccess=\u6570\u636E\u540C\u6B65\u6210\u529F
dataset.state.completeFailed=\u4EFB\u52A1\u5931\u8D25
dataset.state.completeSuccess=\u4EFB\u52A1\u6210\u529F
dataset.common.syncData=\u540C\u6B65\u6570\u636E
dataset.common.visual=\u53EF\u89C6\u5316
dataset.common.visualType=\u53EF\u89C6\u5316\u7C7B\u578B
Expand Down Expand Up @@ -670,6 +683,24 @@ dataset.common.lifeCycleDay=\u5929
dataset.common.lifeCycleHour=\u5C0F\u65F6
dataset.common.notSpecifiedTitle=\u672A\u6307\u5B9A
dataset.common.history=\u540C\u6B65\u5386\u53F2
dataset.history.totalCount=\u603B\u6570
dataset.history.processedCount=\u5DF2\u5B8C\u6210
dataset.history.progress=\u8FDB\u5EA6
dataset.history.viewLog=\u67E5\u770B\u65E5\u5FD7
dataset.history.logger=\u540C\u6B65\u65E5\u5FD7
dataset.history.autoRefresh=\u81EA\u52A8\u5237\u65B0
dataset.history.refreshInterval=\u6BCF {seconds} \u79D2\u5237\u65B0
dataset.history.stop=\u505C\u6B62
dataset.history.stopRequested=\u5DF2\u8BF7\u6C42\u505C\u6B62\uFF0C\u4EFB\u52A1\u5C06\u5728\u4E0B\u4E00\u884C\u68C0\u67E5\u70B9\u9000\u51FA
dataset.history.viewConfigure=\u67E5\u770B\u914D\u7F6E
dataset.history.configureTitle=\u672C\u6B21\u540C\u6B65\u5B9E\u9645\u751F\u6548\u7684\u6267\u884C\u5668\u914D\u7F6E
dataset.sync.overrideTitle=\u672C\u6B21\u8FD0\u884C\u8986\u76D6\uFF08\u4EC5\u53EF\u8C03\u5B57\u6BB5\uFF09
configure.runtime.title=\u8FD0\u884C\u65F6\u914D\u7F6E
configure.runtime.categoryExecutor=\u6267\u884C\u5668
configure.runtime.categoryDataset=\u6570\u636E\u96C6\u76EE\u6807\u5B58\u50A8
configure.runtime.adminOnly=\u4EC5\u7BA1\u7406\u5458
configure.runtime.empty=\uFF08\u6682\u65E0\u6761\u76EE\uFF0C\u4E0B\u6B21\u542F\u52A8\u4F1A\u81EA\u52A8 seed\uFF09
configure.runtime.selectHint=\u8BF7\u5728\u5DE6\u4FA7\u9009\u62E9\u4E00\u4E2A\u914D\u7F6E\u9879\u8FDB\u884C\u7F16\u8F91
dataset.common.clearData=\u6E05\u9664\u6570\u636E
dataset.common.error=\u67E5\u770B\u9519\u8BEF
dataset.common.info=\u67E5\u770B\u8BE6\u60C5
Expand Down
79 changes: 79 additions & 0 deletions configure/schema/2026.0.0/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
USE
`datacap`;

-- Executor 命名统一:去掉冗余的 "Executor" 后缀,与 SPI ExecutorService.name() / ExecutorPlugin.getName() 对齐。
-- 例如 'SeatunnelExecutor' -> 'Seatunnel','LocalExecutor' -> 'Local'。
-- Unify executor identifier with SPI's name(): strip the redundant "Executor" suffix.

UPDATE `datacap_dataset`
SET `executor` = 'Local'
WHERE `executor` = 'LocalExecutor';

UPDATE `datacap_dataset`
SET `executor` = 'Seatunnel'
WHERE `executor` = 'SeatunnelExecutor';

ALTER TABLE `datacap_dataset`
ALTER COLUMN `executor` SET DEFAULT 'Local';

UPDATE `datacap_workflow`
SET `executor` = 'Local'
WHERE `executor` = 'LocalExecutor';

UPDATE `datacap_workflow`
SET `executor` = 'Seatunnel'
WHERE `executor` = 'SeatunnelExecutor';

-- 同步历史新增 总数 / 已完成 / 进度 三列
-- Sync history adds total count / processed count / progress columns
ALTER TABLE `datacap_dataset_history`
ADD COLUMN `total_count` BIGINT DEFAULT NULL COMMENT 'Total rows from source query, NULL if pre-count is disabled',
ADD COLUMN `processed_count` BIGINT DEFAULT NULL COMMENT 'Rows successfully written to target',
ADD COLUMN `progress` DECIMAL(5, 2) DEFAULT NULL COMMENT 'processed_count / total_count * 100, NULL when total unknown';

-- 同步历史新增 任务名 / 工作目录,用于定位 executor 写出的独立任务日志文件
-- Sync history adds task name / work home so the UI can locate the executor's task log file
ALTER TABLE `datacap_dataset_history`
ADD COLUMN `task_name` VARCHAR(64) DEFAULT NULL COMMENT 'Executor task name; also the log file basename',
ADD COLUMN `work_home` VARCHAR(512) DEFAULT NULL COMMENT 'Executor task workHome; logs live at {work_home}/{task_name}.log';

-- 同步历史新增 effective executor 配置 JSON:本次同步实际生效的所有配置(DB 默认 + 用户临时覆盖合并后)
-- Sync history adds the effective executor configuration JSON used by this run
ALTER TABLE `datacap_dataset_history`
ADD COLUMN `executor_configure` TEXT DEFAULT NULL COMMENT 'Effective executor configuration JSON used for this sync';

-- 通用配置表:承载 EXECUTOR / DATASET 等不同范畴的运行时配置
-- Generic runtime configuration table backing datacap_configure_service.
-- One row per (category, name); configure column is JSON-serialized Map<String,String>.
CREATE TABLE IF NOT EXISTS `datacap_configure`
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(128) NOT NULL COMMENT 'e.g. Local / Seatunnel / Default',
`code` VARCHAR(64) DEFAULT NULL,
`active` TINYINT(1) DEFAULT 1,
`category` VARCHAR(32) NOT NULL COMMENT 'EXECUTOR / DATASET',
`configure` TEXT DEFAULT NULL COMMENT 'JSON-serialized configuration map',
`description` VARCHAR(512) DEFAULT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configure_category_name` (`category`, `name`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = 'Runtime configuration storage (datacap_configure)';

-- 新菜单:管理员 → 系统 → 运行时配置(/system/configure)
-- Admin menu entry for the runtime configuration page. Idempotent insert by `code`.
INSERT INTO `datacap_menu` (`name`, `code`, `description`, `url`, `group_name`, `sorted`, `type`, `parent`, `active`, `i18n_key`, `icon`)
SELECT '管理员 - 系统 - 运行时配置', 'RUNTIME_CONFIGURE',
'管理员:管理员权限用户可以访问\n位置:顶部管理一级子菜单',
'/system/configure', NULL, 7, 'VIEW', 8, 1, 'configure.runtime.title', 'Settings2'
WHERE NOT EXISTS (SELECT 1 FROM `datacap_menu` WHERE `code` = 'RUNTIME_CONFIGURE');

-- 绑定该菜单到 admin 角色(role_id=1)。menu_id 字段为 mediumtext,CAST 后插入。
INSERT INTO `datacap_role_menu_relation` (`role_id`, `menu_id`)
SELECT '1', CAST(`id` AS CHAR)
FROM `datacap_menu`
WHERE `code` = 'RUNTIME_CONFIGURE'
AND NOT EXISTS (
SELECT 1 FROM `datacap_role_menu_relation` r
WHERE r.role_id = '1' AND r.menu_id = CAST(`datacap_menu`.id AS CHAR)
);
2 changes: 1 addition & 1 deletion configure/schema/datacap.sql
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ CREATE TABLE `datacap_dataset` (
`code` varchar(100) DEFAULT (uuid()),
`column_mode` varchar(100) DEFAULT 'DIMENSION',
`scheduler` varchar(100) DEFAULT 'LocalScheduler',
`executor` varchar(100) DEFAULT 'LocalExecutor',
`executor` varchar(100) DEFAULT 'Local',
`total_rows` bigint DEFAULT '0',
`total_size` varchar(100) DEFAULT NULL,
`life_cycle` bigint DEFAULT '0',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,18 @@ public String getName()
return StringUtils.remove(this.getClass().getSimpleName(), "Plugin");
}

/**
* 声明该插件可配置的字段(名称 / 类型 / 默认值 / 是否允许普通用户临时覆盖)。
* 默认空:旧插件零改动。Executor / Scheduler / FS / Notify 等子类按需重写。
*
* Declare configurable fields exposed by this plugin. Default empty for full
* backward compatibility — subclasses (Executor / Scheduler / FS / Notify / ...) override as needed.
*/
public java.util.List<io.edurt.datacap.plugin.configure.PluginConfigureField> configures()
{
return java.util.Collections.emptyList();
}

public String getVersion()
{
if (cachedVersion != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.edurt.datacap.plugin.configure;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* 描述一个插件可配置字段。所有插件类型(Executor / Scheduler / FS / …)共用。
* - name: 字段 key,序列化到 JSON 配置中的属性名
* - type: 字段类型,决定 UI 渲染(文本框 / 数字 / 开关 / 密码)
* - defaultValue: 默认值的字符串表示;调用方按 type 解析
* - description: 字段说明,UI tooltip 用
* - tunable: true = 普通用户在触发任务时可临时覆盖;false = 仅管理员可在系统配置改
*
* Generic configurable-field descriptor used by any plugin category.
* - tunable=false fields are administrator-only.
* - tunable=true fields can also be overridden by end users at task invocation time.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PluginConfigureField
{
private String name;
private PluginFieldType type;
private String defaultValue;
private String description;
private boolean tunable;

public PluginConfigureField(String name, PluginFieldType type, String defaultValue, String description)
{
this(name, type, defaultValue, description, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.edurt.datacap.plugin.configure;

/**
* 插件可配置字段的类型,决定 UI 渲染方式与默认值的解析方式。
* 故意保持最小集,所有插件类型(Executor / Scheduler / FS / Notify …)通用。
*
* Lightweight field type shared by all plugin categories.
* Kept minimal on purpose so any plugin layer can declare configurable fields without
* pulling in business-side enums.
*/
public enum PluginFieldType
{
STRING,
NUMBER,
BOOLEAN,
PASSWORD
}
Loading
Loading