diff --git a/frontend/src/domain/dataSource/DataSourceRegistry.ts b/frontend/src/domain/dataSource/DataSourceRegistry.ts new file mode 100644 index 00000000..114d9204 --- /dev/null +++ b/frontend/src/domain/dataSource/DataSourceRegistry.ts @@ -0,0 +1,53 @@ +/** + * 数据源注册表 + * 集中管理所有预设数据源配置 + */ +import { DataSourceType, type DataSourceRegistry } from './types'; +import { jenkinsServersConfig } from './presets/jenkins'; +import { k8sClustersConfig } from './presets/k8s'; +import { gitRepositoriesConfig } from './presets/git'; +import { dockerRegistriesConfig } from './presets/docker'; +import { notificationChannelTypesConfig, notificationChannelsConfig } from './presets/notification'; +import { usersConfig, rolesConfig, departmentsConfig } from './presets/user'; + +/** + * 数据源配置注册表 + */ +export const DATA_SOURCE_REGISTRY: DataSourceRegistry = { + [DataSourceType.JENKINS_SERVERS]: jenkinsServersConfig, + [DataSourceType.K8S_CLUSTERS]: k8sClustersConfig, + [DataSourceType.GIT_REPOSITORIES]: gitRepositoriesConfig, + [DataSourceType.DOCKER_REGISTRIES]: dockerRegistriesConfig, + [DataSourceType.NOTIFICATION_CHANNEL_TYPES]: notificationChannelTypesConfig, + [DataSourceType.NOTIFICATION_CHANNELS]: notificationChannelsConfig, + [DataSourceType.USERS]: usersConfig, + [DataSourceType.ROLES]: rolesConfig, + [DataSourceType.DEPARTMENTS]: departmentsConfig +}; + +/** + * 获取数据源配置 + * @param type 数据源类型 + * @returns 数据源配置,如果不存在则返回 undefined + */ +export const getDataSourceConfig = (type: DataSourceType) => { + return DATA_SOURCE_REGISTRY[type]; +}; + +/** + * 检查数据源类型是否已注册 + * @param type 数据源类型 + * @returns 是否已注册 + */ +export const hasDataSource = (type: DataSourceType): boolean => { + return type in DATA_SOURCE_REGISTRY; +}; + +/** + * 获取所有已注册的数据源类型 + * @returns 数据源类型列表 + */ +export const getAllDataSourceTypes = (): DataSourceType[] => { + return Object.keys(DATA_SOURCE_REGISTRY) as DataSourceType[]; +}; + diff --git a/frontend/src/domain/dataSource/DataSourceService.ts b/frontend/src/domain/dataSource/DataSourceService.ts new file mode 100644 index 00000000..89c7e34e --- /dev/null +++ b/frontend/src/domain/dataSource/DataSourceService.ts @@ -0,0 +1,74 @@ +/** + * 数据源服务 + * 提供数据源加载和管理功能 + */ +import request from '@/utils/request'; +import { DataSourceType, type DataSourceOption } from './types'; +import { getDataSourceConfig } from './DataSourceRegistry'; + +/** + * 数据源服务类 + */ +class DataSourceService { + /** + * 加载单个数据源 + * @param type 数据源类型 + * @returns 选项列表 + */ + async load(type: DataSourceType): Promise { + const config = getDataSourceConfig(type); + + if (!config) { + console.error(`❌ 数据源类型 ${type} 未配置`); + return []; + } + + try { + const response = await request.get(config.url, { params: config.params }); + // request 拦截器已经提取了 data 字段,response 直接是数组 + return config.transform(response || []); + } catch (error) { + console.error(`❌ 加载数据源 ${type} 失败:`, error); + return []; + } + } + + /** + * 批量加载多个数据源(用于预加载) + * @param types 数据源类型列表 + * @returns 数据源数据映射 + */ + async loadMultiple( + types: DataSourceType[] + ): Promise> { + const results = await Promise.all( + types.map(type => this.load(type)) + ); + + return types.reduce((acc, type, index) => { + acc[type] = results[index]; + return acc; + }, {} as Record); + } + + /** + * 预加载常用数据源(提升用户体验) + * @returns 预加载的数据源映射 + */ + async preload(): Promise> { + const commonTypes: DataSourceType[] = [ + DataSourceType.JENKINS_SERVERS, + DataSourceType.K8S_CLUSTERS, + DataSourceType.USERS + ]; + return this.loadMultiple(commonTypes); + } +} + +// 导出单例 +export const dataSourceService = new DataSourceService(); + +// 向后兼容:导出原有的函数式API +export const loadDataSource = (type: DataSourceType) => dataSourceService.load(type); +export const loadMultipleDataSources = (types: DataSourceType[]) => dataSourceService.loadMultiple(types); + diff --git a/frontend/src/domain/dataSource/index.ts b/frontend/src/domain/dataSource/index.ts new file mode 100644 index 00000000..106843a4 --- /dev/null +++ b/frontend/src/domain/dataSource/index.ts @@ -0,0 +1,15 @@ +/** + * 数据源领域模块 + * 统一导出所有公共接口 + */ + +// 类型定义 +export { DataSourceType } from './types'; +export type { DataSourceOption, DataSourceConfig, DataSourceRegistry } from './types'; + +// 注册表 +export { DATA_SOURCE_REGISTRY, getDataSourceConfig, hasDataSource, getAllDataSourceTypes } from './DataSourceRegistry'; + +// 服务 +export { dataSourceService, loadDataSource, loadMultipleDataSources } from './DataSourceService'; + diff --git a/frontend/src/domain/dataSource/presets/docker.ts b/frontend/src/domain/dataSource/presets/docker.ts new file mode 100644 index 00000000..3fde4a4d --- /dev/null +++ b/frontend/src/domain/dataSource/presets/docker.ts @@ -0,0 +1,17 @@ +/** + * Docker 镜像仓库数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const dockerRegistriesConfig: DataSourceConfig = { + url: '/api/v1/docker-registry/list', + params: { enabled: true }, + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: item.name, + value: item.id, + url: item.url + })); + } +}; + diff --git a/frontend/src/domain/dataSource/presets/git.ts b/frontend/src/domain/dataSource/presets/git.ts new file mode 100644 index 00000000..1385bbf3 --- /dev/null +++ b/frontend/src/domain/dataSource/presets/git.ts @@ -0,0 +1,17 @@ +/** + * Git 仓库数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const gitRepositoriesConfig: DataSourceConfig = { + url: '/api/v1/git-repo/list', + params: { enabled: true }, + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.name} (${item.url})`, + value: item.id, + url: item.url + })); + } +}; + diff --git a/frontend/src/domain/dataSource/presets/jenkins.ts b/frontend/src/domain/dataSource/presets/jenkins.ts new file mode 100644 index 00000000..212c6db2 --- /dev/null +++ b/frontend/src/domain/dataSource/presets/jenkins.ts @@ -0,0 +1,18 @@ +/** + * Jenkins 服务器数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const jenkinsServersConfig: DataSourceConfig = { + url: '/api/v1/external-system/list', + params: { type: 'JENKINS', enabled: true }, + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.name} (${item.url})`, + value: item.id, + url: item.url, + name: item.name + })); + } +}; + diff --git a/frontend/src/domain/dataSource/presets/k8s.ts b/frontend/src/domain/dataSource/presets/k8s.ts new file mode 100644 index 00000000..20d3d58e --- /dev/null +++ b/frontend/src/domain/dataSource/presets/k8s.ts @@ -0,0 +1,17 @@ +/** + * Kubernetes 集群数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const k8sClustersConfig: DataSourceConfig = { + url: '/api/v1/k8s-cluster/list', + params: { enabled: true }, + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: item.name, + value: item.id, + apiServer: item.apiServer + })); + } +}; + diff --git a/frontend/src/domain/dataSource/presets/notification.ts b/frontend/src/domain/dataSource/presets/notification.ts new file mode 100644 index 00000000..4b46584c --- /dev/null +++ b/frontend/src/domain/dataSource/presets/notification.ts @@ -0,0 +1,25 @@ +/** + * 通知渠道数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const notificationChannelTypesConfig: DataSourceConfig = { + url: '/api/v1/notification-channel/types', + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.label}`, + value: item.code + })); + } +}; + +export const notificationChannelsConfig: DataSourceConfig = { + url: '/api/v1/notification-channel/list', + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `(${item.channelType})-${item.name}`, + value: item.id + })); + } +}; + diff --git a/frontend/src/domain/dataSource/presets/user.ts b/frontend/src/domain/dataSource/presets/user.ts new file mode 100644 index 00000000..edf37e31 --- /dev/null +++ b/frontend/src/domain/dataSource/presets/user.ts @@ -0,0 +1,43 @@ +/** + * 用户、角色、部门数据源 + */ +import type { DataSourceConfig } from '../types'; + +export const usersConfig: DataSourceConfig = { + url: '/api/v1/user/list', + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.nickname} (${item.username})`, + value: item.username, // 后台使用 username 进行审批 + id: item.id, + email: item.email, + departmentName: item.departmentName + })); + } +}; + +export const rolesConfig: DataSourceConfig = { + url: '/api/v1/role/list', + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.name} (${item.code})`, + value: item.code, // 使用 code 作为值 + id: item.id, + description: item.description + })); + } +}; + +export const departmentsConfig: DataSourceConfig = { + url: '/api/v1/department/list', + transform: (data: any[]) => { + return data.map((item: any) => ({ + label: `${item.name} (${item.code})`, + value: item.code, // 使用 code 作为值 + id: item.id, + description: item.description, + parentId: item.parentId + })); + } +}; + diff --git a/frontend/src/domain/dataSource/types.ts b/frontend/src/domain/dataSource/types.ts new file mode 100644 index 00000000..530eca62 --- /dev/null +++ b/frontend/src/domain/dataSource/types.ts @@ -0,0 +1,42 @@ +/** + * 数据源领域 - 类型定义 + */ + +/** + * 数据源类型枚举 + */ +export enum DataSourceType { + JENKINS_SERVERS = 'JENKINS_SERVERS', + K8S_CLUSTERS = 'K8S_CLUSTERS', + GIT_REPOSITORIES = 'GIT_REPOSITORIES', + DOCKER_REGISTRIES = 'DOCKER_REGISTRIES', + NOTIFICATION_CHANNEL_TYPES = 'NOTIFICATION_CHANNEL_TYPES', + NOTIFICATION_CHANNELS = 'NOTIFICATION_CHANNELS', + USERS = 'USERS', + ROLES = 'ROLES', + DEPARTMENTS = 'DEPARTMENTS' +} + +/** + * 数据源选项接口 + */ +export interface DataSourceOption { + label: string; + value: any; + [key: string]: any; +} + +/** + * 数据源配置接口 + */ +export interface DataSourceConfig { + url: string; + params?: Record; + transform: (data: any) => DataSourceOption[]; +} + +/** + * 数据源注册表类型 + */ +export type DataSourceRegistry = Record; + diff --git a/frontend/src/pages/FormDesigner/components/PropertyPanel.tsx b/frontend/src/pages/FormDesigner/components/PropertyPanel.tsx index d4ccafe8..040d9d02 100644 --- a/frontend/src/pages/FormDesigner/components/PropertyPanel.tsx +++ b/frontend/src/pages/FormDesigner/components/PropertyPanel.tsx @@ -19,7 +19,7 @@ import { } from 'antd'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; import type { FieldConfig, FormConfig } from '../types'; -import { DataSourceType } from '@/pages/Workflow/Design/utils/dataSourceLoader'; +import { DataSourceType } from '@/domain/dataSource'; const { Text } = Typography; const { TabPane } = Tabs; diff --git a/frontend/src/pages/FormDesigner/hooks/useFieldOptions.ts b/frontend/src/pages/FormDesigner/hooks/useFieldOptions.ts index 9acf9f43..10b19cf2 100644 --- a/frontend/src/pages/FormDesigner/hooks/useFieldOptions.ts +++ b/frontend/src/pages/FormDesigner/hooks/useFieldOptions.ts @@ -6,7 +6,7 @@ import { useState, useEffect } from 'react'; import type { FieldConfig, FieldOption } from '../types'; import request from '../../../utils/request'; -import { loadDataSource, DataSourceType as WorkflowDataSourceType } from '@/pages/Workflow/Design/utils/dataSourceLoader'; +import { loadDataSource, DataSourceType as WorkflowDataSourceType } from '@/domain/dataSource'; /** * 从对象中根据路径提取数据 diff --git a/frontend/src/pages/Workflow/Design/components/NodeConfigModal.tsx b/frontend/src/pages/Workflow/Design/components/NodeConfigModal.tsx index 620cdcf7..6041662b 100644 --- a/frontend/src/pages/Workflow/Design/components/NodeConfigModal.tsx +++ b/frontend/src/pages/Workflow/Design/components/NodeConfigModal.tsx @@ -19,7 +19,7 @@ import type { FlowNode, FlowNodeData, FlowEdge } from '../types'; import type { WorkflowNodeDefinition, JSONSchema } from '../nodes/types'; import { isConfigurableNode } from '../nodes/types'; import { convertJsonSchemaToZod, extractDataSourceTypes } from '../utils/schemaConverter'; -import { loadDataSource, DataSourceType, type DataSourceOption } from '../utils/dataSourceLoader'; +import { loadDataSource, DataSourceType, type DataSourceOption } from '@/domain/dataSource'; import { convertObjectToUUID, convertObjectToDisplayName } from '@/utils/workflow/variableConversion'; import VariableInput from '@/components/VariableInput'; diff --git a/frontend/src/pages/Workflow/Design/nodes/ApprovalNode.tsx b/frontend/src/pages/Workflow/Design/nodes/ApprovalNode.tsx index 037933f0..0f0ac87a 100644 --- a/frontend/src/pages/Workflow/Design/nodes/ApprovalNode.tsx +++ b/frontend/src/pages/Workflow/Design/nodes/ApprovalNode.tsx @@ -1,5 +1,5 @@ import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types'; -import {DataSourceType} from '../utils/dataSourceLoader'; +import { DataSourceType } from '@/domain/dataSource'; /** * 审批节点定义 diff --git a/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx b/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx index 805d21dd..bbb8eb29 100644 --- a/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx +++ b/frontend/src/pages/Workflow/Design/nodes/JenkinsBuildNode.tsx @@ -1,5 +1,5 @@ import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types'; -import {DataSourceType} from '../utils/dataSourceLoader'; +import { DataSourceType } from '@/domain/dataSource'; /** * Jenkins构建节点定义(纯配置) diff --git a/frontend/src/pages/Workflow/Design/nodes/NotificationNode.tsx b/frontend/src/pages/Workflow/Design/nodes/NotificationNode.tsx index 3fbc0060..47534f23 100644 --- a/frontend/src/pages/Workflow/Design/nodes/NotificationNode.tsx +++ b/frontend/src/pages/Workflow/Design/nodes/NotificationNode.tsx @@ -1,5 +1,5 @@ import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types'; -import {DataSourceType} from "@/pages/Workflow/Design/utils/dataSourceLoader.ts"; +import { DataSourceType } from "@/domain/dataSource"; /** * 通知节点定义 diff --git a/frontend/src/pages/Workflow/Design/utils/dataSourceLoader.ts b/frontend/src/pages/Workflow/Design/utils/dataSourceLoader.ts deleted file mode 100644 index 101776cc..00000000 --- a/frontend/src/pages/Workflow/Design/utils/dataSourceLoader.ts +++ /dev/null @@ -1,181 +0,0 @@ -import request from '@/utils/request'; - -/** - * 数据源类型枚举 - */ -export enum DataSourceType { - JENKINS_SERVERS = 'JENKINS_SERVERS', - K8S_CLUSTERS = 'K8S_CLUSTERS', - GIT_REPOSITORIES = 'GIT_REPOSITORIES', - DOCKER_REGISTRIES = 'DOCKER_REGISTRIES', - NOTIFICATION_CHANNEL_TYPES = 'NOTIFICATION_CHANNEL_TYPES', - NOTIFICATION_CHANNELS = 'NOTIFICATION_CHANNELS', - USERS = 'USERS', - ROLES = 'ROLES', - DEPARTMENTS = 'DEPARTMENTS' -} - -/** - * 数据源配置接口 - */ -export interface DataSourceConfig { - url: string; - params?: Record; - transform: (data: any) => Array<{ label: string; value: any; [key: string]: any }>; -} - -/** - * 数据源选项接口 - */ -export interface DataSourceOption { - label: string; - value: any; - - [key: string]: any; -} - -/** - * 数据源配置注册表 - */ -export const DATA_SOURCE_REGISTRY: Record = { - [DataSourceType.JENKINS_SERVERS]: { - url: '/api/v1/external-system/list', - params: {type: 'JENKINS', enabled: true}, - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.name} (${item.url})`, - value: item.id, - url: item.url, - name: item.name - })); - } - }, - [DataSourceType.NOTIFICATION_CHANNEL_TYPES]: { - url: '/api/v1/notification-channel/types', - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.label}`, - value: item.code - })); - } - }, - [DataSourceType.NOTIFICATION_CHANNELS]: { - url: '/api/v1/notification-channel/list', - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `(${item.channelType})-${item.name}`, - value: item.id - })); - } - }, - [DataSourceType.K8S_CLUSTERS]: { - url: '/api/v1/k8s-cluster/list', - params: {enabled: true}, - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: item.name, - value: item.id, - apiServer: item.apiServer - })); - } - }, - [DataSourceType.GIT_REPOSITORIES]: { - url: '/api/v1/git-repo/list', - params: {enabled: true}, - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.name} (${item.url})`, - value: item.id, - url: item.url - })); - } - }, - [DataSourceType.DOCKER_REGISTRIES]: { - url: '/api/v1/docker-registry/list', - params: {enabled: true}, - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: item.name, - value: item.id, - url: item.url - })); - } - }, - [DataSourceType.USERS]: { - url: '/api/v1/user/list', - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.nickname} (${item.username})`, - value: item.username, // 后台使用 username 进行审批 - id: item.id, - email: item.email, - departmentName: item.departmentName - })); - } - }, - [DataSourceType.ROLES]: { - url: '/api/v1/role/list', - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.name} (${item.code})`, - value: item.code, // 使用 code 作为值 - id: item.id, - description: item.description - })); - } - }, - [DataSourceType.DEPARTMENTS]: { - url: '/api/v1/department/list', - transform: (data: any[]) => { - return data.map((item: any) => ({ - label: `${item.name} (${item.code})`, - value: item.code, // 使用 code 作为值 - id: item.id, - description: item.description, - parentId: item.parentId - })); - } - } -}; - -/** - * 加载数据源 - * @param type 数据源类型 - * @returns 选项列表 - */ -export const loadDataSource = async (type: DataSourceType): Promise => { - const config = DATA_SOURCE_REGISTRY[type]; - - if (!config) { - console.error(`数据源类型 ${type} 未配置`); - return []; - } - - try { - const response = await request.get(config.url, {params: config.params}); - // request 拦截器已经提取了 data 字段,response 直接是数组 - return config.transform(response || []); - } catch (error) { - console.error(`加载数据源 ${type} 失败:`, error); - return []; - } -}; - -/** - * 批量加载多个数据源(用于预加载) - * @param types 数据源类型列表 - * @returns 数据源数据映射 - */ -export const loadMultipleDataSources = async ( - types: DataSourceType[] -): Promise> => { - const results = await Promise.all( - types.map(type => loadDataSource(type)) - ); - - return types.reduce((acc, type, index) => { - acc[type] = results[index]; - return acc; - }, {} as Record); -}; -