diff --git a/README.md b/README.md index 17ee4eb..7ece2e6 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ 以下功能尚在开发中 -- [ ] 定时文档同步 +- [x] 定时文档同步 - [ ] 表字段协同注释 - [ ] 操作审计日志 -- [x] 容器化部署 +- [ ] 容器化部署 ## 简介 @@ -28,20 +28,38 @@ Databasir 采用了前后端分离的模式进行开发和部署,前端和后 ## 展示 +- 首页 + ![](README/home.jpg) +- 项目中心 + ![](README/group-projects.jpg) +- 项目创建 + ![](README/group-project-create.jpg) +- 项目文档 + ![](README/group-project-document.jpg) +- 分组成员 + ![](README/group-member-list.jpg) +- 添加成员 + ![](README/group-member-add.jpg) +- 用户中心 + ![](README/user.jpg) +- 个人中心 + ![](README/user-profile.jpg) +- 系统邮件 + ![](README/sys-mail.jpg) diff --git a/api/build.gradle b/api/build.gradle index 7e26378..d4f5551 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -25,6 +25,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-quartz' + implementation 'org.flywaydb:flyway-core' } diff --git a/api/src/main/java/com/databasir/DatabasirApplication.java b/api/src/main/java/com/databasir/DatabasirApplication.java index 8ace436..d7081cb 100644 --- a/api/src/main/java/com/databasir/DatabasirApplication.java +++ b/api/src/main/java/com/databasir/DatabasirApplication.java @@ -3,8 +3,10 @@ package com.databasir; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(exclude = {R2dbcAutoConfiguration.class}) +@EnableScheduling public class DatabasirApplication { public static void main(String[] args) { diff --git a/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncJob.java b/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncJob.java new file mode 100644 index 0000000..6ec5161 --- /dev/null +++ b/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncJob.java @@ -0,0 +1,25 @@ +package com.databasir.job; + +import com.databasir.core.domain.document.service.DocumentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +@RequiredArgsConstructor +@Slf4j +public class ProjectDocumentAutoSyncJob implements Job { + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobDataMap dataMap = context.getMergedJobDataMap(); + log.info("start sync project document: " + dataMap.toString()); + DocumentService documentService = (DocumentService) dataMap.get("documentService"); + Integer projectId = dataMap.getInt("projectId"); + documentService.syncByProjectId(projectId); + log.info("sync project document {} over....", projectId); + } + +} diff --git a/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncTriggerJob.java b/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncTriggerJob.java new file mode 100644 index 0000000..a30a1e6 --- /dev/null +++ b/api/src/main/java/com/databasir/job/ProjectDocumentAutoSyncTriggerJob.java @@ -0,0 +1,166 @@ +package com.databasir.job; + +import com.databasir.core.domain.document.service.DocumentService; +import com.databasir.dao.impl.ProjectSyncRuleDao; +import com.databasir.dao.tables.pojos.ProjectSyncRulePojo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.quartz.impl.matchers.GroupMatcher; +import org.springframework.data.util.Pair; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ProjectDocumentAutoSyncTriggerJob { + + private static final String JOB_IDENTITY_PATTERN = "JOB_PROJECT[%s]"; + + private static final String TRIGGER_IDENTITY_PATTERN = "TRIGGER_PROJECT[%s]"; + + private final Scheduler scheduler; + + private final ProjectSyncRuleDao projectSyncRuleDao; + + private final DocumentService documentService; + + /** + * 启动 isAutoSync=true 的任务 + */ + @Scheduled(fixedRate = 1000) + public void scheduleProjectSync() { + List currentlyTriggerProjectIds = getProjectJobDetails() + .stream() + .map(job -> job.getJobDataMap().getInt("projectId")) + .collect(Collectors.toList()); + // 查询已开启自动同步但还未运行的任务 + projectSyncRuleDao.selectByIsAutoSyncAndNotInProjectIds(true, currentlyTriggerProjectIds) + .stream() + .map(this::jobAndTriggers) + .forEach(jobAndTriggers -> { + try { + if (!scheduler.checkExists(jobAndTriggers.getSecond().getKey())) { + scheduler.scheduleJob(jobAndTriggers.getFirst(), jobAndTriggers.getSecond()); + log.info("schedule jobs: {}", jobAndTriggers.getFirst()); + } + } catch (SchedulerException e) { + log.error("failed to unScheduleProjectSync.scheduleJob ", e); + } + }); + } + + /** + * 停止 Rule.cron 更新过的任务 + */ + @Scheduled(fixedRate = 1000) + public void reScheduleProjectSync() { + List triggerKeys = getProjectJobDetails() + .stream() + .filter(jobDetail -> { + JobDataMap dataMap = jobDetail.getJobDataMap(); + int projectId = dataMap.getInt("projectId"); + String cron = dataMap.getString("cron"); + return !projectSyncRuleDao.existsByProjectIdAndCron(projectId, cron); + }) + .map(jobDetail -> jobDetail.getJobDataMap().getInt("projectId")) + .map(this::triggerKey) + .collect(Collectors.toList()); + try { + if (!triggerKeys.isEmpty()) { + scheduler.unscheduleJobs(triggerKeys); + log.info("rescheduled jobs: {}", triggerKeys); + } + } catch (SchedulerException e) { + log.error("failed to reScheduleProjectSync ", e); + } + } + + /** + * 停止 isAutoSync=false 的项目 + */ + @Scheduled(fixedRate = 1000) + public void unScheduleProjectSync() { + List projectIds = getProjectJobDetails() + .stream() + .map(job -> job.getJobDataMap().getInt("projectId")) + .collect(Collectors.toList()); + // 查询已关闭自动同步但还在运行的任务 + List triggerKeys = projectSyncRuleDao.selectByIsAutoSyncAndProjectIds(false, projectIds) + .stream() + .map(p -> triggerKey(p.getProjectId())) + .collect(Collectors.toList()); + try { + if (!triggerKeys.isEmpty()) { + scheduler.unscheduleJobs(triggerKeys); + log.info("unscheduled jobs: {}", triggerKeys); + } + } catch (SchedulerException e) { + log.error("failed to unScheduleProjectSync ", e); + } + } + + private List getProjectJobDetails() { + try { + return scheduler.getJobKeys(GroupMatcher.anyGroup()) + .stream() + .map(jobKey -> { + try { + JobDetail job = scheduler.getJobDetail(jobKey); + if (job.getJobDataMap().containsKey("projectId")) { + return Optional.of(job); + } else { + return Optional.empty(); + } + } catch (SchedulerException e) { + log.error("getJobDetail error " + jobKey, e); + return Optional.empty(); + } + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } catch (SchedulerException e) { + log.error("get jobKeys error", e); + return Collections.emptyList(); + } + } + + private Pair jobAndTriggers(ProjectSyncRulePojo rule) { + JobDataMap dataMap = new JobDataMap(); + Integer projectId = rule.getProjectId(); + Integer ruleId = rule.getId(); + dataMap.put("projectId", projectId); + dataMap.put("ruleId", ruleId); + dataMap.put("cron", rule.getAutoSyncCron()); + dataMap.put("documentService", documentService); + JobDetail job = JobBuilder.newJob() + .ofType(ProjectDocumentAutoSyncJob.class) + .withIdentity(jobKey(projectId)) + .withDescription("auto sync project document") + .usingJobData(dataMap) + .build(); + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(rule.getAutoSyncCron()); + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(triggerKey(projectId)) + .forJob(job) + .withSchedule(scheduleBuilder) + .startNow() + .build(); + return Pair.of(job, trigger); + } + + private TriggerKey triggerKey(Integer projectId) { + return TriggerKey.triggerKey(String.format(TRIGGER_IDENTITY_PATTERN, projectId)); + } + + private JobKey jobKey(Integer projectId) { + return JobKey.jobKey(String.format(JOB_IDENTITY_PATTERN, projectId)); + } +} diff --git a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java index c9726ed..b2123ee 100644 --- a/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java +++ b/core/src/main/java/com/databasir/core/domain/document/service/DocumentService.java @@ -176,7 +176,7 @@ public class DocumentService { public Page getVersionsBySchemaSourceId(Integer projectId, Pageable page) { return databaseDocumentDao.selectOptionalByProjectId(projectId) - .map(schemaMeta -> databaseDocumentHistoryDao.selectPageByDatabaseDocumentId(page, schemaMeta.getId()) + .map(schemaMeta -> databaseDocumentHistoryDao.selectVersionPageByDatabaseDocumentId(page, schemaMeta.getId()) .map(history -> DatabaseDocumentVersionResponse.builder() .version(history.getVersion()) .createAt(history.getCreateAt()) diff --git a/core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java b/core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java index f97b19c..046b9fa 100644 --- a/core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java +++ b/core/src/main/java/com/databasir/core/domain/project/converter/ProjectResponseConverter.java @@ -31,5 +31,7 @@ public interface ProjectResponseConverter { @Mapping(target = "id", source = "project.id") @Mapping(target = "createAt", source = "project.createAt") - ProjectSimpleResponse toSimple(ProjectPojo project, DataSourcePojo dataSource); + ProjectSimpleResponse toSimple(ProjectPojo project, + DataSourcePojo dataSource, + ProjectSyncRulePojo syncRule); } diff --git a/core/src/main/java/com/databasir/core/domain/project/data/ProjectCreateRequest.java b/core/src/main/java/com/databasir/core/domain/project/data/ProjectCreateRequest.java index dd39937..f2d0ee0 100644 --- a/core/src/main/java/com/databasir/core/domain/project/data/ProjectCreateRequest.java +++ b/core/src/main/java/com/databasir/core/domain/project/data/ProjectCreateRequest.java @@ -54,6 +54,10 @@ public class ProjectCreateRequest { private List ignoreColumnNameRegexes = new ArrayList<>(); + private Boolean isAutoSync = false; + + private String autoSyncCron; + } } diff --git a/core/src/main/java/com/databasir/core/domain/project/data/ProjectDetailResponse.java b/core/src/main/java/com/databasir/core/domain/project/data/ProjectDetailResponse.java index 37daa73..5776654 100644 --- a/core/src/main/java/com/databasir/core/domain/project/data/ProjectDetailResponse.java +++ b/core/src/main/java/com/databasir/core/domain/project/data/ProjectDetailResponse.java @@ -15,6 +15,8 @@ public class ProjectDetailResponse { private String description; + private Integer groupId; + private DataSourceResponse dataSource; private ProjectSyncRuleResponse projectSyncRule; @@ -51,6 +53,10 @@ public class ProjectDetailResponse { private List ignoreColumnNameRegexes = new ArrayList<>(); + private Boolean isAutoSync; + + private String autoSyncCron; + private LocalDateTime createAt; } } diff --git a/core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java b/core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java index 770addd..6c06eea 100644 --- a/core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java +++ b/core/src/main/java/com/databasir/core/domain/project/data/ProjectSimpleResponse.java @@ -17,6 +17,10 @@ public class ProjectSimpleResponse { private String databaseType; + private Boolean isAutoSync; + + private String autoSyncCron; + private LocalDateTime createAt; } diff --git a/core/src/main/java/com/databasir/core/domain/project/data/ProjectUpdateRequest.java b/core/src/main/java/com/databasir/core/domain/project/data/ProjectUpdateRequest.java index cbc46f0..fb88995 100644 --- a/core/src/main/java/com/databasir/core/domain/project/data/ProjectUpdateRequest.java +++ b/core/src/main/java/com/databasir/core/domain/project/data/ProjectUpdateRequest.java @@ -52,5 +52,10 @@ public class ProjectUpdateRequest { private List ignoreColumnNameRegexes = new ArrayList<>(); + @NotNull + private Boolean isAutoSync; + + private String autoSyncCron; + } } diff --git a/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java b/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java index b7b7e9f..e1f8adf 100644 --- a/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java +++ b/core/src/main/java/com/databasir/core/domain/project/service/ProjectService.java @@ -73,7 +73,6 @@ public class ProjectService { List properties = dataSourcePojoConverter.of(propertyValues, dataSourceId); dataSourcePropertyDao.batchInsert(properties); - // TODO redesign it ProjectSyncRulePojo syncRule = projectPojoConverter.of(request.getProjectSyncRule(), projectId); projectSyncRuleDao.insertAndReturnId(syncRule); } @@ -98,9 +97,10 @@ public class ProjectService { dataSourcePropertyDao.batchInsert(properties); } - // update project sync rule TODO redesign it + // update project sync rule ProjectSyncRulePojo syncRule = projectPojoConverter.of(request.getProjectSyncRule(), projectId); - projectSyncRuleDao.updateByProjectId(syncRule); + projectSyncRuleDao.deleteByProjectId(projectId); + projectSyncRuleDao.insertAndReturnId(syncRule); // update project info ProjectPojo project = projectPojoConverter.of(request); @@ -132,9 +132,13 @@ public class ProjectService { Map dataSourceMapByProjectId = dataSourceDao.selectInProjectIds(projectIds) .stream() .collect(Collectors.toMap(DataSourcePojo::getProjectId, Function.identity())); + Map syncRuleMapByProjectId = projectSyncRuleDao.selectInProjectIds(projectIds) + .stream() + .collect(Collectors.toMap(ProjectSyncRulePojo::getProjectId, Function.identity())); return pageData.map(project -> { DataSourcePojo dataSource = dataSourceMapByProjectId.get(project.getId()); - return projectResponseConverter.toSimple(project, dataSource); + ProjectSyncRulePojo syncRule = syncRuleMapByProjectId.get(project.getId()); + return projectResponseConverter.toSimple(project, dataSource, syncRule); }); } diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/Login.java b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/Login.java index 55dc139..0f4cd34 100644 --- a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/Login.java +++ b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/Login.java @@ -72,12 +72,12 @@ public class Login extends TableImpl { /** * The column databasir.login.access_token_expire_at. */ - public final TableField ACCESS_TOKEN_EXPIRE_AT = createField(DSL.name("access_token_expire_at"), SQLDataType.LOCALDATETIME(0).nullable(false), this, ""); + public final TableField ACCESS_TOKEN_EXPIRE_AT = createField(DSL.name("access_token_expire_at"), SQLDataType.LOCALDATETIME(0).nullable(false).defaultValue(DSL.field("CURRENT_TIMESTAMP", SQLDataType.LOCALDATETIME)), this, ""); /** * The column databasir.login.refresh_token_expire_at. */ - public final TableField REFRESH_TOKEN_EXPIRE_AT = createField(DSL.name("refresh_token_expire_at"), SQLDataType.LOCALDATETIME(0).nullable(false), this, ""); + public final TableField REFRESH_TOKEN_EXPIRE_AT = createField(DSL.name("refresh_token_expire_at"), SQLDataType.LOCALDATETIME(0).nullable(false).defaultValue(DSL.field("CURRENT_TIMESTAMP", SQLDataType.LOCALDATETIME)), this, ""); /** * The column databasir.login.update_at. diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/ProjectSyncRule.java b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/ProjectSyncRule.java index 3407302..fe82bad 100644 --- a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/ProjectSyncRule.java +++ b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/ProjectSyncRule.java @@ -18,7 +18,7 @@ import org.jooq.Identity; import org.jooq.JSON; import org.jooq.Name; import org.jooq.Record; -import org.jooq.Row6; +import org.jooq.Row8; import org.jooq.Schema; import org.jooq.Table; import org.jooq.TableField; @@ -72,6 +72,16 @@ public class ProjectSyncRule extends TableImpl { */ public final TableField IGNORE_COLUMN_NAME_REGEX_ARRAY = createField(DSL.name("ignore_column_name_regex_array"), SQLDataType.JSON.nullable(false), this, ""); + /** + * The column databasir.project_sync_rule.is_auto_sync. + */ + public final TableField IS_AUTO_SYNC = createField(DSL.name("is_auto_sync"), SQLDataType.BOOLEAN.nullable(false).defaultValue(DSL.inline("0", SQLDataType.BOOLEAN)), this, ""); + + /** + * The column databasir.project_sync_rule.auto_sync_cron. + */ + public final TableField AUTO_SYNC_CRON = createField(DSL.name("auto_sync_cron"), SQLDataType.VARCHAR(128).nullable(false).defaultValue(DSL.inline("", SQLDataType.VARCHAR)), this, ""); + /** * The column databasir.project_sync_rule.update_at. */ @@ -164,11 +174,11 @@ public class ProjectSyncRule extends TableImpl { } // ------------------------------------------------------------------------- - // Row6 type methods + // Row8 type methods // ------------------------------------------------------------------------- @Override - public Row6 fieldsRow() { - return (Row6) super.fieldsRow(); + public Row8 fieldsRow() { + return (Row8) super.fieldsRow(); } } diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/pojos/ProjectSyncRulePojo.java b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/pojos/ProjectSyncRulePojo.java index d16d912..147a608 100644 --- a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/pojos/ProjectSyncRulePojo.java +++ b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/pojos/ProjectSyncRulePojo.java @@ -22,6 +22,8 @@ public class ProjectSyncRulePojo implements Serializable { private Integer projectId; private JSON ignoreTableNameRegexArray; private JSON ignoreColumnNameRegexArray; + private Boolean isAutoSync; + private String autoSyncCron; private LocalDateTime updateAt; private LocalDateTime createAt; @@ -32,6 +34,8 @@ public class ProjectSyncRulePojo implements Serializable { this.projectId = value.projectId; this.ignoreTableNameRegexArray = value.ignoreTableNameRegexArray; this.ignoreColumnNameRegexArray = value.ignoreColumnNameRegexArray; + this.isAutoSync = value.isAutoSync; + this.autoSyncCron = value.autoSyncCron; this.updateAt = value.updateAt; this.createAt = value.createAt; } @@ -41,6 +45,8 @@ public class ProjectSyncRulePojo implements Serializable { Integer projectId, JSON ignoreTableNameRegexArray, JSON ignoreColumnNameRegexArray, + Boolean isAutoSync, + String autoSyncCron, LocalDateTime updateAt, LocalDateTime createAt ) { @@ -48,6 +54,8 @@ public class ProjectSyncRulePojo implements Serializable { this.projectId = projectId; this.ignoreTableNameRegexArray = ignoreTableNameRegexArray; this.ignoreColumnNameRegexArray = ignoreColumnNameRegexArray; + this.isAutoSync = isAutoSync; + this.autoSyncCron = autoSyncCron; this.updateAt = updateAt; this.createAt = createAt; } @@ -112,6 +120,34 @@ public class ProjectSyncRulePojo implements Serializable { this.ignoreColumnNameRegexArray = ignoreColumnNameRegexArray; } + /** + * Getter for databasir.project_sync_rule.is_auto_sync. + */ + public Boolean getIsAutoSync() { + return this.isAutoSync; + } + + /** + * Setter for databasir.project_sync_rule.is_auto_sync. + */ + public void setIsAutoSync(Boolean isAutoSync) { + this.isAutoSync = isAutoSync; + } + + /** + * Getter for databasir.project_sync_rule.auto_sync_cron. + */ + public String getAutoSyncCron() { + return this.autoSyncCron; + } + + /** + * Setter for databasir.project_sync_rule.auto_sync_cron. + */ + public void setAutoSyncCron(String autoSyncCron) { + this.autoSyncCron = autoSyncCron; + } + /** * Getter for databasir.project_sync_rule.update_at. */ @@ -148,6 +184,8 @@ public class ProjectSyncRulePojo implements Serializable { sb.append(", ").append(projectId); sb.append(", ").append(ignoreTableNameRegexArray); sb.append(", ").append(ignoreColumnNameRegexArray); + sb.append(", ").append(isAutoSync); + sb.append(", ").append(autoSyncCron); sb.append(", ").append(updateAt); sb.append(", ").append(createAt); diff --git a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/records/ProjectSyncRuleRecord.java b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/records/ProjectSyncRuleRecord.java index 5c7e61c..f5d93fd 100644 --- a/dao/generated-src/jooq/main/java/com/databasir/dao/tables/records/ProjectSyncRuleRecord.java +++ b/dao/generated-src/jooq/main/java/com/databasir/dao/tables/records/ProjectSyncRuleRecord.java @@ -12,8 +12,8 @@ import java.time.LocalDateTime; import org.jooq.Field; import org.jooq.JSON; import org.jooq.Record1; -import org.jooq.Record6; -import org.jooq.Row6; +import org.jooq.Record8; +import org.jooq.Row8; import org.jooq.impl.UpdatableRecordImpl; @@ -21,7 +21,7 @@ import org.jooq.impl.UpdatableRecordImpl; * This class is generated by jOOQ. */ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) -public class ProjectSyncRuleRecord extends UpdatableRecordImpl implements Record6 { +public class ProjectSyncRuleRecord extends UpdatableRecordImpl implements Record8 { private static final long serialVersionUID = 1L; @@ -85,32 +85,60 @@ public class ProjectSyncRuleRecord extends UpdatableRecordImpldatabasir.project_sync_rule.is_auto_sync. + */ + public void setIsAutoSync(Boolean value) { + set(4, value); + } + + /** + * Getter for databasir.project_sync_rule.is_auto_sync. + */ + public Boolean getIsAutoSync() { + return (Boolean) get(4); + } + + /** + * Setter for databasir.project_sync_rule.auto_sync_cron. + */ + public void setAutoSyncCron(String value) { + set(5, value); + } + + /** + * Getter for databasir.project_sync_rule.auto_sync_cron. + */ + public String getAutoSyncCron() { + return (String) get(5); + } + /** * Setter for databasir.project_sync_rule.update_at. */ public void setUpdateAt(LocalDateTime value) { - set(4, value); + set(6, value); } /** * Getter for databasir.project_sync_rule.update_at. */ public LocalDateTime getUpdateAt() { - return (LocalDateTime) get(4); + return (LocalDateTime) get(6); } /** * Setter for databasir.project_sync_rule.create_at. */ public void setCreateAt(LocalDateTime value) { - set(5, value); + set(7, value); } /** * Getter for databasir.project_sync_rule.create_at. */ public LocalDateTime getCreateAt() { - return (LocalDateTime) get(5); + return (LocalDateTime) get(7); } // ------------------------------------------------------------------------- @@ -123,17 +151,17 @@ public class ProjectSyncRuleRecord extends UpdatableRecordImpl fieldsRow() { - return (Row6) super.fieldsRow(); + public Row8 fieldsRow() { + return (Row8) super.fieldsRow(); } @Override - public Row6 valuesRow() { - return (Row6) super.valuesRow(); + public Row8 valuesRow() { + return (Row8) super.valuesRow(); } @Override @@ -157,12 +185,22 @@ public class ProjectSyncRuleRecord extends UpdatableRecordImpl field5() { + public Field field5() { + return ProjectSyncRule.PROJECT_SYNC_RULE.IS_AUTO_SYNC; + } + + @Override + public Field field6() { + return ProjectSyncRule.PROJECT_SYNC_RULE.AUTO_SYNC_CRON; + } + + @Override + public Field field7() { return ProjectSyncRule.PROJECT_SYNC_RULE.UPDATE_AT; } @Override - public Field field6() { + public Field field8() { return ProjectSyncRule.PROJECT_SYNC_RULE.CREATE_AT; } @@ -187,12 +225,22 @@ public class ProjectSyncRuleRecord extends UpdatableRecordImpl selectPageByDatabaseDocumentId(Pageable request, Integer schemaDocumentId) { - return super.selectByPage(request, DATABASE_DOCUMENT_HISTORY.DATABASE_DOCUMENT_ID.eq(schemaDocumentId)); + public Page selectVersionPageByDatabaseDocumentId(Pageable request, Integer schemaDocumentId) { + Condition condition = DATABASE_DOCUMENT_HISTORY.DATABASE_DOCUMENT_ID.eq(schemaDocumentId); + Integer count = getDslContext() + .selectCount().from(DATABASE_DOCUMENT_HISTORY).where(condition) + .fetchOne(0, int.class); + int total = count == null ? 0 : count; + List data = getDslContext() + .select( + DATABASE_DOCUMENT_HISTORY.VERSION, + DATABASE_DOCUMENT_HISTORY.DATABASE_DOCUMENT_ID, + DATABASE_DOCUMENT_HISTORY.CREATE_AT + ) + .from(DATABASE_DOCUMENT_HISTORY) + .where(condition) + .orderBy(getSortFields(request.getSort())) + .offset(request.getOffset()) + .limit(request.getPageSize()) + .fetchInto(DatabaseDocumentVersionPojo.class); + return new PageImpl<>(data, request, total); } - } diff --git a/dao/src/main/java/com/databasir/dao/impl/ProjectSyncRuleDao.java b/dao/src/main/java/com/databasir/dao/impl/ProjectSyncRuleDao.java index 70efc5c..a0a45f5 100644 --- a/dao/src/main/java/com/databasir/dao/impl/ProjectSyncRuleDao.java +++ b/dao/src/main/java/com/databasir/dao/impl/ProjectSyncRuleDao.java @@ -9,6 +9,8 @@ import org.jooq.DSLContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collections; +import java.util.List; import java.util.Optional; import static com.databasir.dao.Tables.PROJECT_SYNC_RULE; @@ -46,4 +48,51 @@ public class ProjectSyncRuleDao extends BaseDao selectInProjectIds(List projectIds) { + if (projectIds == null || projectIds.isEmpty()) { + return Collections.emptyList(); + } + return getDslContext() + .selectFrom(PROJECT_SYNC_RULE).where(PROJECT_SYNC_RULE.PROJECT_ID.in(projectIds)) + .fetchInto(ProjectSyncRulePojo.class); + } + + public List selectByIsAutoSyncAndProjectIds(boolean isAutoSync, List projectIds) { + if (projectIds == null || projectIds.isEmpty()) { + return Collections.emptyList(); + } + return getDslContext() + .selectFrom(PROJECT_SYNC_RULE).where( + PROJECT_SYNC_RULE.IS_AUTO_SYNC.eq(isAutoSync) + .and(PROJECT_SYNC_RULE.PROJECT_ID.in(projectIds)) + ) + .fetchInto(ProjectSyncRulePojo.class); + } + + public List selectByIsAutoSyncAndNotInProjectIds(boolean isAutoSync, List projectIds) { + if (projectIds == null || projectIds.isEmpty()) { + return getDslContext() + .selectFrom(PROJECT_SYNC_RULE) + .where(PROJECT_SYNC_RULE.IS_AUTO_SYNC.eq(isAutoSync) + .and(PROJECT_SYNC_RULE.PROJECT_ID.notIn(projectIds))) + .fetchInto(ProjectSyncRulePojo.class); + } else { + return getDslContext() + .selectFrom(PROJECT_SYNC_RULE).where(PROJECT_SYNC_RULE.IS_AUTO_SYNC.eq(isAutoSync)) + .fetchInto(ProjectSyncRulePojo.class); + } + } + + public boolean existsByProjectIdAndCron(int projectId, String cron) { + return dslContext.fetchExists(PROJECT_SYNC_RULE, + PROJECT_SYNC_RULE.PROJECT_ID.eq(projectId) + .and(PROJECT_SYNC_RULE.AUTO_SYNC_CRON.eq(cron))); + } } diff --git a/dao/src/main/java/com/databasir/dao/value/DatabaseDocumentVersionPojo.java b/dao/src/main/java/com/databasir/dao/value/DatabaseDocumentVersionPojo.java new file mode 100644 index 0000000..064c38f --- /dev/null +++ b/dao/src/main/java/com/databasir/dao/value/DatabaseDocumentVersionPojo.java @@ -0,0 +1,15 @@ +package com.databasir.dao.value; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class DatabaseDocumentVersionPojo { + + private Integer databaseDocumentId; + + private Long version; + + private LocalDateTime createAt; +} diff --git a/dao/src/main/resources/db/migration/V1__init.sql b/dao/src/main/resources/db/migration/V1__init.sql index 4af21d2..8facb26 100644 --- a/dao/src/main/resources/db/migration/V1__init.sql +++ b/dao/src/main/resources/db/migration/V1__init.sql @@ -78,11 +78,13 @@ CREATE TABLE `project` CREATE TABLE `project_sync_rule` ( id INT PRIMARY KEY AUTO_INCREMENT, - project_id INT NOT NULL, - ignore_table_name_regex_array JSON NOT NULL, - ignore_column_name_regex_array JSON NOT NULL, - update_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - create_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + project_id INT NOT NULL, + ignore_table_name_regex_array JSON NOT NULL, + ignore_column_name_regex_array JSON NOT NULL, + is_auto_sync BOOLEAN NOT NULL DEFAULT FALSE, + auto_sync_cron VARCHAR(128) NOT NULL DEFAULT '', + update_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + create_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT UNIQUE uk_project_id (project_id) ) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;