From c30064634a5c48e47e85be2681ff43f4aef1a648 Mon Sep 17 00:00:00 2001 From: wgzhao Date: Mon, 4 May 2026 17:51:51 +0800 Subject: [PATCH 1/3] feat(scheduler): add biz-date collect date mode for sources Motivation:\nIntroduce source-level collection date policies so automatic scheduling can skip non-trading days while keeping manual backfill available.\n\nWhat changed:\n- Added CollectDateMode (DAILY/WEEKDAY/WEEKEND) to source model and schema.\n- Added SourceScheduleMatcher based on bizDate weekday/weekend evaluation.\n- Applied source date filtering to source-level scheduler, queue scans, override scans, and daily refresh/reset initialization path.\n- Updated SourceService schedule-change detection to include enabled and collectDateMode changes.\n- Added frontend form/list support for collectDateMode selection and display.\n- Added memory.md entry for this change set.\n\nValidation:\n- mvn -pl backend -DskipTests compile\n- npm run type-check --- .../addax/admin/common/CollectDateMode.java | 12 +++ .../wgzhao/addax/admin/model/EtlSource.java | 7 ++ .../addax/admin/repository/EtlTableRepo.java | 26 ++++++- .../admin/scheduler/CollectionScheduler.java | 11 +++ .../admin/service/SourceScheduleMatcher.java | 51 ++++++++++++ .../addax/admin/service/SourceService.java | 17 +++- .../addax/admin/service/TableService.java | 78 +++++++++++++++++-- .../addax/admin/service/TaskService.java | 19 ++++- frontend/src/components/source/AddSource.vue | 30 ++++++- frontend/src/types/database.ts | 1 + frontend/src/views/source.vue | 13 ++++ memory.md | 41 ++++++++++ scripts/schema.sql | 3 + 13 files changed, 293 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/java/com/wgzhao/addax/admin/common/CollectDateMode.java create mode 100644 backend/src/main/java/com/wgzhao/addax/admin/service/SourceScheduleMatcher.java create mode 100644 memory.md diff --git a/backend/src/main/java/com/wgzhao/addax/admin/common/CollectDateMode.java b/backend/src/main/java/com/wgzhao/addax/admin/common/CollectDateMode.java new file mode 100644 index 00000000..446fc9c3 --- /dev/null +++ b/backend/src/main/java/com/wgzhao/addax/admin/common/CollectDateMode.java @@ -0,0 +1,12 @@ +package com.wgzhao.addax.admin.common; + +/** + * Source-level collection date policy. + */ +public enum CollectDateMode +{ + DAILY, + WEEKDAY, + WEEKEND +} + diff --git a/backend/src/main/java/com/wgzhao/addax/admin/model/EtlSource.java b/backend/src/main/java/com/wgzhao/addax/admin/model/EtlSource.java index 71ae89ca..7c8d5b9b 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/model/EtlSource.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/model/EtlSource.java @@ -1,8 +1,11 @@ package com.wgzhao.addax.admin.model; import com.wgzhao.addax.admin.common.DbType; +import com.wgzhao.addax.admin.common.CollectDateMode; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -44,6 +47,10 @@ public class EtlSource @Column(name = "start_at") private LocalTime startAt; + @Enumerated(EnumType.STRING) + @Column(name = "collect_date_mode", length = 16, nullable = false) + private CollectDateMode collectDateMode = CollectDateMode.DAILY; + @Column(name = "prerequisite", length = 4000) private String prerequisite; diff --git a/backend/src/main/java/com/wgzhao/addax/admin/repository/EtlTableRepo.java b/backend/src/main/java/com/wgzhao/addax/admin/repository/EtlTableRepo.java index 89908322..559dced9 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/repository/EtlTableRepo.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/repository/EtlTableRepo.java @@ -23,6 +23,10 @@ where t.status not in ( 'X' ,'U') @Query("UPDATE EtlTable t SET t.status = 'N', t.retryCnt = 3 where t.status not in ( 'X', 'U')") void resetAllEtlFlags(); + @Modifying + @Query("UPDATE EtlTable t SET t.status = 'N', t.retryCnt = 3 where t.status not in ( 'X', 'U') and t.sid in :sourceIds") + void resetEtlFlagsBySourceIds(@Param("sourceIds") List sourceIds); + @Query(value = """ select t from EtlTable t @@ -48,6 +52,7 @@ select count(*) from etl_table t left join etl_source s on t.sid = s.id @Query(""" SELECT t FROM EtlTable t JOIN EtlSource s on t.sid = s.id WHERE t.status NOT IN ('Y','X','U') AND t.retryCnt > 0 + AND t.sid in :sourceIds AND ( :checkTime = false OR (s.startAt > :switchTime AND s.startAt < :currentTime) @@ -55,7 +60,8 @@ WHERE t.status NOT IN ('Y','X','U') AND t.retryCnt > 0 """) List findRunnableTasks(@Param("switchTime") LocalTime switchTime, @Param("currentTime") LocalTime currentTime, - @Param("checkTime") boolean checkTime); + @Param("checkTime") boolean checkTime, + @Param("sourceIds") List sourceIds); /** * 查询某个 source 下“继承调度”的可运行任务(表 startAt 为空,实际调度由 source.startAt 决定) @@ -64,11 +70,13 @@ List findRunnableTasks(@Param("switchTime") LocalTime switchTime, SELECT t FROM EtlTable t JOIN EtlSource s on t.sid = s.id WHERE s.enabled = true AND t.sid = :sid + AND t.sid in :sourceIds AND t.startAt is null AND t.status NOT IN ('Y','X','U') AND t.retryCnt > 0 """) - List findRunnableInheritedTasksBySource(@Param("sid") int sid); + List findRunnableInheritedTasksBySource(@Param("sid") int sid, + @Param("sourceIds") List sourceIds); /** * 查询“表级覆盖调度”的可运行任务(表 startAt 等于指定时间点) @@ -76,11 +84,13 @@ AND t.status NOT IN ('Y','X','U') @Query(""" SELECT t FROM EtlTable t JOIN EtlSource s on t.sid = s.id WHERE s.enabled = true + AND t.sid in :sourceIds AND t.startAt = :startAt AND t.status = 'N' AND t.retryCnt > 0 """) - List findRunnableOverrideTasksByStartAt(@Param("startAt") LocalTime startAt); + List findRunnableOverrideTasksByStartAt(@Param("startAt") LocalTime startAt, + @Param("sourceIds") List sourceIds); /** * 查询“表级覆盖调度”的可运行任务(表 startAt 落在指定时间窗口内,闭区间)。 @@ -88,6 +98,7 @@ AND t.status NOT IN ('Y','X','U') @Query(""" SELECT t FROM EtlTable t JOIN EtlSource s on t.sid = s.id WHERE s.enabled = true + AND t.sid in :sourceIds AND t.startAt is not null AND t.startAt >= :from AND t.startAt <= :to @@ -95,7 +106,8 @@ AND t.status NOT IN ('Y','X','U') AND t.retryCnt > 0 """) List findRunnableOverrideTasksBetween(@Param("from") LocalTime from, - @Param("to") LocalTime to); + @Param("to") LocalTime to, + @Param("sourceIds") List sourceIds); int countBySid(int sid); @@ -106,4 +118,10 @@ List findRunnableOverrideTasksBetween(@Param("from") LocalTime from, WHERE t.status <> 'X' AND s.enabled = true """) List findCanRefreshTables(); + + @Query(""" + SELECT t FROM EtlTable t JOIN EtlSource s on t.sid = s.id + WHERE t.status <> 'X' AND s.enabled = true AND t.sid in :sourceIds + """) + List findCanRefreshTablesBySourceIds(@Param("sourceIds") List sourceIds); } diff --git a/backend/src/main/java/com/wgzhao/addax/admin/scheduler/CollectionScheduler.java b/backend/src/main/java/com/wgzhao/addax/admin/scheduler/CollectionScheduler.java index 6d37a975..ce96a496 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/scheduler/CollectionScheduler.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/scheduler/CollectionScheduler.java @@ -3,6 +3,8 @@ import com.wgzhao.addax.admin.model.EtlSource; import com.wgzhao.addax.admin.redis.RedisLockService; import com.wgzhao.addax.admin.repository.EtlSourceRepo; +import com.wgzhao.addax.admin.service.SourceScheduleMatcher; +import com.wgzhao.addax.admin.service.SystemConfigService; import com.wgzhao.addax.admin.service.TaskSchedulerService; import com.wgzhao.addax.admin.service.TaskService; import lombok.AllArgsConstructor; @@ -12,6 +14,7 @@ import org.springframework.stereotype.Component; import java.time.Duration; +import java.time.LocalDate; import java.time.LocalTime; import java.util.List; @@ -24,6 +27,8 @@ public class CollectionScheduler private final EtlSourceRepo etlSourceRepo; private final TaskService taskService; private final RedisLockService redisLockService; + private final SourceScheduleMatcher sourceScheduleMatcher; + private final SystemConfigService configService; @EventListener(ApplicationReadyEvent.class) public void onApplicationReady() @@ -61,6 +66,12 @@ public void scheduleOrUpdateTask(EtlSource source) log.info("Could not acquire lock for source {}, skipping this run", source.getCode()); return; } + LocalDate bizDate = configService.getBizDateAsDate(); + if (!sourceScheduleMatcher.matches(source, bizDate)) { + log.info("Skip scheduled collection for source {} because collectDateMode={} does not match bizDate={}", + source.getCode(), source.getCollectDateMode(), bizDate); + return; + } // keep existing behavior: enqueue runnable tables for this source taskService.executeTasksForSource(source.getId()); } diff --git a/backend/src/main/java/com/wgzhao/addax/admin/service/SourceScheduleMatcher.java b/backend/src/main/java/com/wgzhao/addax/admin/service/SourceScheduleMatcher.java new file mode 100644 index 00000000..dc5f45c4 --- /dev/null +++ b/backend/src/main/java/com/wgzhao/addax/admin/service/SourceScheduleMatcher.java @@ -0,0 +1,51 @@ +package com.wgzhao.addax.admin.service; + +import com.wgzhao.addax.admin.common.CollectDateMode; +import com.wgzhao.addax.admin.model.EtlSource; +import org.springframework.stereotype.Component; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.List; + +@Component +public class SourceScheduleMatcher +{ + public boolean matches(EtlSource source, LocalDate bizDate) + { + if (source == null || bizDate == null) { + return false; + } + return matches(resolveMode(source), bizDate.getDayOfWeek()); + } + + public List resolveMatchedEnabledSourceIds(List enabledSources, LocalDate bizDate) + { + if (enabledSources == null || enabledSources.isEmpty() || bizDate == null) { + return List.of(); + } + DayOfWeek dayOfWeek = bizDate.getDayOfWeek(); + return enabledSources.stream() + .filter(EtlSource::isEnabled) + .filter(source -> matches(resolveMode(source), dayOfWeek)) + .map(EtlSource::getId) + .toList(); + } + + private boolean matches(CollectDateMode mode, DayOfWeek dayOfWeek) + { + if (mode == CollectDateMode.WEEKDAY) { + return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; + } + if (mode == CollectDateMode.WEEKEND) { + return dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY; + } + return true; + } + + private CollectDateMode resolveMode(EtlSource source) + { + return source.getCollectDateMode() == null ? CollectDateMode.DAILY : source.getCollectDateMode(); + } +} + diff --git a/backend/src/main/java/com/wgzhao/addax/admin/service/SourceService.java b/backend/src/main/java/com/wgzhao/addax/admin/service/SourceService.java index cd49c6df..f55787ed 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/service/SourceService.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/service/SourceService.java @@ -1,6 +1,7 @@ package com.wgzhao.addax.admin.service; import com.wgzhao.addax.admin.common.DbType; +import com.wgzhao.addax.admin.common.CollectDateMode; import com.wgzhao.addax.admin.dto.TableMetaDto; import com.wgzhao.addax.admin.event.SourceUpdatedEvent; import com.wgzhao.addax.admin.model.EtlSource; @@ -117,11 +118,19 @@ public EtlSource save(EtlSource etlSource) etlSource.setDbType(parsed.getValue()); } + if (etlSource.getCollectDateMode() == null) { + etlSource.setCollectDateMode(CollectDateMode.DAILY); + } + etlSourceRepo.save(etlSource); if (existing == null) { return etlSource; } - boolean scheduleChanged = existing.getStartAt() != etlSource.getStartAt(); + CollectDateMode existingCollectDateMode = existing.getCollectDateMode() == null ? CollectDateMode.DAILY : existing.getCollectDateMode(); + CollectDateMode newCollectDateMode = etlSource.getCollectDateMode() == null ? CollectDateMode.DAILY : etlSource.getCollectDateMode(); + boolean scheduleChanged = !Objects.equals(existing.getStartAt(), etlSource.getStartAt()) + || existing.isEnabled() != etlSource.isEnabled() + || existingCollectDateMode != newCollectDateMode; // 更新该采集源下所有采集任务的模板,这里主要考虑到可能调整了采集源的连接参数 // 如果连接串,账号,密码三者没变更,则不要更新任务模板 @@ -185,9 +194,13 @@ public EtlSource create(EtlSource etlSource) etlSource.setDbType(parsed.getValue()); } + if (etlSource.getCollectDateMode() == null) { + etlSource.setCollectDateMode(CollectDateMode.DAILY); + } + EtlSource save = etlSourceRepo.save(etlSource); // 新采集源创建时,默认创建一个同步任务 - collectionScheduler.scheduleOrUpdateTask(etlSource); + collectionScheduler.scheduleOrUpdateTask(save); return save; } diff --git a/backend/src/main/java/com/wgzhao/addax/admin/service/TableService.java b/backend/src/main/java/com/wgzhao/addax/admin/service/TableService.java index bc5f969e..34aa40d3 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/service/TableService.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/service/TableService.java @@ -6,6 +6,7 @@ import com.wgzhao.addax.admin.model.EtlTable; import com.wgzhao.addax.admin.model.VwEtlTableWithSource; import com.wgzhao.addax.admin.redis.RedisLockService; +import com.wgzhao.addax.admin.repository.EtlSourceRepo; import com.wgzhao.addax.admin.repository.EtlTableRepo; import com.wgzhao.addax.admin.repository.VwEtlTableWithSourceRepo; import com.wgzhao.addax.admin.utils.QueryUtil; @@ -20,8 +21,10 @@ import org.springframework.transaction.annotation.Transactional; import java.sql.Timestamp; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -45,6 +48,8 @@ public class TableService private final RedisLockService redisLockService; private final SystemConfigService configService; private final UserNotificationService userNotificationService; + private final EtlSourceRepo etlSourceRepo; + private final SourceScheduleMatcher sourceScheduleMatcher; /** * 刷新指定采集表的资源(如字段、模板等) @@ -226,6 +231,30 @@ public void refreshAllTableResources() } } + public void refreshTableResourcesBySourceIds(List sourceIds) + { + if (sourceIds == null || sourceIds.isEmpty()) { + return; + } + List tables = etlTableRepo.findCanRefreshTablesBySourceIds(sourceIds); + for (EtlTable table : tables) { + if (Thread.currentThread().isInterrupted()) { + log.info("refreshTableResourcesBySourceIds interrupted, stopping at table {}", table.getId()); + break; + } + try { + refreshTableResources(table); + } + catch (Exception e) { + log.error("Failed to refresh resources for table {}", table.getId(), e); + } + if (!redisLockService.isRefreshInProgress()) { + log.info("Global schema refresh flag cleared, aborting refreshTableResourcesBySourceIds at table {}", table.getId()); + break; + } + } + } + @Async public void refreshAllTableResourcesAsyncWithNotify(String username) { @@ -436,7 +465,11 @@ public List getRunnableTasks() LocalTime switchTime = dictService.getSwitchTimeAsTime(); LocalTime currentTime = LocalDateTime.now().toLocalTime(); boolean checkTime = currentTime.isAfter(switchTime); - return etlTableRepo.findRunnableTasks(switchTime, currentTime, checkTime); + List sourceIds = resolveMatchedEnabledSourceIds(); + if (sourceIds.isEmpty()) { + return List.of(); + } + return etlTableRepo.findRunnableTasks(switchTime, currentTime, checkTime, sourceIds); } /** @@ -455,7 +488,11 @@ public List getRunnableTasks(int sourceId) LocalTime switchTime = dictService.getSwitchTimeAsTime(); LocalTime currentTime = LocalDateTime.now().toLocalTime(); boolean checkTime = currentTime.isAfter(switchTime); - return etlTableRepo.findRunnableTasks(switchTime, currentTime, checkTime) + List sourceIds = resolveMatchedEnabledSourceIds(); + if (sourceIds.isEmpty() || !sourceIds.contains(sourceId)) { + return List.of(); + } + return etlTableRepo.findRunnableTasks(switchTime, currentTime, checkTime, sourceIds) .stream() .filter(t -> t.getSid() == sourceId) .toList(); @@ -469,7 +506,11 @@ public List getRunnableInheritedTasksBySource(int sourceId) if (redisLockService.isRefreshInProgress()) { return List.of(); } - return etlTableRepo.findRunnableInheritedTasksBySource(sourceId); + List sourceIds = resolveMatchedEnabledSourceIds(); + if (sourceIds.isEmpty() || !sourceIds.contains(sourceId)) { + return List.of(); + } + return etlTableRepo.findRunnableInheritedTasksBySource(sourceId, sourceIds); } /** @@ -480,7 +521,11 @@ public List getRunnableOverrideTasksByStartAt(LocalTime startAt) if (redisLockService.isRefreshInProgress()) { return List.of(); } - return etlTableRepo.findRunnableOverrideTasksByStartAt(startAt); + List sourceIds = resolveMatchedEnabledSourceIds(); + if (sourceIds.isEmpty()) { + return List.of(); + } + return etlTableRepo.findRunnableOverrideTasksByStartAt(startAt, sourceIds); } public List getRunnableOverrideTasksBetween(LocalTime from, LocalTime to) @@ -488,7 +533,11 @@ public List getRunnableOverrideTasksBetween(LocalTime from, LocalTime if (redisLockService.isRefreshInProgress()) { return List.of(); } - return etlTableRepo.findRunnableOverrideTasksBetween(from, to); + List sourceIds = resolveMatchedEnabledSourceIds(); + if (sourceIds.isEmpty()) { + return List.of(); + } + return etlTableRepo.findRunnableOverrideTasksBetween(from, to, sourceIds); } /** @@ -525,6 +574,25 @@ public void resetAllFlags() etlTableRepo.resetAllEtlFlags(); } + @Transactional + public void resetFlagsBySourceIds(List sourceIds) + { + if (sourceIds == null || sourceIds.isEmpty()) { + return; + } + etlTableRepo.resetEtlFlagsBySourceIds(sourceIds); + } + + public List resolveMatchedEnabledSourceIds() + { + LocalDate bizDate = configService.getBizDateAsDate(); + List sourceIds = sourceScheduleMatcher.resolveMatchedEnabledSourceIds(etlSourceRepo.findByEnabled(true), bizDate); + if (sourceIds.isEmpty()) { + return List.of(); + } + return new ArrayList<>(sourceIds); + } + /** * 查找特殊任务 * diff --git a/backend/src/main/java/com/wgzhao/addax/admin/service/TaskService.java b/backend/src/main/java/com/wgzhao/addax/admin/service/TaskService.java index 3ec2c069..51bfd256 100644 --- a/backend/src/main/java/com/wgzhao/addax/admin/service/TaskService.java +++ b/backend/src/main/java/com/wgzhao/addax/admin/service/TaskService.java @@ -11,6 +11,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import java.time.LocalDate; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -42,6 +43,8 @@ public class TaskService private final ExecutionManager executionManager; private final StringRedisTemplate stringRedisTemplate; private final ObjectMapper objectMapper; + private final SourceService sourceService; + private final SourceScheduleMatcher sourceScheduleMatcher; /** * 执行指定采集源下的所有采集任务,将任务加入队列 @@ -84,13 +87,19 @@ public void updateParams() // Submit refresh job and wait with timeout future = executor.submit(() -> { try { - // Reset flags so tasks become eligible after refresh - tableService.resetAllFlags(); // Reload system configuration configService.loadConfig(); - // Refresh schema / resources for all tables. This checks source schema and updates target metadata when changed. - tableService.refreshAllTableResources(); + LocalDate bizDate = configService.getBizDateAsDate(); + List sourceIds = sourceScheduleMatcher.resolveMatchedEnabledSourceIds(sourceService.findEnabledSources(), bizDate); + if (sourceIds.isEmpty()) { + log.info("No source matches collect date policy on bizDate={}, skip flag reset and table refresh", bizDate); + } + else { + // Why: daily initialization should only affect sources collectable on current bizDate. + tableService.resetFlagsBySourceIds(sourceIds); + tableService.refreshTableResourcesBySourceIds(sourceIds); + } // truncate the job queue table except for running tasks // 106 行注释:在完成必要参数配置及初始化后,需要清理 etl_job_queue 中的历史记录 @@ -211,6 +220,7 @@ public List findAllSpecialTask() public TaskResultDto submitTask(long tableId) { + // Why: manual single-task run is mainly used for backfill, so it bypasses collect-date policy checks. // 如果 schema 刷新中(由 redis 锁控制),拒绝提交 try { if (redisLockService != null && redisLockService.isRefreshInProgress()) { @@ -231,6 +241,7 @@ public TaskResultDto submitTask(long tableId) public TaskResultDto submitTask(long tableId, String username) { + // Why: manual single-task run is mainly used for backfill, so it bypasses collect-date policy checks. // 如果 schema 刷新中(由 redis 锁控制),拒绝提交 try { if (redisLockService != null && redisLockService.isRefreshInProgress()) { diff --git a/frontend/src/components/source/AddSource.vue b/frontend/src/components/source/AddSource.vue index 88f2008f..da4e8c55 100644 --- a/frontend/src/components/source/AddSource.vue +++ b/frontend/src/components/source/AddSource.vue @@ -39,6 +39,20 @@ :error-messages="timeError" variant="outlined" density="compact" /> + +
+
采集日期
+ +
+
最大并发数
@@ -159,6 +173,7 @@ const defaultSourceItem: EtlSource = { username: "", pass: "", startAt: "", + collectDateMode: "DAILY", prerequisite: "", preScript: "", remark: "", @@ -168,6 +183,12 @@ const defaultSourceItem: EtlSource = { const sourceItem = ref({ ...defaultSourceItem }); +const collectDateModeItems = [ + { title: '每天', value: 'DAILY' }, + { title: '每个工作日', value: 'WEEKDAY' }, + { title: '仅周末', value: 'WEEKEND' } +]; + const emit = defineEmits(["closeDialog", "save"]); // 时间格式验证 @@ -243,6 +264,10 @@ const save = async () => { sourceItem.value.startAt = formatTimeInput(sourceItem.value.startAt); } + if (!sourceItem.value.collectDateMode) { + sourceItem.value.collectDateMode = 'DAILY'; + } + sourceService.save(sourceItem.value) .then(() => { notify('保存成功', 'success'); @@ -298,7 +323,10 @@ const initializeForm = () => { if (props.sid != -1) { sourceService.get(Number(props.sid)) .then(resp => { - sourceItem.value = resp; + sourceItem.value = { + ...resp, + collectDateMode: resp.collectDateMode || 'DAILY' + }; }) .catch(error => { console.log(error); diff --git a/frontend/src/types/database.ts b/frontend/src/types/database.ts index 671b9c00..0339e56a 100644 --- a/frontend/src/types/database.ts +++ b/frontend/src/types/database.ts @@ -184,6 +184,7 @@ export interface EtlSource { username?: string // 采集源连接的账号 pass?: string // 采集源连接的密码 startAt?: string // 采集的定时启动时间点 + collectDateMode?: 'DAILY' | 'WEEKDAY' | 'WEEKEND' // 采集日期模式 prerequisite?: string // 能否开始采集的先决条件 preScript?: string // 标志符合条件后的前置脚本 remark?: string // 系统备注信息 diff --git a/frontend/src/views/source.vue b/frontend/src/views/source.vue index a77af29d..70438062 100644 --- a/frontend/src/views/source.vue +++ b/frontend/src/views/source.vue @@ -60,6 +60,9 @@ class="ml-2" > +