feat: 完善数据中枢联邦查询闭环

- 重构数据源生命周期、元数据纳管与运行时切换

- 增加只读 SQL、查询审计、跨节点取消与工作流联动

- 完善管理端连接配置、元数据浏览与 SQL 工作台
This commit is contained in:
2026-08-26 18:14:34 +08:00
parent 3e79e99925
commit e38821e48a
113 changed files with 17032 additions and 1151 deletions

View File

@@ -24,6 +24,11 @@ import java.util.List;
@RequestMapping("/api/v1/datacenterDataset")
public class DatacenterDatasetController {
/** 对外 Schema 接口的默认字段页码。 */
private static final long DEFAULT_FIELD_PAGE_NUMBER = 1L;
/** 对外 Schema 接口的默认字段页大小。 */
private static final long DEFAULT_FIELD_PAGE_SIZE = 200L;
@Resource
private DatacenterDatasetQueryService queryService;
@Resource
@@ -32,13 +37,18 @@ public class DatacenterDatasetController {
@PostMapping("/queryPage")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<Page<Row>> queryPage(@RequestBody DatacenterQueryRequest request) {
return Result.ok(queryService.queryPage(request));
return Result.ok(queryService.queryPage(
request, SaTokenUtil.getLoginAccount()));
}
@GetMapping("/schema")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterSchemaResponse> schema(DatasetRef datasetRef) {
return Result.ok(queryService.getSchema(datasetRef));
public Result<DatacenterSchemaResponse> schema(
DatasetRef datasetRef,
@RequestParam(defaultValue = "1") Long fieldPageNumber,
@RequestParam(defaultValue = "200") Long fieldPageSize) {
return Result.ok(queryService.getSchema(
datasetRef, fieldPageNumber, fieldPageSize));
}
@GetMapping("/managedTables")
@@ -63,6 +73,13 @@ public class DatacenterDatasetController {
request == null ? List.of() : request.getFields(),
account
);
return Result.ok(queryService.getSchema(registryService.resolveDatasetRef(table.getId())));
return Result.ok(queryService.getSchema(
registryService.resolveDatasetRef(table.getId()),
request == null || request.getFieldPageNumber() == null
? DEFAULT_FIELD_PAGE_NUMBER
: request.getFieldPageNumber(),
request == null || request.getFieldPageSize() == null
? DEFAULT_FIELD_PAGE_SIZE
: request.getFieldPageSize()));
}
}

View File

@@ -0,0 +1,84 @@
package tech.easyflow.admin.controller.datacenter;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.datacenter.execution.model.DatacenterSqlConsoleRequest;
import tech.easyflow.datacenter.execution.model.DatacenterSqlConsoleResult;
import tech.easyflow.datacenter.execution.model.DatacenterSqlCancelRequest;
import tech.easyflow.datacenter.federation.DatacenterFederationQueryService;
import tech.easyflow.datacenter.federation.DatacenterFederationQueryCancellationService;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
/**
* 数据中枢管理端只读 SQL 控制台。
*/
@RestController
@RequestMapping("/api/v1/datacenterQuery")
public class DatacenterQueryController {
private final DatacenterDatasetRegistryService registryService;
private final DatacenterFederationQueryService queryService;
private final DatacenterFederationQueryCancellationService cancellationService;
/**
* 创建查询 Controller。
*
* @param registryService 数据集注册服务
* @param queryService Federation 查询服务
* @param cancellationService 跨节点查询取消服务
*/
public DatacenterQueryController(
DatacenterDatasetRegistryService registryService,
DatacenterFederationQueryService queryService,
DatacenterFederationQueryCancellationService cancellationService) {
this.registryService = registryService;
this.queryService = queryService;
this.cancellationService = cancellationService;
}
/**
* 执行一条受 Calcite 与业务 Policy 校验的只读 SQL。
*
* @param request 查询请求
* @return 有界查询结果
*/
@PostMapping("/execute")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterSqlConsoleResult> execute(
@RequestBody DatacenterSqlConsoleRequest request) {
LoginAccount account = SaTokenUtil.getLoginAccount();
DatacenterSource source = registryService.getSourceRequired(
request == null ? null : request.sourceId());
return Result.ok(queryService.execute(
source,
request == null ? null : request.sql(),
java.util.List.of(),
request == null ? null : request.maxRows(),
account,
"MANUAL",
account == null || account.getId() == null
? null : account.getId().toString(),
request == null ? null : request.queryId()));
}
/**
* 取消当前租户在任一节点执行的 SQL 查询。
*
* @param request 取消请求
* @return 是否在本地或集群中接受取消提示
*/
@PostMapping("/cancel")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<Boolean> cancel(@RequestBody DatacenterSqlCancelRequest request) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(cancellationService.cancel(
request == null ? null : request.queryId(), account));
}
}

View File

@@ -8,10 +8,16 @@ import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.datacenter.entity.DatacenterTable;
import tech.easyflow.datacenter.execution.model.DatacenterConnectionTestResult;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.model.DatacenterBatchRegisterRequest;
import tech.easyflow.datacenter.meta.model.DatacenterCatalogMeta;
import tech.easyflow.datacenter.meta.model.DatacenterMetadataPage;
import tech.easyflow.datacenter.meta.model.DatacenterRemoveSourceRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceActivateRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceCandidateMetadataRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceCandidateCatalogRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceDraftRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceReconfigureRequest;
import tech.easyflow.datacenter.meta.model.DatacenterSourceView;
import tech.easyflow.datacenter.meta.model.DatacenterTableDetailMeta;
import tech.easyflow.datacenter.meta.service.DatacenterSourceService;
@@ -19,6 +25,9 @@ import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.List;
/**
* 数据源绑定、生命周期与元数据浏览接口。
*/
@RestController
@RequestMapping("/api/v1/datacenterSource")
public class DatacenterSourceController {
@@ -26,23 +35,100 @@ public class DatacenterSourceController {
@Resource
private DatacenterSourceService sourceService;
@PostMapping("/testConnection")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterConnectionTestResult> testConnection(@RequestBody DatacenterSource source) {
@PostMapping("/draft")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> saveDraft(@RequestBody DatacenterSourceDraftRequest request) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.testConnection(source, account));
return Result.ok(sourceService.saveDraft(request, account));
}
@PostMapping("/save")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSource> save(@RequestBody DatacenterSource source) {
@PostMapping("/{sourceId}/probe")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterConnectionTestResult> probe(@PathVariable BigInteger sourceId) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.saveSource(source, account));
return Result.ok(sourceService.probe(sourceId, account));
}
@PostMapping("/activate")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> activate(@RequestBody DatacenterSourceActivateRequest request) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.activate(request, account));
}
/**
* 探测活动数据源的未发布候选配置。
*
* @param request 候选连接配置
* @return 连接探测结果
*/
@PostMapping("/candidate/probe")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterConnectionTestResult> probeCandidate(
@RequestBody DatacenterSourceDraftRequest request) {
return Result.ok(sourceService.probeCandidate(
request, SaTokenUtil.getLoginAccount()));
}
/**
* 浏览活动数据源候选配置可访问的命名空间。
*
* @param request 候选连接配置
* @return 命名空间列表
*/
@PostMapping("/candidate/catalogs")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<List<DatacenterCatalogMeta>> candidateCatalogs(
@RequestBody DatacenterSourceDraftRequest request) {
return Result.ok(sourceService.listCandidateCatalogs(
request, SaTokenUtil.getLoginAccount()));
}
/**
* 分页浏览候选配置可访问的命名空间。
*
* @param request 候选配置和分页条件
* @return 有界命名空间列表
*/
@PostMapping("/candidate/catalogs/page")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterMetadataPage<DatacenterCatalogMeta>> candidateCatalogsPage(
@RequestBody DatacenterSourceCandidateCatalogRequest request) {
return Result.ok(sourceService.listCandidateCatalogsPage(
request, SaTokenUtil.getLoginAccount()));
}
/**
* 分页浏览活动数据源候选配置可访问的表。
*
* @param request 候选配置与分页条件
* @return 有界表列表
*/
@PostMapping("/candidate/tables")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterMetadataPage<DatacenterTable>> candidateTables(
@RequestBody DatacenterSourceCandidateMetadataRequest request) {
return Result.ok(sourceService.listCandidateTables(
request, SaTokenUtil.getLoginAccount()));
}
/**
* 原地发布活动数据源的新连接配置和纳管范围。
*
* @param request 重配置请求
* @return 发布后的数据源视图
*/
@PostMapping("/reconfigure")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> reconfigure(
@RequestBody DatacenterSourceReconfigureRequest request) {
return Result.ok(sourceService.reconfigure(
request, SaTokenUtil.getLoginAccount()));
}
@GetMapping("/page")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<Page<DatacenterSource>> page(Long pageNumber, Long pageSize) {
public Result<Page<DatacenterSourceView>> page(Long pageNumber, Long pageSize) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.pageSources(pageNumber, pageSize, account));
}
@@ -54,19 +140,53 @@ public class DatacenterSourceController {
return Result.ok(sourceService.listCatalogs(sourceId, account));
}
/**
* 分页浏览当前数据源的命名空间。
*
* @param sourceId 数据源 ID
* @param keyword 名称搜索词
* @param pageNumber 页码
* @param pageSize 每页大小
* @return 有界命名空间列表
*/
@GetMapping("/catalogs/page")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterMetadataPage<DatacenterCatalogMeta>> catalogsPage(
BigInteger sourceId,
String keyword,
Long pageNumber,
Long pageSize) {
return Result.ok(sourceService.listCatalogsPage(
sourceId, keyword, pageNumber, pageSize,
SaTokenUtil.getLoginAccount()));
}
@GetMapping("/tables")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<List<DatacenterTable>> tables(BigInteger sourceId, String catalogName) {
public Result<DatacenterMetadataPage<DatacenterTable>> tables(
BigInteger sourceId,
String catalogName,
String keyword,
Long pageNumber,
Long pageSize) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.listTables(sourceId, catalogName, account));
return Result.ok(sourceService.listTables(
sourceId, catalogName, keyword, pageNumber, pageSize, account));
}
@GetMapping("/tableDetail")
@SaCheckPermission("/api/v1/datacenterSource/query")
public Result<DatacenterTableDetailMeta> tableDetail(BigInteger sourceId, String catalogName, String tableName,
@RequestParam(defaultValue = "false") boolean register) {
public Result<DatacenterTableDetailMeta> tableDetail(
BigInteger sourceId,
String catalogName,
String tableName,
@RequestParam(defaultValue = "false") boolean register,
Long fieldPageNumber,
Long fieldPageSize) {
LoginAccount account = SaTokenUtil.getLoginAccount();
return Result.ok(sourceService.getTableDetail(sourceId, catalogName, tableName, register, account));
return Result.ok(sourceService.getTableDetail(
sourceId, catalogName, tableName, register,
fieldPageNumber, fieldPageSize, account));
}
@PostMapping("/registerBatch")
@@ -83,4 +203,44 @@ public class DatacenterSourceController {
sourceService.removeSource(request == null ? null : request.getSourceId(), account);
return Result.ok();
}
/**
* 停用活动数据源。
*
* @param sourceId 数据源 ID
* @return 停用后的数据源视图
*/
@PostMapping("/{sourceId}/disable")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> disable(@PathVariable BigInteger sourceId) {
return Result.ok(sourceService.disable(
sourceId, SaTokenUtil.getLoginAccount()));
}
/**
* 重新启用已停用数据源。
*
* @param sourceId 数据源 ID
* @return 启用后的数据源视图
*/
@PostMapping("/{sourceId}/enable")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> enable(@PathVariable BigInteger sourceId) {
return Result.ok(sourceService.enable(
sourceId, SaTokenUtil.getLoginAccount()));
}
/**
* 刷新已纳管对象的 JDBC 元数据观测状态。
*
* @param sourceId 数据源 ID
* @return 刷新后的数据源视图
*/
@PostMapping("/{sourceId}/metadata/refresh")
@SaCheckPermission("/api/v1/datacenterSource/save")
public Result<DatacenterSourceView> refreshMetadata(
@PathVariable BigInteger sourceId) {
return Result.ok(sourceService.refreshMetadata(
sourceId, SaTokenUtil.getLoginAccount()));
}
}

View File

@@ -126,6 +126,7 @@ public record WorkflowDesignerOptionsView(
* 已接入数据集安全选项。
*
* @param id 数据集 ID
* @param tenantId 租户 ID
* @param sourceId 数据源 ID
* @param catalogId 目录 ID
* @param tableName 数据表名称
@@ -133,6 +134,7 @@ public record WorkflowDesignerOptionsView(
*/
public record DatasetOption(
@JsonSerialize(using = ToStringSerializer.class) BigInteger id,
@JsonSerialize(using = ToStringSerializer.class) BigInteger tenantId,
@JsonSerialize(using = ToStringSerializer.class) BigInteger sourceId,
@JsonSerialize(using = ToStringSerializer.class) BigInteger catalogId,
String tableName,

View File

@@ -653,6 +653,7 @@ public class WorkflowDesignerOptionService {
private WorkflowDesignerOptionsView.DatasetOption toDatasetOption(DatacenterTable table) {
return new WorkflowDesignerOptionsView.DatasetOption(
table.getId(),
table.getTenantId(),
table.getSourceId(),
table.getCatalogId(),
table.getTableName(),