From a09c0fa9ef9795809a28a1dc64a8e048c7fe56f8 Mon Sep 17 00:00:00 2001 From: newbyvector Date: Thu, 19 Dec 2024 17:45:49 +0800 Subject: [PATCH] repo-sync-2024-12-19T17:45:25+0800 --- apps/platform/package.json | 1 + apps/platform/src/assets/format.svg | 7 + .../platform/src/assets/left-panel-spread.svg | 6 +- .../component-config-protocol.ts | 38 +- .../component-config/config-form-view.tsx | 5 + .../config-render-contribution.ts | 66 + .../config-render-protocol.ts | 1 + .../binning-modification/drawer/index.tsx | 6 +- .../drawer/index.tsx | 6 +- .../code-editor/index.less | 83 + .../scql-editor-content/code-editor/index.tsx | 334 +++ .../scql-editor-content/index.less | 5 + .../scql-editor/scql-editor-content/index.tsx | 82 + .../upstream-feature-render/index.less | 47 + .../upstream-feature-render/index.tsx | 66 + .../output-feature-show.tsx | 198 ++ .../custom-serializer-registry.ts | 8 + .../default-col-selection-template.tsx | 67 +- .../multi-field-select-modal.tsx | 18 +- .../default-feature-selection/select-tree.tsx | 3 +- .../default-join-node-selection-template.tsx | 116 ++ .../default-node-selection-template.tsx | 2 +- .../config-item-render/default-sql-editor.tsx | 2 + .../node-parties-by-upstream-output.tsx | 132 ++ .../query-col-selection.tsx | 265 +++ .../result-receive-select.tsx | 168 ++ .../add-cooperative-node-modal.tsx | 4 +- .../src/modules/dag-log/log.drawer.layout.tsx | 18 +- .../dag-record/graph-request-service.ts | 25 +- .../src/modules/dag-result/index.less | 7 + .../src/modules/dag-result/result-modal.tsx | 12 +- .../src/modules/dag-result/result-report.tsx | 38 +- .../dag-result/vis/output-table/index.tsx | 7 +- .../src/modules/dag-result/vis/utils.ts | 12 +- .../dag-submit/graph-request-service.ts | 14 + .../data-manager/data-manager.view.tsx | 4 +- .../components/create-data-source/index.tsx | 9 +- .../data-table-add/add-data/add-data.view.tsx | 4 +- .../data-table-structure.view.tsx | 21 +- .../dataTableStructure/sql-keyword.ts | 1786 +++++++++++++++++ .../upload-table/upload-table.view.tsx | 20 +- .../project-auth-config/index.tsx | 7 + .../modules/guide-pipeline/concept-info.tsx | 2 +- .../main-dag/graph-request-service.tsx | 32 +- .../src/modules/main-dag/graph-service.ts | 20 +- .../pipeline/pipeline-creation-view.tsx | 2 +- .../modules/pipeline/pipeline-protocol.tsx | 4 +- .../src/modules/pipeline/pipeline-view.tsx | 6 +- .../templates/pipeline-template-psi-guide.ts | 29 +- .../templates/pipeline-template-psi.ts | 29 +- .../templates/pipeline-template-risk-guide.ts | 31 +- .../templates/pipeline-template-risk.ts | 31 +- .../templates/pipeline-template-tee-guide.ts | 2 +- .../templates/pipeline-template-tee.ts | 2 +- .../src/services/secretpad/typings.d.ts | 2 + packages/dag/src/actions/add-node.ts | 1 + packages/dag/src/actions/change-status.ts | 2 +- packages/dag/src/actions/drag-node.ts | 5 +- packages/dag/src/actions/query-status.ts | 6 +- packages/dag/src/actions/render.ts | 1 + packages/dag/src/request/protocol.ts | 2 +- packages/dag/src/shapes/descriptions.tsx | 14 +- packages/dag/src/shapes/index.less | 1 + packages/dag/src/shapes/node.tsx | 30 +- packages/dag/src/types/index.ts | 1 + pnpm-lock.yaml | 48 +- 66 files changed, 3882 insertions(+), 141 deletions(-) create mode 100644 apps/platform/src/assets/format.svg create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.less create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.less create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.less create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/output-feature-show.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/default-join-node-selection-template.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/node-parties-by-upstream-output.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/unbalance-psi-custom/query-col-selection.tsx create mode 100644 apps/platform/src/modules/component-config/config-item-render/unbalance-psi-custom/result-receive-select.tsx create mode 100644 apps/platform/src/modules/data-table-add/component/dataTableStructure/sql-keyword.ts diff --git a/apps/platform/package.json b/apps/platform/package.json index 52bab32..928aa6d 100644 --- a/apps/platform/package.json +++ b/apps/platform/package.json @@ -38,6 +38,7 @@ "query-string": "^6.14.1", "react-csv": "^2.2.2", "react-syntax-highlighter": "^15.5.0", + "sql-formatter": "^15.4.2", "umi": "^4.0.64", "uuid": "^10.0.0", "valtio": "^1.10.7" diff --git a/apps/platform/src/assets/format.svg b/apps/platform/src/assets/format.svg new file mode 100644 index 0000000..166957c --- /dev/null +++ b/apps/platform/src/assets/format.svg @@ -0,0 +1,7 @@ + + + 7 ICON/1 Line/save + + + + \ No newline at end of file diff --git a/apps/platform/src/assets/left-panel-spread.svg b/apps/platform/src/assets/left-panel-spread.svg index 0749cb6..a51ab59 100644 --- a/apps/platform/src/assets/left-panel-spread.svg +++ b/apps/platform/src/assets/left-panel-spread.svg @@ -7,14 +7,14 @@ - + - + - \ No newline at end of file + diff --git a/apps/platform/src/modules/component-config/component-config-protocol.ts b/apps/platform/src/modules/component-config/component-config-protocol.ts index 7105719..94e103b 100644 --- a/apps/platform/src/modules/component-config/component-config-protocol.ts +++ b/apps/platform/src/modules/component-config/component-config-protocol.ts @@ -67,7 +67,12 @@ export const codeNameRenderKey = { 'preprocessing/psi': 'UNION_KEY_SELECT', 'preprocessing/sqlite': 'SQL', 'data_filter/sample': 'SAMPLE', - 'ml.predict/read_model': 'MODEL_SELECT', // TODO:修改名称 + 'ml.predict/read_model': 'MODEL_SELECT', + 'stats/scql_analysis': 'SQL_ANALYSIS', + 'data_prep/unbalance_psi': 'UNBALANCE_PSI', + 'data_prep/unbalance_psi_cache': 'UNBALANCE_PSI_CACHE', + 'data_prep/psi_tp': 'UNION_KEY_SELECT', + 'preprocessing/sql_processor': 'SQL_PROCESSOR', }; export interface ComponentConfig { @@ -141,7 +146,36 @@ export const getUpstreamKey = { return attrs[0]?.s; }); }, - + 'data_prep/psi_tp': ( + upstreamNodes: GraphNodeDetail[], + graphNode?: GraphNodeDetail, + ) => { + const { inputs = [] } = graphNode || {}; + return upstreamNodes.map((n, index) => { + const { codeName, nodeDef } = n || {}; + if (!['read_data/datatable'].includes(codeName)) { + return inputs[index]; + } + const { attrs } = nodeDef; + if (!attrs) return inputs[index]; + return attrs[0]?.s; + }); + }, + 'data_prep/unbalance_psi': ( + upstreamNodes: GraphNodeDetail[], + graphNode?: GraphNodeDetail, + ) => { + const { inputs = [] } = graphNode || {}; + return upstreamNodes.map((n, index) => { + const { codeName, nodeDef } = n || {}; + if (!['read_data/datatable'].includes(codeName)) { + return inputs[index]; + } + const { attrs } = nodeDef; + if (!attrs) return inputs[index]; + return attrs[0]?.s; + }); + }, 'preprocessing/psi': ( upstreamNodes: GraphNodeDetail[], graphNode?: GraphNodeDetail, diff --git a/apps/platform/src/modules/component-config/config-form-view.tsx b/apps/platform/src/modules/component-config/config-form-view.tsx index f26b663..7eb9b27 100644 --- a/apps/platform/src/modules/component-config/config-form-view.tsx +++ b/apps/platform/src/modules/component-config/config-form-view.tsx @@ -509,6 +509,7 @@ export const ConfigFormComponent: React.FC = (prop) => { attrConfig={attrConfig} translation={translation} disabled={!isEditable} + onSaveConfig={onFormFinished} /> ); })} @@ -553,6 +554,7 @@ export const ConfigFormComponent: React.FC = (prop) => { translation={translation} disabled={!isEditable} attrConfig={attrConfig} + onSaveConfig={onFormFinished} /> ); })} @@ -585,6 +587,7 @@ export const ConfigurationNodeRender = ({ disabled = false, componentConfig, attrConfig, + onSaveConfig, }: { form: FormInstance; config: ConfigItem; @@ -596,6 +599,7 @@ export const ConfigurationNodeRender = ({ style?: Record; componentConfig: ConfigItem[]; attrConfig?: AttrConfig; + onSaveConfig?: (val: Record | undefined>) => void; }) => { const configRenderRegistry = useModel(ConfigRenderRegistry); const Render = configRenderRegistry.getRender(config, exif); @@ -623,6 +627,7 @@ export const ConfigurationNodeRender = ({ translation={translation} disabled={disabled} attrConfig={attrConfig} + onSaveConfig={onSaveConfig} /> ) : ( diff --git a/apps/platform/src/modules/component-config/config-item-render/config-render-contribution.ts b/apps/platform/src/modules/component-config/config-item-render/config-render-contribution.ts index 54c230c..5dd5827 100644 --- a/apps/platform/src/modules/component-config/config-item-render/config-render-contribution.ts +++ b/apps/platform/src/modules/component-config/config-item-render/config-render-contribution.ts @@ -7,8 +7,11 @@ import { CaseWhenRender } from './custom-render/case-when-render'; import { GroupByRender } from './custom-render/groupby-render'; import { LinearModelParametersModificationRender } from './custom-render/linear-model-parameters-modification'; import ObservationsQuantilesRender from './custom-render/observations-quantiles-render'; +import { AnalyzeSQLEditor } from './custom-render/scql-editor/scql-editor-content'; +import { UpstreamOutputFeatureRender } from './custom-render/upstream-feature-render'; import { DefaultColSelection } from './default-col-selection-template'; import { DefaultMultiTableFeatureSelection } from './default-feature-selection/default-feature-selection'; +import { DefaultJoinNodeSelect } from './default-join-node-selection-template'; import { DefaultModelSelect } from './default-model-selection-template'; import { DefaultNodeSelect } from './default-node-selection-template'; import { @@ -22,6 +25,9 @@ import { } from './default-render-template'; import { DefaultSQLEditor } from './default-sql-editor'; import { DefaultTableSelect } from './default-table-selection-temple'; +import { nodePartiesByUpstreamOutputSelect } from './node-parties-by-upstream-output'; +import { UnbalancePsiColSelect } from './unbalance-psi-custom/query-col-selection'; +import { UnbalancePsiCustomSelect } from './unbalance-psi-custom/result-receive-select'; export class DefaultConfigRender implements ConfigRenderProtocol { registerConfigRenders() { @@ -54,6 +60,14 @@ export class DefaultConfigRender implements ConfigRenderProtocol { : false, component: LinearModelParametersModificationRender, }, + { + canHandle: (node: CustomConfigNode) => + node.type === 'AT_CUSTOM_PROTOBUF' && + node.custom_protobuf_cls === 'feature_column_config_pb2.FeatureColumnConfig' + ? 1 + : false, + component: UpstreamOutputFeatureRender, + }, { canHandle: (node: CustomConfigNode) => node.type === 'AT_CUSTOM_PROTOBUF' && @@ -79,6 +93,24 @@ export class DefaultConfigRender implements ConfigRenderProtocol { : false, component: GroupByRender, }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => + node.type === 'AT_STRING' && + node.name === 'script_input' && + renderKey === 'SQL_ANALYSIS' + ? 1 + : false, + component: AnalyzeSQLEditor, + }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => + node.type === 'AT_PARTY' && + node.name === 'task_initiator' && + renderKey === 'SQL_ANALYSIS' + ? 1 + : false, + component: nodePartiesByUpstreamOutputSelect, + }, { canHandle: (node: AtomicConfigNode, renderKey?: string) => { return renderKey === 'UNION_KEY_SELECT' && node.type === 'AT_SF_TABLE_COL' @@ -87,6 +119,31 @@ export class DefaultConfigRender implements ConfigRenderProtocol { }, component: DefaultColSelection, }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => { + // 非平衡 PSI 求交键 + return renderKey === 'UNBALANCE_PSI' && node.type === 'AT_SF_TABLE_COL' + ? 1 + : false; + }, + component: UnbalancePsiColSelect, + }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => { + // 非平衡 PSI 结果接收方 + return renderKey === 'UNBALANCE_PSI' && node.type === 'AT_PARTY' ? 1 : false; + }, + component: UnbalancePsiCustomSelect, + }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => { + // 非平衡 PSI 密文缓存 + return node.type === 'AT_PARTY' && renderKey === 'UNBALANCE_PSI_CACHE' + ? 1 + : false; + }, + component: DefaultJoinNodeSelect, + }, { canHandle: (node: AtomicConfigNode) => { return node.name === 'receiver' ? 3 : false; @@ -116,6 +173,15 @@ export class DefaultConfigRender implements ConfigRenderProtocol { node.type === 'AT_SF_TABLE_COL' ? 1 : false, component: DefaultMultiTableFeatureSelection, }, + { + canHandle: (node: AtomicConfigNode, renderKey?: string) => + node.type === 'AT_STRING' && + node.name === 'sql' && + renderKey === 'SQL_PROCESSOR' + ? 1 + : false, + component: DefaultSQLEditor, + }, { canHandle: (node: AtomicConfigNode, renderKey?: string) => node.type === 'AT_STRING' && node.name === 'sql' && renderKey === 'SQL' diff --git a/apps/platform/src/modules/component-config/config-item-render/config-render-protocol.ts b/apps/platform/src/modules/component-config/config-item-render/config-render-protocol.ts index db32c5a..780eabd 100644 --- a/apps/platform/src/modules/component-config/config-item-render/config-render-protocol.ts +++ b/apps/platform/src/modules/component-config/config-item-render/config-render-protocol.ts @@ -39,6 +39,7 @@ export type RenderProp = { index: number; upstreamTables?: [string, string]; attrConfig?: AttrConfig; + onSaveConfig?: (val: Record | undefined>) => void; }; export type ConfigRender = { diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/binning-modification/drawer/index.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/binning-modification/drawer/index.tsx index 5b16ff5..4e1c5b7 100644 --- a/apps/platform/src/modules/component-config/config-item-render/custom-render/binning-modification/drawer/index.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/binning-modification/drawer/index.tsx @@ -18,7 +18,11 @@ const BinningResultDrawer = () => { closable destroyOnClose open={isVisible} - bodyStyle={{ padding: '16px 16px 0 16px' }} + styles={{ + body: { + padding: '16px 16px 0 16px', + }, + }} onClose={() => { setCurrOperation(undefined); setVisible(false); diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/linear-model-parameters-modification/drawer/index.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/linear-model-parameters-modification/drawer/index.tsx index 59d078c..497a43f 100644 --- a/apps/platform/src/modules/component-config/config-item-render/custom-render/linear-model-parameters-modification/drawer/index.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/linear-model-parameters-modification/drawer/index.tsx @@ -18,7 +18,11 @@ const ModificationResultDrawer = () => { closable destroyOnClose open={isVisible} - bodyStyle={{ padding: '16px 16px 0 16px' }} + styles={{ + body: { + padding: '16px 16px 0 16px', + }, + }} onClose={() => { setVisible(false); }} diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.less b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.less new file mode 100644 index 0000000..134bc8e --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.less @@ -0,0 +1,83 @@ +.toolbar { + display: flex; + height: 36px; + align-items: center; + justify-content: space-between; + background: rgb(0 0 0 / 2%); +} + +.isNotFullScreenBtnClick { + position: absolute; + top: -28px; + right: 0; +} + +.toolButton { + color: rgb(0 0 0 / 65%); + font-size: 12px; + + &:hover { + color: rgb(0 0 0 / 65%) !important; + } +} + +.editor { + background-color: #fff; + + .title { + display: flex; + height: 56px; + align-items: center; + justify-content: space-between; + margin: 0 24px; + + .titleInput { + color: #1d2129; + font-size: 20px; + font-weight: 500; + } + } + + .content { + display: flex; + + .fullscreenWorkspace { + width: calc(100vw - 275px); + padding-left: 12px; + background: rgb(0 0 0 / 2%); + } + + .normalWorkspace { + width: 100%; + } + + .rightConfig { + width: 280px; + border-top: 1px solid rgb(0 0 0 / 6%); + border-left: 1px solid rgb(0 0 0 / 6%); + background: #fff; + + .titleText { + padding: 16px 12px; + color: rgb(0 0 0 / 88%); + font-size: 14px; + font-weight: 500; + line-height: 20px; + } + } + } + + .code { + height: 320px; + } + + .fullscreenCode { + height: calc(100vh - 140px); + } +} + +.footer { + padding: 12px 20px 8px; + background: #fff; + box-shadow: inset 0 1px 0 0 rgb(0 0 0 / 6%); +} diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.tsx new file mode 100644 index 0000000..2fb47c7 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/code-editor/index.tsx @@ -0,0 +1,334 @@ +import { + FullscreenOutlined, + FullscreenExitOutlined, + QuestionCircleOutlined, +} from '@ant-design/icons'; +import { useFullscreen } from 'ahooks'; +import { Button, Flex, message, Space, Tooltip } from 'antd'; +import classNames from 'classnames'; +import * as Monaco from 'monaco-editor'; +import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/sql/sql'; +import type { FC } from 'react'; +import { useEffect, useRef } from 'react'; +import { format } from 'sql-formatter'; + +import formatIcon from '@/assets/format.svg'; +import redoIcon from '@/assets/redo.svg'; +import undoIcon from '@/assets/undo.svg'; +import type { RenderProp } from '@/modules/component-config/config-item-render/config-render-protocol'; +import { OutputFeatureContent } from '@/modules/component-config/config-item-render/custom-render/upstream-feature-render/output-feature-show'; + +import styles from './index.less'; + +interface SqlEditorProps { + value: string; + onChange: (val: string) => void; + config: RenderProp; + onSaveConfig: () => void; + disabled?: boolean; + name?: string; + tooltip?: string; +} + +export const ScqlEditorCore: FC = (props) => { + const { + value = '', + onChange, + disabled = false, + name, + tooltip, + config, + onSaveConfig, + } = props; + const editorRef = useRef(null); + const fullscreenRef = useRef(null); + const monacoSuggestion = useRef(null); + const [isFullscreen, { enterFullscreen, exitFullscreen }] = + useFullscreen(fullscreenRef); + const editor = useRef(null); + + const handleValueChange = () => { + editor?.current?.onDidChangeModelContent(() => { + const editedValue = editor?.current?.getValue(); + onChange(editedValue || ''); + }); + }; + + useEffect(() => { + if (editor.current?.getValue() !== value) { + editor.current?.setValue(value || ''); + } + }, [value]); + + const initEditor = () => { + if (editorRef?.current) { + const monacoEditor = Monaco.editor.create(editorRef?.current, { + language: 'sql', + value, + readOnly: disabled, + contextmenu: false, + suggestLineHeight: 24, + formatOnPaste: true, + automaticLayout: true, + minimap: { enabled: false }, + lineHeight: 20, + folding: true, + wordWrap: 'on', + lineDecorationsWidth: 0, + lineNumbersMinChars: 1, + suggestSelection: 'first', + wordBasedSuggestions: false, + suggest: { snippetsPreventQuickSuggestions: false }, + autoClosingQuotes: 'always', + autoDetectHighContrast: false, + quickSuggestions: true, + hover: { + enabled: false, + }, + }); + editor.current = monacoEditor; + handleValueChange(); + } + }; + + const disposeEditor = () => { + if (editor?.current) { + editor.current.dispose(); + editor.current = null; + } + if (monacoSuggestion?.current) { + monacoSuggestion.current.dispose(); + monacoSuggestion.current = null; + } + }; + + useEffect(() => { + if (editor.current) { + editor.current?.updateOptions({ + quickSuggestions: isFullscreen, + contextmenu: isFullscreen, + }); + } + if (isFullscreen) { + monacoSuggestion.current = Monaco.languages.registerCompletionItemProvider( + 'sql', + { + provideCompletionItems() { + const suggestions: any[] = []; + sqlLanguage.keywords.forEach((item: any) => { + suggestions.push({ + label: item, + kind: Monaco.languages.CompletionItemKind.Keyword, + insertText: item, + }); + }); + return { + suggestions: suggestions, + }; + }, + }, + ); + } else { + if (monacoSuggestion.current) { + monacoSuggestion.current?.dispose(); + } + } + }, [isFullscreen]); + + useEffect(() => { + initEditor(); + return () => { + disposeEditor(); + }; + }, []); + + const undo = () => { + if (editor && editor?.current) { + editor.current.trigger('', 'undo', ''); + } + }; + + const redo = () => { + if (editor && editor?.current) { + editor.current.trigger('', 'redo', ''); + } + }; + + const formatCode = () => { + if (editor && editor?.current) { + const model = editor.current.getModel(); + if (!model) return; + const selection = editor.current.getSelection(); + const hasSelection = selection && !selection.isEmpty(); + + const selectCodeText = hasSelection + ? model.getValueInRange(selection) + : model.getValue(); + + const formatRange = hasSelection ? selection : model.getFullModelRange(); + + const formatSqlText = format(selectCodeText, { + language: 'mysql', + }); + + editor.current.executeEdits('', [ + { + range: formatRange, + text: formatSqlText, + }, + ]); + } + }; + + const handleFullScreen = () => { + if (isFullscreen) { + exitFullscreen(); + } else { + enterFullscreen(); + } + }; + + return ( +
+ {isFullscreen && ( +
+
+ +
{name}
+ {tooltip && ( + + document.getElementById('scriptIdInput') as HTMLDivElement + } + > + + + )} +
+
+ +
+ )} + +
+
+
+ + + + + + + + {!isFullscreen && ( + + )} +
+
+
+ + {isFullscreen && ( +
+
输入
+ +
+ )} +
+ + {isFullscreen && ( +
+ + + + +
+ )} +
+ ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.less b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.less new file mode 100644 index 0000000..58f9d23 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.less @@ -0,0 +1,5 @@ +.configItemLabel { + color: rgb(0 0 0 / 45%); + font-size: 12px; + font-weight: 400; +} diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.tsx new file mode 100644 index 0000000..6ec2100 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/scql-editor/scql-editor-content/index.tsx @@ -0,0 +1,82 @@ +import { Form } from 'antd'; + +import type { RenderProp } from '@/modules/component-config/config-item-render/config-render-protocol'; + +import { ScqlEditorCore } from './code-editor'; +import styles from './index.less'; + +const SELECT_REGEX = /SELECT[\s\S]*?(?=;|$)/gi; // 捕获 SELECT 语句直到分号结束 + +export const AnalyzeSQLEditor: React.FC> = (config) => { + const { + form, + onChange, + value, + defaultVal, + node, + translation, + disabled, + onSaveConfig, + } = config; + let name = translation[node.name] || node.name; + const { prefixes } = node; + if (prefixes && prefixes[0] === 'input') { + if (prefixes[1]) name = `${translation[prefixes[1]] || prefixes[1]} ${name}`; + } + + // 保存配置 + const saveConfig = () => { + if (form && onSaveConfig) { + onSaveConfig(form.getFieldsValue()); + } + }; + + return ( + + {name}
} + name={ + node.prefixes && node.prefixes.length > 0 + ? node.prefixes.join('/') + '/' + node.name + : node.name + } + tooltip={ + node.docString ? translation[node.docString] || node.docString : undefined + } + rules={[ + { + required: node.isRequired, + }, + { + validator: (_, codeText: string) => { + if (!codeText) { + return Promise.reject(new Error('')); + } + const selectStatements = codeText.match(SELECT_REGEX); + if (!selectStatements) { + return Promise.reject(new Error('必须有一个 SELECT 语句')); + } + if (selectStatements.length > 1) { + return Promise.reject('每个组件只允许写一个 SELECT 语句'); + } + return Promise.resolve(); + }, + }, + ]} + initialValue={defaultVal} + colon={false} + messageVariables={{ label: translation[node.name] || node.name }} + > + + + + ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.less b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.less new file mode 100644 index 0000000..cc82416 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.less @@ -0,0 +1,47 @@ +.inputTitle { + margin-bottom: 8px; + color: #00000073; + font-size: 12px; + line-height: 22px; +} + +.inputTableContent { + :global(.ant-descriptions .ant-descriptions-row > td) { + padding-bottom: 4px !important; + } +} + +.content { + overflow: auto; + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 16px 8px; + margin-bottom: 12px; + background-color: #fafafa; +} + +.ellipsisText { + overflow: hidden; + width: 167px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.copy { + color: #1677ff; + cursor: pointer; +} + +.featureFull { + width: 100%; + white-space: wrap; + word-break: break-all; +} + +.screenFullcontent { + overflow: auto; + height: 100%; + max-height: calc(100% - 250px); + background: #fff; +} diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.tsx new file mode 100644 index 0000000..fd107bd --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/index.tsx @@ -0,0 +1,66 @@ +import { ExclamationCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; +import { Flex, Form, Tooltip } from 'antd'; + +import type { RenderProp } from '../../config-render-protocol'; + +import styles from './index.less'; +import type { Feature } from './output-feature-show'; +import { OutputFeatureShow } from './output-feature-show'; + +export const upstreamOutputFeatureSerializer = (val: Feature[], clsName: string) => { + const cw = val; + return { ...{ custom_value: cw }, custom_protobuf_cls: clsName }; +}; + +export const upstreamOutputFeatureUnserializer = (val?: { + custom_value: Feature[]; +}) => { + const name = 'custom_value'; + if (!val || !val[name]) { + return []; + } + const value = val[name]; + return value; +}; + +/** + * 上游算子输出到结果表展示 + */ +export const UpstreamOutputFeatureRender: React.FC> = ( + config, +) => { + const { onChange, value, node } = config; + + return ( + + + +
+ +
+
如使用 scql 分析组件,字段名不可存在 sql 关键字和中划线
+
+ + +
输入
+
+ + + +
+
+
+ + + +
+ ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/output-feature-show.tsx b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/output-feature-show.tsx new file mode 100644 index 0000000..23224bb --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/custom-render/upstream-feature-render/output-feature-show.tsx @@ -0,0 +1,198 @@ +import { CopyOutlined } from '@ant-design/icons'; +import type { GraphNode } from '@secretflow/dag'; +import { Descriptions, Empty, Flex, message, Space, Tooltip } from 'antd'; +import classNames from 'classnames'; +import { parse } from 'query-string'; +import { useState, useEffect, useMemo } from 'react'; +import React from 'react'; +import { useLocation } from 'umi'; + +import type { RenderProp } from '@/modules/component-config/config-item-render/config-render-protocol'; +import { getGraphNodeOutput } from '@/services/secretpad/GraphController'; + +import styles from './index.less'; + +export type Feature = { + tableName: string; + tableFeatures: string[]; + nodeName: string; +}; + +export type UpstreamOutput = { + id: string; + outputId: string; + codeName: string; +}; + +export const OutputFeatureShow: React.FC> = (props) => { + const { nodeAllInfo, onChange, value } = props; + const { search } = useLocation(); + const { projectId, dagId } = parse(search) as { projectId: string; dagId: string }; + + const { upstreamSampleNodes, graphNode, upstreamNodes } = nodeAllInfo; + // 如果上游不是直接接的样本表,服务端要求名称是 各方的节点ID + 当前scql算子上游的输入 即graphNode 的 inputs[0] + // 如果上游是样本表 则展示 各方的节点ID + 样本表ID + const baseName = graphNode?.inputs?.[0]; + const upstreamIsSmapleNode = upstreamNodes[0]?.codeName === 'read_data/datatable'; + + const [sheets, setSheets] = useState([]); + + const { pathname } = useLocation(); + + useEffect(() => { + if (value) { + setSheets(value); + } + }, [value]); + + const upstreamOutputs = useMemo(() => { + return getUpstreamNodesOutputs(upstreamSampleNodes); + }, [upstreamSampleNodes]); + + useEffect(() => { + /** 直接获取最上游的样本表 */ + const getTablesFeature = async (outputs: UpstreamOutput[]) => { + const tableInfos: Feature[] = []; + await Promise.all( + outputs.map(async (output) => { + const { data } = await getGraphNodeOutput({ + projectId: projectId as string, + graphId: dagId, + graphNodeId: output.id, + outputId: output.outputId, + }); + if (!data) return; + const { meta, type } = data; + if (type === 'table') { + if (meta?.rows?.length) { + meta.rows.forEach((row) => { + let tableName = ''; + if (upstreamIsSmapleNode) { + // 上游是样本表 则展示 各方的节点ID + 样本表ID(tableId) + tableName = `${row.nodeId}_${row.tableId}`; + } else { + // 名称是上游的输出output + 当前的节点名称 + tableName = `${row.nodeId}_${baseName}`; + } + tableInfos.push({ + tableName: tableName.replaceAll('-', '_'), + tableFeatures: row.fields.split(','), + nodeName: row.nodeName, + }); + }); + } + } + }), + ); + setSheets(tableInfos); + onChange(tableInfos); + }; + if (pathname !== '/record') { + getTablesFeature(upstreamOutputs); + } + }, [upstreamOutputs]); + + return ; +}; + +export const getUpstreamNodesOutputs = (upstreamNodes: GraphNode[]) => { + if (upstreamNodes.length === 0) return []; + const upstreamOutputs: UpstreamOutput[] = []; + upstreamNodes?.forEach((n) => { + const { id, outputs = [], codeName } = n || {}; + outputs.forEach((outputId: any) => { + upstreamOutputs.push({ + id, + outputId, + codeName, + }); + }); + }); + return upstreamOutputs; +}; + +export const OutputFeatureContent = (props: { + sheets: Feature[]; + isFullScreen?: boolean; +}) => { + const { sheets, isFullScreen = false } = props; + + const handleCopy = (text: string) => { + message.config({ + getContainer: () => { + return document.getElementById('scriptIdInput') as HTMLElement; + }, + }); + navigator.clipboard + .writeText(text) + .then(() => { + message.success('复制成功'); + }) + .catch(() => { + message.error('复制失败'); + }); + }; + + return ( +
+ {sheets.length === 0 ? ( + + ) : ( + + {sheets.map((item, index) => ( +
+ + + {item.tableName || '暂无输入表'} + + + {item.nodeName || '暂无名称'} + + + {item.tableFeatures.length === 0 ? ( + '暂无特征' + ) : ( + + +
+ {item.tableFeatures.join(',')} +
+
+ handleCopy(item.tableFeatures.join(','))}> + + +
+ )} +
+
+
+ ))} +
+ )} +
+ ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/custom-serializer-registry.ts b/apps/platform/src/modules/component-config/config-item-render/custom-serializer-registry.ts index 6cdfdb0..9dca539 100644 --- a/apps/platform/src/modules/component-config/config-item-render/custom-serializer-registry.ts +++ b/apps/platform/src/modules/component-config/config-item-render/custom-serializer-registry.ts @@ -18,8 +18,16 @@ import { modelModificationsSerializer, modelModificationsUnSerializer, } from './custom-render/linear-model-parameters-modification'; +import { + upstreamOutputFeatureSerializer, + upstreamOutputFeatureUnserializer, +} from './custom-render/upstream-feature-render'; export const customSerializerRegistry = { + 'feature_column_config_pb2.FeatureColumnConfig': { + serializer: upstreamOutputFeatureSerializer, + unserializer: upstreamOutputFeatureUnserializer, + }, 'case_when_rules_pb2.CaseWhenRule': { serializer: caseWhenSerializer, unserializer: caseWhenUnserializer, diff --git a/apps/platform/src/modules/component-config/config-item-render/default-col-selection-template.tsx b/apps/platform/src/modules/component-config/config-item-render/default-col-selection-template.tsx index f606668..9bfb5d7 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-col-selection-template.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-col-selection-template.tsx @@ -21,12 +21,21 @@ import style from './index.less'; // Used for select join key for psi export const DefaultColSelection: React.FC> = (config) => { - const { node, upstreamTables = [], index, translation, disabled } = config; + const { + node, + upstreamTables = [], + index, + translation, + disabled, + form, + nodeAllInfo, + } = config; const inputName = (node.prefixes || ['input', 'input_table'])[1]; const formName = node.prefixes && node.prefixes.length > 0 ? node.prefixes.join('/') + '/' + node.name : node.name; + const { col_min_cnt_inclusive = 0, col_max_cnt_inclusive } = node; const [colsOptions, setCols] = useState<{ value: string; label: string }[]>([]); const [tableName, setTableName] = useState(); @@ -37,12 +46,12 @@ export const DefaultColSelection: React.FC> = (config) => { const [columnInfo, setColumnInfo] = useState<{ colName: string; colType: string }[]>( [], ); - const { search } = useLocation(); const { projectId, dagId } = parse(search) as { projectId: string; dagId: string; }; + useDeepCompareEffect(() => { const getTables = async () => { const dataTableList: IDataTable[] = []; @@ -170,6 +179,17 @@ export const DefaultColSelection: React.FC> = (config) => { if (fromTable) getCols(fromTable); }, [fromTable]); + /** 找到这个组件中所有 AT_SF_TABLE_COL 渲染的节点 form名称 */ + const getNamePathList = () => { + return config.componentConfig + .filter((item) => item.type === 'AT_SF_TABLE_COL') + .map((nodeItem) => { + return nodeItem.prefixes && nodeItem.prefixes.length > 0 + ? nodeItem.prefixes.join('/') + '/' + nodeItem.name + : nodeItem.name; + }); + }; + return ( <>
@@ -210,7 +230,7 @@ export const DefaultColSelection: React.FC> = (config) => { }`}
} tooltip={translation[node.docString] || node.docString} - labelCol={{ span: 6 }} + labelCol={{ span: 7 }} wrapperCol={{ span: 20 }} rules={[ { @@ -236,9 +256,46 @@ export const DefaultColSelection: React.FC> = (config) => { {(!col_max_cnt_inclusive || col_max_cnt_inclusive > 1) && !disabled && (
{i === 0 ? ( - add()} /> + { + // 隐私求交和三方隐私求交,点击form.list的删除或者新增,算子的所有的这个类型都会删除或新增 + if ( + nodeAllInfo.name === 'data_prep/psi' || + nodeAllInfo.name === 'data_prep/psi_tp' + ) { + const valueAll = form?.getFieldsValue(); + const nameList = getNamePathList(); + nameList.map((item) => { + const arr = valueAll[item]; + arr.push(undefined); + form?.setFieldValue(item, arr); + }); + } else { + add(); + } + }} + /> ) : ( - remove(i)} /> + { + if ( + nodeAllInfo.name === 'data_prep/psi' || + nodeAllInfo.name === 'data_prep/psi_tp' + ) { + const valueAll = form?.getFieldsValue(); + const nameList = getNamePathList(); + nameList.map((item: string) => { + const currentValues = valueAll[item]; + const newValues = currentValues.filter( + (_: string, index_num: number) => index_num !== i, + ); + form?.setFieldValue(item, newValues); + }); + } else { + remove(i); + } + }} + /> )}
)} diff --git a/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/multi-field-select-modal.tsx b/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/multi-field-select-modal.tsx index 6f2e650..d0ed4ad 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/multi-field-select-modal.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/multi-field-select-modal.tsx @@ -39,14 +39,16 @@ export const MultiFieldSelectModal = (props: IProps) => { setSelectedFields(fields); } else { if (selectedTable) { - const { data } = await getProjectDatatable({ - nodeId: selectedTable.nodeId, - datatableId: selectedTable.datatableId, - projectId, - type: 'CSV', - }); - if (!data) return; - setSelectedFields(data.configs); + /** 解决单选的时候,会默认将所有字段都选择上,所以注释调下面代码 setSelectedFields(data.configs),直接设置为空数组 */ + // const { data } = await getProjectDatatable({ + // nodeId: selectedTable.nodeId, + // datatableId: selectedTable.datatableId, + // projectId, + // type: 'CSV', + // }); + // if (!data) return; + // setSelectedFields(data.configs); + setSelectedFields([]); } } }; diff --git a/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/select-tree.tsx b/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/select-tree.tsx index 32ba4e4..4ad425b 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/select-tree.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-feature-selection/select-tree.tsx @@ -22,6 +22,7 @@ const typeMap = { int: 'INTEGER', int32: 'INTEGER', int64: 'INTEGER', + bool: 'BOOL', }; type TreeOptions = { @@ -192,7 +193,7 @@ export const SelectTree = ({ const onCheck = (checked: string[]) => { const filterCheckedKeysValue = checked.filter( - (item) => !['STRING', 'FLOAT', 'INTEGER'].includes(item), + (item) => !['STRING', 'FLOAT', 'INTEGER', 'BOOL'].includes(item), ); setSelectedFields(filterCheckedKeysValue.map((item) => ({ colName: item }))); }; diff --git a/apps/platform/src/modules/component-config/config-item-render/default-join-node-selection-template.tsx b/apps/platform/src/modules/component-config/config-item-render/default-join-node-selection-template.tsx new file mode 100644 index 0000000..7e5cae6 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/default-join-node-selection-template.tsx @@ -0,0 +1,116 @@ +import { Form, Select } from 'antd'; +import { uniqBy } from 'lodash'; +import { parse } from 'query-string'; +import { useState, useEffect } from 'react'; +import { useLocation } from 'umi'; + +import { getGraphNodeOutput } from '@/services/secretpad/GraphController'; +import { getProject } from '@/services/secretpad/ProjectController'; + +import styles from '../index.less'; + +import type { RenderProp } from './config-render-protocol'; + +// 非平衡 PSI ,用于选择和他合作项目的节点,且不能选择自己 +export const DefaultJoinNodeSelect: React.FC> = (config) => { + const { + onChange, + value, + defaultVal, + node, + translation, + upstreamTables = [], + } = config; + const { search } = useLocation(); + const { projectId, dagId } = parse(search) as { projectId: string; dagId: string }; + + const [nodes, setNodes] = useState<{ label: string; value: string }[]>([]); + + useEffect(() => { + const getNodes = async () => { + const nodeList = [] as { label: string; value: string }[]; + + const { data } = await getProject({ projectId }); + + const { nodes: nodeLs } = data || {}; + if (nodeLs) { + nodeLs.map((_node) => { + nodeList.push({ + value: _node.nodeId!, + label: _node.nodeName!, + }); + }); + await Promise.all( + upstreamTables.map(async (upstream) => { + if (upstream) { + // 优先从上游输出查找 + const [, graphNodeId] = upstream.match(/(.*)-output-([0-9]+)$/) || []; + if (graphNodeId) { + const { data: outputData } = await getGraphNodeOutput({ + projectId, + graphId: dagId, + graphNodeId, + outputId: upstream, + }); + if (outputData?.meta?.rows?.[0]) { + const { nodeId, nodeName } = outputData.meta.rows[0]; + const indexToDelete = nodeList.findIndex( + (item) => item.value === nodeId && item.label === nodeName, + ); + if (indexToDelete !== -1) { + nodeList.splice(indexToDelete, 1); // 删除找到的元素 + } + return; + } + } + } + }), + ); + setNodes(uniqBy(nodeList, 'value')); + } else { + setNodes([]); + } + }; + getNodes(); + }, []); + + return ( + + + {translation[node.name] || node.name} + + } + name={ + node.prefixes && node.prefixes.length > 0 + ? node.prefixes.join('/') + '/' + node.name + : node.name + } + messageVariables={{ label: translation[node.name] || node.name }} + tooltip={ + node.docString ? translation[node.docString] || node.docString : undefined + } + rules={[ + { + required: node.isRequired, + }, + ]} + initialValue={defaultVal} + colon={false} + > + + + + ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/default-node-selection-template.tsx b/apps/platform/src/modules/component-config/config-item-render/default-node-selection-template.tsx index eb9b22b..bae36b7 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-node-selection-template.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-node-selection-template.tsx @@ -26,6 +26,7 @@ export const DefaultNodeSelect: React.FC> = (config) => { componentConfig, attrConfig, } = config; + const [nodes, setNodes] = useState<{ label: string; value: string }[]>([]); const nodeService = useModel(NodeService); @@ -38,7 +39,6 @@ export const DefaultNodeSelect: React.FC> = (config) => { let isMultiple = true; if (list_max_length_inclusive === 1) isMultiple = false; - useEffect(() => { const getNodes = async () => { const nodeList = [] as { label: string; value: string }[]; diff --git a/apps/platform/src/modules/component-config/config-item-render/default-sql-editor.tsx b/apps/platform/src/modules/component-config/config-item-render/default-sql-editor.tsx index 6337227..bfd0bf8 100644 --- a/apps/platform/src/modules/component-config/config-item-render/default-sql-editor.tsx +++ b/apps/platform/src/modules/component-config/config-item-render/default-sql-editor.tsx @@ -38,6 +38,8 @@ const SQLEditor: FC = (props) => { wordWrap: 'on', lineDecorationsWidth: 0, lineNumbersMinChars: 2, + contextmenu: false, + quickSuggestions: false, }); editor.current = monacoEditor; diff --git a/apps/platform/src/modules/component-config/config-item-render/node-parties-by-upstream-output.tsx b/apps/platform/src/modules/component-config/config-item-render/node-parties-by-upstream-output.tsx new file mode 100644 index 0000000..2a255d9 --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/node-parties-by-upstream-output.tsx @@ -0,0 +1,132 @@ +import { Form, Select } from 'antd'; +import { parse } from 'query-string'; +import { useState, useEffect } from 'react'; +import { useLocation } from 'umi'; + +import type { RenderProp } from './config-render-protocol'; +import { getComponentByRenderStrategy } from './helper'; +import styles from './index.less'; + +import { hasAccess, Platform } from '@/components/platform-wrapper'; +import { getProject } from '@/services/secretpad/ProjectController'; + +export type UpstreamOutput = { + id: string; + outputId: string; + codeName: string; +}; + +/** + * 根据项目的参与方来进行选择 + * p2p 模式下只选择自己,也就是 get 接口 nodeType === 'primary', + * center 模式可以全部选择 + */ +export const nodePartiesByUpstreamOutputSelect: React.FC> = ( + config, +) => { + const { + form, + onChange, + value, + defaultVal, + node, + translation, + componentConfig, + attrConfig, + } = config; + + const isAutonomyMode = hasAccess({ type: [Platform.AUTONOMY] }); + + const [nodes, setNodes] = useState<{ label: string; value: string }[]>([]); + + const isNoWrap = !!attrConfig?.style?.noWrap; + + const { prefixes, list_max_length_inclusive } = node; + + const { search } = useLocation(); + const { projectId } = parse(search) as { projectId: string }; + + let isMultiple = true; + if (list_max_length_inclusive === 1) isMultiple = false; + + useEffect(() => { + const getNodes = async () => { + const { data } = await getProject({ projectId }); + if (!data) return; + const { nodes = [] } = data || {}; + const nodeList = isAutonomyMode + ? (nodes || []).filter((item) => item.nodeType === 'primary') + : nodes; + const nodeOptions = (nodeList || []).map((item) => ({ + label: item.nodeName!, + value: item.nodeId!, + })); + setNodes(nodeOptions); + }; + getNodes(); + }, [isAutonomyMode, projectId]); + + const item = ( + + {translation[node.name] || node.name} + + } + name={ + node.prefixes && node.prefixes.length > 0 + ? node.prefixes.join('/') + '/' + node.name + : node.name + } + messageVariables={{ label: translation[node.name] || node.name }} + tooltip={translation[node.docString] || node.docString} + rules={[ + { + required: node.isRequired, + }, + { + validator: (_, val) => { + if (node.isRequired) { + if (Array.isArray(val) && val.length > 0) { + const filtered = val.filter((i) => i); + if (filtered.length === 0) + return Promise.reject( + `${translation[node.name] || node.name}是必填字段`, + ); + } + } + + return Promise.resolve(); + }, + }, + ]} + initialValue={defaultVal} + colon={false} + labelCol={isNoWrap ? { span: 12 } : undefined} + getValueProps={(val) => { + if (val?.length === 1 && !val[0]) { + return { value: undefined }; + } + return { value: val }; + }} + > + + (option?.label ?? '') + .toLowerCase() + .includes(input.toLowerCase()) + } + /> + + + {(!col_max_cnt_inclusive || col_max_cnt_inclusive > 1) && !disabled && ( +
+ {i === 0 ? ( + { + add(); + }} + /> + ) : ( + { + remove(i); + }} + /> + )} +
+ )} + + ))} + + )} + + + ); +}; diff --git a/apps/platform/src/modules/component-config/config-item-render/unbalance-psi-custom/result-receive-select.tsx b/apps/platform/src/modules/component-config/config-item-render/unbalance-psi-custom/result-receive-select.tsx new file mode 100644 index 0000000..3e4319a --- /dev/null +++ b/apps/platform/src/modules/component-config/config-item-render/unbalance-psi-custom/result-receive-select.tsx @@ -0,0 +1,168 @@ +import type { GraphNode } from '@secretflow/dag'; +import { Form, Select } from 'antd'; +import { uniqBy } from 'lodash'; +import { parse } from 'query-string'; +import { useState, useEffect, useMemo } from 'react'; +import { useLocation } from 'umi'; + +import { getGraphNodeOutput } from '@/services/secretpad/GraphController'; + +import styles from '../../index.less'; +import type { RenderProp } from '../config-render-protocol'; +import { getComponentByRenderStrategy } from '../helper'; + +export type UpstreamOutput = { + id: string; + outputId: string; + codeName: string; +}; + +/** + * 拿到与当前算子相连接的上游所有的样本表 + * 根据样本表的输出,拿到参与方 + * 根据最上游的样本表获取参与方进行选择 + */ +export const UnbalancePsiCustomSelect: React.FC> = (config) => { + const { + form, + onChange, + value, + defaultVal, + node, + translation, + componentConfig, + attrConfig, + nodeAllInfo, + } = config; + const { upstreamSampleNodes } = nodeAllInfo; + + const [nodes, setNodes] = useState<{ label: string; value: string }[]>([]); + + const isNoWrap = !!attrConfig?.style?.noWrap; + + const { prefixes, list_max_length_inclusive } = node; + + const { search } = useLocation(); + const { projectId, dagId } = parse(search) as { projectId: string; dagId: string }; + + let isMultiple = true; + if (list_max_length_inclusive === 1) isMultiple = false; + + const getUpstreamNodesOutputs = (upstreamNodes: GraphNode[]) => { + if (upstreamNodes.length === 0) return []; + const upstreamOutputList: UpstreamOutput[] = []; + upstreamNodes?.forEach((n) => { + const { id, outputs = [], codeName } = n || {}; + outputs.forEach((outputId: any) => { + upstreamOutputList.push({ + id, + outputId, + codeName, + }); + }); + }); + return upstreamOutputList; + }; + + /** 获取上游的样本表的输出ID */ + const upstreamOutputs = useMemo(() => { + return getUpstreamNodesOutputs(upstreamSampleNodes); + }, [upstreamSampleNodes]); + + // 直接利用输出ID找出他们的参与方 + useEffect(() => { + /** 直接获取最上游的样本表 */ + const getNodes = async (outputs: UpstreamOutput[]) => { + const nodeList = [] as { label: string; value: string }[]; + await Promise.all( + outputs.map(async (output) => { + const { data } = await getGraphNodeOutput({ + projectId: projectId as string, + graphId: dagId, + graphNodeId: output.id, + outputId: output.outputId, + }); + if (!data) return; + const { meta, type } = data; + if (type === 'table') { + if (meta?.rows?.length) { + meta.rows.forEach((row) => { + nodeList.push({ + value: row.nodeId, + label: row.nodeName, + }); + }); + } + } + }), + ); + setNodes(uniqBy(nodeList, 'value')); + }; + + getNodes(upstreamOutputs); + }, [upstreamOutputs]); + + const item = ( + + {translation[node.name] || node.name} + + } + name={ + node.prefixes && node.prefixes.length > 0 + ? node.prefixes.join('/') + '/' + node.name + : node.name + } + messageVariables={{ label: translation[node.name] || node.name }} + tooltip={translation[node.docString] || node.docString} + rules={[ + { + required: node.isRequired, + }, + { + validator: (_, val) => { + if (node.isRequired) { + if (Array.isArray(val) && val.length > 0) { + const filtered = val.filter((i) => i); + if (filtered.length === 0) + return Promise.reject( + `${translation[node.name] || node.name}是必填字段`, + ); + } + } + + return Promise.resolve(); + }, + }, + ]} + initialValue={defaultVal} + colon={false} + labelCol={isNoWrap ? { span: 12 } : undefined} + getValueProps={(val) => { + if (val?.length === 1 && !val[0]) { + return { value: undefined }; + } + return { value: val }; + }} + > + + diff --git a/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx b/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx index 67b0e4a..2408956 100644 --- a/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx +++ b/apps/platform/src/modules/data-table-add/add-data/add-data.view.tsx @@ -319,9 +319,9 @@ export const DataAddDrawer = ({ - + )} diff --git a/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx b/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx index 53fe3d4..bb10225 100644 --- a/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx +++ b/apps/platform/src/modules/data-table-add/component/dataTableStructure/data-table-structure.view.tsx @@ -23,6 +23,7 @@ import { Model, useModel } from '@/util/valtio-helper'; import { analysisCsv } from '../upload-table/util'; import styles from './index.less'; +import { isScqlFeature } from './sql-keyword'; const downloadData = [ { 特征名称: 'id1', 特征类型: 'string', 特征描述: '' }, @@ -159,6 +160,13 @@ export const DataTableStructure = () => { + + {dataTableStructureService.featuresError.length > 0 && (
{ '特征名称长度限制64字符,请缩短特征名,需要同步修改本地数据文件schema', }, { - pattern: /^([a-zA-Z0-9-_]*)$/, - message: '名称可由英文/数字/下划线/中划线组成', + pattern: /^[a-zA-Z][a-zA-Z0-9-_]*$/, + message: + '名称可由英文/数字/下划线/中划线组成,且不能以数字开头', }, { validator: (rule, value) => { @@ -250,7 +259,13 @@ export const DataTableStructure = () => { { + if (!word) return false; + return SQL_KEYWORD.map((keyword) => keyword.toLowerCase()).includes( + word.toLowerCase(), + ); +}; + +// 不包含sql关键字和中划线 +export const isScqlFeature = (word: string) => { + return isSqlKeyword(word) || word?.includes('-'); +}; + +export const SQL_KEYWORD = [ + 'ACCESSIBLE', + + 'ACCOUNT', + + 'ACTION', + + 'ACTIVE', + + 'ADD', + + 'ADMIN', + + 'AFTER', + + 'AGAINST', + + 'AGGREGATE', + + 'ALGORITHM', + + 'ALL', + + 'ALTER', + + 'ALWAYS', + + 'ANALYSE', + + 'ANALYZE', + + 'AND', + + 'ANY', + + 'ARRAY', + + 'AS', + + 'ASC', + + 'ASCII', + + 'ASENSITIVE', + + 'AT', + + 'ATTRIBUTE', + + 'AUTHENTICATION', + + 'AUTOEXTEND_SIZE', + + 'AUTO_INCREMENT', + + 'AVG', + + 'AVG_ROW_LENGTH', + + 'BACKUP', + + 'BEFORE', + + 'BEGIN', + + 'BETWEEN', + + 'BIGINT', + + 'BINARY', + + 'BINLOG', + + 'BIT', + + 'BLOB', + + 'BLOCK', + + 'BOOL', + + 'BOOLEAN', + + 'BOTH', + + 'BTREE', + + 'BUCKETS', + + 'BULK', + + 'BY', + + 'BYTE', + + 'CACHE', + + 'CALL', + + 'CASCADE', + + 'CASCADED', + + 'CASE', + + 'CATALOG_NAME', + + 'CHAIN', + + 'CHALLENGE_RESPONSE', + + 'CHANGE', + + 'CHANGED', + + 'CHANNEL', + + 'CHAR', + + 'CHARACTER', + + 'CHARSET', + + 'CHECK', + + 'CHECKSUM', + + 'CIPHER', + + 'CLASS_ORIGIN', + + 'CLIENT', + + 'CLONE', + + 'CLOSE', + + 'COALESCE', + + 'CODE', + + 'COLLATE', + + 'COLLATION', + + 'COLUMN', + + 'COLUMNS', + + 'COLUMN_FORMAT', + + 'COLUMN_NAME', + + 'COMMENT', + + 'COMMIT', + + 'COMMITTED', + + 'COMPACT', + + 'COMPLETION', + + 'COMPONENT', + + 'COMPRESSED', + + 'COMPRESSION', + + 'CONCURRENT', + + 'CONDITION', + + 'CONNECTION', + + 'CONSISTENT', + + 'CONSTRAINT', + + 'CONSTRAINT_CATALOG', + + 'CONSTRAINT_NAME', + + 'CONSTRAINT_SCHEMA', + + 'CONTAINS', + + 'CONTEXT', + + 'CONTINUE', + + 'CONVERT', + + 'CPU', + + 'CREATE', + + 'CROSS', + + 'CUBE', + + 'CUME_DIST', + + 'CURRENT', + + 'CURRENT_DATE', + + 'CURRENT_TIME', + + 'CURRENT_TIMESTAMP', + + 'CURRENT_USER', + + 'CURSOR', + + 'CURSOR_NAME', + + 'DATA', + + 'DATABASE', + + 'DATABASES', + + 'DATAFILE', + + 'DATE', + + 'DATETIME', + + 'DAY', + + 'DAY_HOUR', + + 'DAY_MICROSECOND', + + 'DAY_MINUTE', + + 'DAY_SECOND', + + 'DEALLOCATE', + + 'DEC', + + 'DECIMAL', + + 'DECLARE', + + 'DEFAULT', + + 'DEFAULT_AUTH', + + 'DEFINER', + + 'DEFINITION', + + 'DELAYED', + + 'DELAY_KEY_WRITE', + + 'DELETE', + + 'DENSE_RANK', + + 'DESC', + + 'DESCRIBE', + + 'DESCRIPTION', + 'DES_KEY_FILE', + + 'DETERMINISTIC', + + 'DIAGNOSTICS', + + 'DIRECTORY', + + 'DISABLE', + + 'DISCARD', + + 'DISK', + + 'DISTINCT', + + 'DISTINCTROW', + + 'DIV', + + 'DO', + + 'DOUBLE', + + 'DROP', + + 'DUAL', + + 'DUMPFILE', + + 'DUPLICATE', + + 'DYNAMIC', + + 'EACH', + + 'ELSE', + + 'ELSEIF', + + 'EMPTY', + + 'ENABLE', + + 'ENCLOSED', + + 'ENCRYPTION', + + 'END', + + 'ENDS', + + 'ENFORCED', + + 'ENGINE', + + 'ENGINES', + + 'ENGINE_ATTRIBUTE', + + 'ENUM', + + 'ERROR', + + 'ERRORS', + + 'ESCAPE', + + 'ESCAPED', + + 'EVENT', + + 'EVENTS', + + 'EVERY', + + 'EXCEPT', + + 'EXCHANGE', + + 'EXCLUDE', + + 'EXECUTE', + + 'EXISTS', + + 'EXIT', + + 'EXPANSION', + + 'EXPIRE', + + 'EXPLAIN', + + 'EXPORT', + + 'EXTENDED', + + 'EXTENT_SIZE', + + 'FACTOR', + + 'FAILED_LOGIN_ATTEMPTS', + + 'FALSE', + + 'FAST', + + 'FAULTS', + + 'FETCH', + + 'FIELDS', + + 'FILE', + + 'FILE_BLOCK_SIZE', + + 'FILTER', + + 'FINISH', + + 'FIRST', + + 'FIRST_VALUE', + + 'FIXED', + + 'FLOAT', + + 'FLOAT4', + + 'FLOAT8', + + 'FLUSH', + + 'FOLLOWING', + + 'FOLLOWS', + + 'FOR', + + 'FORCE', + + 'FOREIGN', + + 'FORMAT', + + 'FOUND', + + 'FROM', + + 'FULL', + + 'FULLTEXT', + + 'FUNCTION', + + 'GENERAL', + + 'GENERATE', + 'GENERATED', + + 'GEOMCOLLECTION', + 'GEOMETRY', + + 'GEOMETRYCOLLECTION', + + 'GET', + + 'GET_FORMAT', + + 'GET_MASTER_PUBLIC_KEY', + + 'GET_SOURCE_PUBLIC_KEY', + + 'GLOBAL', + + 'GRANT', + + 'GRANTS', + + 'GROUP', + + 'GROUPING', + + 'GROUPS', + + 'GROUP_REPLICATION', + + 'GTID_ONLY', + + 'HANDLER', + + 'HASH', + + 'HAVING', + + 'HELP', + + 'HIGH_PRIORITY', + + 'HISTOGRAM', + + 'HISTORY', + + 'HOST', + + 'HOSTS', + + 'HOUR', + + 'HOUR_MICROSECOND', + + 'HOUR_MINUTE', + + 'HOUR_SECOND', + + 'IDENTIFIED', + + 'IF', + + 'IGNORE', + + 'IGNORE_SERVER_IDS', + + 'IMPORT', + + 'IN', + + 'INACTIVE', + + 'INDEX', + + 'INDEXES', + + 'INFILE', + + 'INITIAL', + + 'INITIAL_SIZE', + + 'INITIATE', + + 'INNER', + + 'INOUT', + + 'INSENSITIVE', + + 'INSERT', + + 'INSERT_METHOD', + + 'INSTALL', + + 'INSTANCE', + + 'INT', + + 'INT1', + + 'INT2', + + 'INT3', + + 'INT4', + + 'INT8', + + 'INTEGER', + + 'INTERSECT', + + 'INTERVAL', + + 'INTO', + + 'INVISIBLE', + + 'INVOKER', + + 'IO', + + 'IO_AFTER_GTIDS', + + 'IO_BEFORE_GTIDS', + + 'IO_THREAD', + + 'IPC', + + 'IS', + + 'ISOLATION', + + 'ISSUER', + 'ITERATE', + + 'JOIN', + + 'JSON', + + 'JSON_TABLE', + + 'JSON_VALUE', + + 'KEY', + + 'KEYRING', + + 'KEYS', + + 'KEY_BLOCK_SIZE', + + 'KILL', + + 'LAG', + + 'LANGUAGE', + + 'LAST', + 'LAST_VALUE', + + 'LATERAL', + + 'LEAD', + + 'LEADING', + + 'LEAVE', + + 'LEAVES', + + 'LEFT', + + 'LESS', + + 'LEVEL', + + 'LIKE', + + 'LIMIT', + + 'LINEAR', + + 'LINES', + + 'LINESTRING', + + 'LIST', + + 'LOAD', + + 'LOCAL', + + 'LOCALTIME', + + 'LOCALTIMESTAMP', + + 'LOCK', + + 'LOCKED', + + 'LOCKS', + + 'LOGFILE', + + 'LOGS', + + 'LONG', + + 'LONGBLOB', + + 'LONGTEXT', + + 'LOOP', + + 'LOW_PRIORITY', + + 'MASTER', + + 'MASTER_AUTO_POSITION', + + 'MASTER_BIND', + + 'MASTER_COMPRESSION_ALGORITHMS', + + 'MASTER_CONNECT_RETRY', + + 'MASTER_DELAY', + + 'MASTER_HEARTBEAT_PERIOD', + + 'MASTER_HOST', + + 'MASTER_LOG_FILE', + + 'MASTER_LOG_POS', + + 'MASTER_PASSWORD', + + 'MASTER_PORT', + + 'MASTER_PUBLIC_KEY_PATH', + + 'MASTER_RETRY_COUNT', + + 'MASTER_SERVER_ID', + + 'MASTER_SSL', + + 'MASTER_SSL_CA', + + 'MASTER_SSL_CAPATH', + + 'MASTER_SSL_CERT', + + 'MASTER_SSL_CIPHER', + + 'MASTER_SSL_CRL', + + 'MASTER_SSL_CRLPATH', + + 'MASTER_SSL_KEY', + + 'MASTER_SSL_VERIFY_SERVER_CERT', + + 'MASTER_TLS_CIPHERSUITES', + + 'MASTER_TLS_VERSION', + + 'MASTER_USER', + + 'MASTER_ZSTD_COMPRESSION_LEVEL', + + 'MATCH', + + 'MAXVALUE', + + 'MAX_CONNECTIONS_PER_HOUR', + + 'MAX_QUERIES_PER_HOUR', + + 'MAX_ROWS', + + 'MAX_SIZE', + + 'MAX_UPDATES_PER_HOUR', + + 'MAX_USER_CONNECTIONS', + + 'MEDIUM', + + 'MEDIUMBLOB', + + 'MEDIUMINT', + + 'MEDIUMTEXT', + + 'MEMBER', + + 'MEMORY', + + 'MERGE', + + 'MESSAGE_TEXT', + + 'MICROSECOND', + + 'MIDDLEINT', + + 'MIGRATE', + + 'MINUTE', + + 'MINUTE_MICROSECOND', + + 'MINUTE_SECOND', + + 'MIN_ROWS', + + 'MOD', + + 'MODE', + + 'MODIFIES', + + 'MODIFY', + + 'MONTH', + + 'MULTILINESTRING', + + 'MULTIPOINT', + + 'MULTIPOLYGON', + + 'MUTEX', + + 'MYSQL_ERRNO', + + 'NAME', + + 'NAMES', + + 'NATIONAL', + + 'NATURAL', + + 'NCHAR', + + 'NDB', + + 'NDBCLUSTER', + + 'NESTED', + + 'NETWORK_NAMESPACE', + + 'NEVER', + + 'NEW', + + 'NEXT', + + 'NO', + + 'NODEGROUP', + + 'NONE', + + 'NOT', + + 'NOWAIT', + + 'NO_WAIT', + + 'NO_WRITE_TO_BINLOG', + + 'NTH_VALUE', + + 'NTILE', + + 'NULL', + + 'NULLS', + + 'NUMBER', + + 'NUMERIC', + + 'NVARCHAR', + + 'OF', + + 'OFF', + + 'OFFSET', + + 'OJ', + + 'OLD', + + 'ON', + + 'ONE', + + 'ONLY', + + 'OPEN', + + 'OPTIMIZE', + + 'OPTIMIZER_COSTS', + + 'OPTION', + + 'OPTIONAL', + + 'OPTIONALLY', + + 'OPTIONS', + + 'OR', + + 'ORDER', + + 'ORDINALITY', + + 'ORGANIZATION', + + 'OTHERS', + + 'OUT', + + 'OUTER', + + 'OUTFILE', + + 'OVER', + + 'OWNER', + + 'PACK_KEYS', + + 'PAGE', + + 'PARSER', + + 'PARTIAL', + + 'PARTITION', + + 'PARTITIONING', + + 'PARTITIONS', + + 'PASSWORD', + + 'PASSWORD_LOCK_TIME', + + 'PATH', + + 'PERCENT_RANK', + + 'PERSIST', + + 'PERSIST_ONLY', + + 'PHASE', + + 'PLUGIN', + + 'PLUGINS', + + 'PLUGIN_DIR', + + 'POINT', + + 'POLYGON', + + 'PORT', + + 'PRECEDES', + + 'PRECEDING', + + 'PRECISION', + + 'PREPARE', + + 'PRESERVE', + + 'PREV', + + 'PRIMARY', + + 'PRIVILEGES', + + 'PRIVILEGE_CHECKS_USER', + + 'PROCEDURE', + + 'PROCESS', + + 'PROCESSLIST', + + 'PROFILE', + + 'PROFILES', + + 'PROXY', + + 'PURGE', + + 'QUARTER', + + 'QUERY', + + 'QUICK', + + 'RANDOM', + + 'RANGE', + + 'RANK', + + 'READ', + + 'READS', + + 'READ_ONLY', + + 'READ_WRITE', + + 'REAL', + + 'REBUILD', + + 'RECOVER', + + 'RECURSIVE', + + 'REDOFILE', + + 'REDO_BUFFER_SIZE', + + 'REDUNDANT', + + 'REFERENCE', + + 'REFERENCES', + + 'REGEXP', + + 'REGISTRATION', + + 'RELAY', + + 'RELAYLOG', + + 'RELAY_LOG_FILE', + + 'RELAY_LOG_POS', + + 'RELAY_THREAD', + + 'RELEASE', + + 'RELOAD', + + 'REMOTE', + + 'REMOVE', + + 'RENAME', + + 'REORGANIZE', + + 'REPAIR', + + 'REPEAT', + + 'REPEATABLE', + + 'REPLACE', + + 'REPLICA', + + 'REPLICAS', + + 'REPLICATE_DO_DB', + + 'REPLICATE_DO_TABLE', + + 'REPLICATE_IGNORE_DB', + + 'REPLICATE_IGNORE_TABLE', + + 'REPLICATE_REWRITE_DB', + + 'REPLICATE_WILD_DO_TABLE', + + 'REPLICATE_WILD_IGNORE_TABLE', + + 'REPLICATION', + + 'REQUIRE', + + 'REQUIRE_ROW_FORMAT', + + 'RESET', + + 'RESIGNAL', + + 'RESOURCE', + + 'RESPECT', + + 'RESTART', + + 'RESTORE', + + 'RESTRICT', + + 'RESUME', + + 'RETAIN', + + 'RETURN', + + 'RETURNED_SQLSTATE', + + 'RETURNING', + + 'RETURNS', + + 'REUSE', + + 'REVERSE', + + 'REVOKE', + + 'RIGHT', + + 'RLIKE', + + 'ROLE', + + 'ROLLBACK', + + 'ROLLUP', + + 'ROTATE', + + 'ROUTINE', + + 'ROW', + + 'ROWS', + + 'ROW_COUNT', + + 'ROW_FORMAT', + + 'ROW_NUMBER', + + 'RTREE', + + 'SAVEPOINT', + + 'SCHEDULE', + + 'SCHEMA', + + 'SCHEMAS', + + 'SCHEMA_NAME', + + 'SECOND', + + 'SECONDARY', + + 'SECONDARY_ENGINE', + + 'SECONDARY_ENGINE_ATTRIBUTE', + + 'SECONDARY_LOAD', + + 'SECONDARY_UNLOAD', + + 'SECOND_MICROSECOND', + + 'SECURITY', + + 'SELECT', + + 'SENSITIVE', + + 'SEPARATOR', + + 'SERIAL', + + 'SERIALIZABLE', + + 'SERVER', + + 'SESSION', + + 'SET', + + 'SHARE', + + 'SHOW', + + 'SHUTDOWN', + + 'SIGNAL', + + 'SIGNED', + + 'SIMPLE', + + 'SKIP', + + 'SLAVE', + + 'SLOW', + + 'SMALLINT', + + 'SNAPSHOT', + + 'SOCKET', + + 'SOME', + + 'SONAME', + + 'SOUNDS', + + 'SOURCE', + + 'SOURCE_AUTO_POSITION', + + 'SOURCE_BIND', + + 'SOURCE_COMPRESSION_ALGORITHMS', + + 'SOURCE_CONNECT_RETRY', + + 'SOURCE_DELAY', + + 'SOURCE_HEARTBEAT_PERIOD', + + 'SOURCE_HOST', + + 'SOURCE_LOG_FILE', + + 'SOURCE_LOG_POS', + + 'SOURCE_PASSWORD', + + 'SOURCE_PORT', + + 'SOURCE_PUBLIC_KEY_PATH', + + 'SOURCE_RETRY_COUNT', + + 'SOURCE_SSL', + + 'SOURCE_SSL_CA', + + 'SOURCE_SSL_CAPATH', + + 'SOURCE_SSL_CERT', + + 'SOURCE_SSL_CIPHER', + + 'SOURCE_SSL_CRL', + + 'SOURCE_SSL_CRLPATH', + + 'SOURCE_SSL_KEY', + + 'SOURCE_SSL_VERIFY_SERVER_CERT', + + 'SOURCE_TLS_CIPHERSUITES', + + 'SOURCE_TLS_VERSION', + + 'SOURCE_USER', + + 'SOURCE_ZSTD_COMPRESSION_LEVEL', + + 'SPATIAL', + + 'SPECIFIC', + + 'SQL', + + 'SQLEXCEPTION', + + 'SQLSTATE', + + 'SQLWARNING', + + 'SQL_AFTER_GTIDS', + + 'SQL_AFTER_MTS_GAPS', + + 'SQL_BEFORE_GTIDS', + + 'SQL_BIG_RESULT', + + 'SQL_BUFFER_RESULT', + + 'SQL_CACHE', + + 'SQL_CALC_FOUND_ROWS', + + 'SQL_NO_CACHE', + + 'SQL_SMALL_RESULT', + + 'SQL_THREAD', + + 'SQL_TSI_DAY', + + 'SQL_TSI_HOUR', + + 'SQL_TSI_MINUTE', + + 'SQL_TSI_MONTH', + + 'SQL_TSI_QUARTER', + + 'SQL_TSI_SECOND', + + 'SQL_TSI_WEEK', + + 'SQL_TSI_YEAR', + + 'SRID', + + 'SSL', + + 'STACKED', + + 'START', + + 'STARTING', + + 'STARTS', + + 'STATS_AUTO_RECALC', + + 'STATS_PERSISTENT', + + 'STATS_SAMPLE_PAGES', + + 'STATUS', + + 'STOP', + + 'STORAGE', + + 'STORED', + + 'STRAIGHT_JOIN', + + 'STREAM', + + 'STRING', + + 'SUBCLASS_ORIGIN', + + 'SUBJECT', + + 'SUBPARTITION', + + 'SUBPARTITIONS', + + 'SUPER', + + 'SUSPEND', + + 'SWAPS', + + 'SWITCHES', + + 'SYSTEM', + + 'TABLE', + + 'TABLES', + + 'TABLESPACE', + + 'TABLE_CHECKSUM', + + 'TABLE_NAME', + + 'TEMPORARY', + + 'TEMPTABLE', + + 'TERMINATED', + + 'TEXT', + + 'THAN', + + 'THEN', + + 'THREAD_PRIORITY', + + 'TIES', + + 'TIME', + + 'TIMESTAMP', + + 'TIMESTAMPADD', + + 'TIMESTAMPDIFF', + + 'TINYBLOB', + + 'TINYINT', + + 'TINYTEXT', + + 'TLS', + + 'TO', + + 'TRAILING', + + 'TRANSACTION', + + 'TRIGGER', + + 'TRIGGERS', + + 'TRUE', + + 'TRUNCATE', + + 'TYPE', + + 'TYPES', + + 'UNBOUNDED', + + 'UNCOMMITTED', + + 'UNDEFINED', + + 'UNDO', + + 'UNDOFILE', + + 'UNDO_BUFFER_SIZE', + + 'UNICODE', + + 'UNINSTALL', + + 'UNION', + + 'UNIQUE', + + 'UNKNOWN', + + 'UNLOCK', + + 'UNREGISTER', + + 'UNSIGNED', + + 'UNTIL', + + 'UPDATE', + + 'UPGRADE', + + 'URL', + + 'USAGE', + + 'USE', + + 'USER', + + 'USER_RESOURCES', + + 'USE_FRM', + + 'USING', + + 'UTC_DATE', + + 'UTC_TIME', + + 'UTC_TIMESTAMP', + + 'VALIDATION', + + 'VALUE', + + 'VALUES', + + 'VARBINARY', + + 'VARCHAR', + + 'VARCHARACTER', + + 'VARIABLES', + + 'VARYING', + + 'VCPU', + + 'VIEW', + + 'VIRTUAL', + + 'VISIBLE', + + 'WAIT', + + 'WARNINGS', + + 'WEEK', + + 'WEIGHT_STRING', + + 'WHEN', + + 'WHERE', + + 'WHILE', + + 'WINDOW', + + 'WITH', + + 'WITHOUT', + + 'WORK', + + 'WRAPPER', + + 'WRITE', + + 'X509', + + 'XA', + + 'XID', + + 'XML', + + 'XOR', + + 'YEAR', + + 'YEAR_MONTH', + + 'ZEROFILL', + + 'ACTIVE', + + 'ADMIN', + + 'ARRAY', + + 'ATTRIBUTE', + + 'AUTHENTICATION', + + 'BUCKETS', + + 'BULK', + + 'CHALLENGE_RESPONSE', + + 'CLONE', + + 'COMPONENT', + + 'CUME_DIST', + + 'DEFINITION', + + 'DENSE_RANK', + + 'DESCRIPTION', + + 'EMPTY', + + 'ENFORCED', + + 'ENGINE_ATTRIBUTE', + + 'EXCEPT', + + 'EXCLUDE', + + 'FACTOR', + + 'FAILED_LOGIN_ATTEMPTS', + + 'FINISH', + + 'FIRST_VALUE', + + 'FOLLOWING', + + 'GENERATE', + + 'GEOMCOLLECTION', + + 'GET_MASTER_PUBLIC_KEY', + + 'GET_SOURCE_PUBLIC_KEY', + + 'GROUPING', + + 'GROUPS', + + 'GTID_ONLY', + + 'HISTOGRAM', + + 'HISTORY', + + 'INACTIVE', + + 'INITIAL', + + 'INITIATE', + + 'INTERSECT', + + 'INVISIBLE', + + 'JSON_TABLE', + + 'JSON_VALUE', + + 'KEYRING', + + 'LAG', + + 'LAST_VALUE', + + 'LATERAL', + + 'LEAD', + + 'LOCKED', + + 'MASTER_COMPRESSION_ALGORITHMS', + + 'MASTER_PUBLIC_KEY_PATH', + + 'MASTER_TLS_CIPHERSUITES', + + 'MASTER_ZSTD_COMPRESSION_LEVEL', + + 'MEMBER', + + 'NESTED', + + 'NETWORK_NAMESPACE', + + 'NOWAIT', + + 'NTH_VALUE', + + 'NTILE', + + 'NULLS', + + 'OF', + + 'OFF', + + 'OJ', + + 'OLD', + + 'OPTIONAL', + + 'ORDINALITY', + + 'ORGANIZATION', + + 'OTHERS', + + 'OVER', + + 'PASSWORD_LOCK_TIME', + + 'PATH', + + 'PERCENT_RANK', + + 'PERSIST', + + 'PERSIST_ONLY', + + 'PRECEDING', + + 'PRIVILEGE_CHECKS_USER', + + 'PROCESS', + + 'RANDOM', + + 'RANK', + + 'RECURSIVE', + + 'REFERENCE', + + 'REGISTRATION', + + 'REPLICA', + + 'REPLICAS', + + 'REQUIRE_ROW_FORMAT', + + 'RESOURCE', + + 'RESPECT', + + 'RESTART', + + 'RETAIN', + + 'RETURNING', + + 'REUSE', + + 'ROLE', + + 'ROW_NUMBER', + + 'SECONDARY', + + 'SECONDARY_ENGINE', + + 'SECONDARY_ENGINE_ATTRIBUTE', + + 'SECONDARY_LOAD', + + 'SECONDARY_UNLOAD', + + 'SKIP', + + 'SOURCE_AUTO_POSITION', + + 'SOURCE_BIND', + + 'SOURCE_COMPRESSION_ALGORITHMS', + + 'SOURCE_CONNECT_RETRY', + + 'SOURCE_DELAY', + + 'SOURCE_HEARTBEAT_PERIOD', + + 'SOURCE_HOST', + + 'SOURCE_LOG_FILE', + + 'SOURCE_LOG_POS', + + 'SOURCE_PASSWORD', + + 'SOURCE_PORT', + + 'SOURCE_PUBLIC_KEY_PATH', + + 'SOURCE_RETRY_COUNT', + + 'SOURCE_SSL', + + 'SOURCE_SSL_CA', + + 'SOURCE_SSL_CAPATH', + + 'SOURCE_SSL_CERT', + + 'SOURCE_SSL_CIPHER', + + 'SOURCE_SSL_CRL', + + 'SOURCE_SSL_CRLPATH', + + 'SOURCE_SSL_KEY', + + 'SOURCE_SSL_VERIFY_SERVER_CERT', + + 'SOURCE_TLS_CIPHERSUITES', + + 'SOURCE_TLS_VERSION', + + 'SOURCE_USER', + + 'SOURCE_ZSTD_COMPRESSION_LEVEL', + + 'SRID', + + 'STREAM', + + 'SYSTEM', + + 'THREAD_PRIORITY', + + 'TIES', + + 'TLS', + + 'UNBOUNDED', + + 'UNREGISTER', + + 'URL', + + 'VCPU', + + 'VISIBLE', + + 'WINDOW', + + 'ZONE', +]; diff --git a/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx b/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx index dff3895..19dc053 100644 --- a/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx +++ b/apps/platform/src/modules/data-table-add/component/upload-table/upload-table.view.tsx @@ -13,6 +13,8 @@ import { NodeService } from '@/modules/node'; import { createDataTable } from '@/services/secretpad/DatatableController'; import { getModel, Model, useModel } from '@/util/valtio-helper'; +import { isScqlFeature } from '../dataTableStructure/sql-keyword'; + import styles from './index.less'; import UploadTableFileList from './upload-list'; import type { FileInfo } from './upload-list'; @@ -266,6 +268,12 @@ export const UploadTable: React.FC = ({
+ {viewInstance.schemaErrors.length > 0 && (
= ({ '特征名称长度限制64字符,请缩短特征名,需要同步修改本地数据文件schema', }, { - pattern: /^([a-zA-Z0-9-_]*)$/, - message: '名称可由英文/数字/下划线/中划线组成', + pattern: /^[a-zA-Z][a-zA-Z0-9-_]*$/, + message: + '名称可由英文/数字/下划线/中划线组成,且不能以数字开头', }, { validator: (rule, value) => { @@ -364,6 +373,13 @@ export const UploadTable: React.FC = ({
)} + { }, { noun: '训练流模版', - desc: '目前包括联合圈人、金融风控两个训练流模板,通过新建项目或者新建训练流时可选新建模板。', + desc: '目前包括联合圈人、二分类建模两个训练流模板,通过新建项目或者新建训练流时可选新建模板。', }, { noun: '模型', diff --git a/apps/platform/src/modules/main-dag/graph-request-service.tsx b/apps/platform/src/modules/main-dag/graph-request-service.tsx index f8acd89..c4622ba 100644 --- a/apps/platform/src/modules/main-dag/graph-request-service.tsx +++ b/apps/platform/src/modules/main-dag/graph-request-service.tsx @@ -72,6 +72,16 @@ const mlTrainCodeNames = [ 'ml.train/ss_xgb_train', ]; +/** + * 训练组件(SecureBoost训练 / SSGLM训练 / 逻辑回归训练 / SS-XGB训练)支持进度展示 + */ +const showProgressCodeNames = [ + 'ml.train/sgb_train', + 'ml.train/ss_glm_train', + 'ml.train/ss_xgb_train', + 'ml.train/ss_sgd_train', +]; + export class GraphRequestService extends DefaultRequestService { // 只有画布更新了,queryDag才会去重新请求接口 graphUpdated = true; @@ -115,10 +125,17 @@ export class GraphRequestService extends DefaultRequestService { return { nodeStatus: - nodes?.map(({ graphNodeId, status }) => ({ - nodeId: graphNodeId as string, - status: nodeStatus[status || 'STAGING'] as unknown as NodeStatus, - })) || [], + nodes?.map(({ graphNodeId, status, progress = 0 }) => { + const currentCodeName = (this.graphData?.nodes || []).find( + (itemNode: { id: string }) => itemNode.id === graphNodeId, + )?.codeName; + const showProcess = showProgressCodeNames.includes(currentCodeName); + return { + nodeId: graphNodeId as string, + status: nodeStatus[status || 'STAGING'] as unknown as NodeStatus, + statusProcess: showProcess ? Number((progress * 100).toFixed(2)) : 0, + }; + }) || [], finished: finished as boolean, }; } @@ -171,7 +188,7 @@ export class GraphRequestService extends DefaultRequestService { const { nodes, edges } = data; const convertedNodes = nodes?.map((n) => { - const { graphNodeId, status, codeName, ...options } = n; + const { graphNodeId, status, codeName, progress = 0, ...options } = n; const configs = (this.componentConfigService.getComponentConfig( { @@ -187,11 +204,15 @@ export class GraphRequestService extends DefaultRequestService { if (graphNodeStatus === NodeStatus.default && !isFinished) { graphNodeStatus = NodeStatus.unfinished; } + return { ...options, codeName, id: graphNodeId, status: graphNodeStatus, + statusProcess: showProgressCodeNames.includes(codeName as string) + ? Number((progress * 100).toFixed(2)) + : 0, styles: { // 目前只支持 (SecureBoost训练 SSGLM训练 SS-XGB训练) 算子才可 继续执行 isContinueRun: mlTrainCodeNames.includes(codeName as string), @@ -214,6 +235,7 @@ export class GraphRequestService extends DefaultRequestService { this.onNodeChangedEmitter.fire(convertedNodes as IGraphNodeType[]); this.graphData = convertedData; + this.graphUpdated = false; return convertedData; } diff --git a/apps/platform/src/modules/main-dag/graph-service.ts b/apps/platform/src/modules/main-dag/graph-service.ts index 1535262..9042dcb 100644 --- a/apps/platform/src/modules/main-dag/graph-service.ts +++ b/apps/platform/src/modules/main-dag/graph-service.ts @@ -24,6 +24,7 @@ import type { import { ComponentConfigRegistry } from '../component-config/component-config-registry'; import { DefaultComponentConfigService } from '../component-config/component-config-service'; import { componentConfigDrawer } from '../component-config/config-modal'; +import { quickConfigDrawer } from '../component-config/template-quick-config/quick-config-drawer'; import { DefaultComponentInterpreterService } from '../component-interpreter/component-interpreter-service'; import type { Component, ComputeMode } from '../component-tree/component-protocol'; import { DefaultComponentTreeService } from '../component-tree/component-tree-service'; @@ -182,13 +183,16 @@ export class GraphService implements GraphEventHandlerProtocol { const arr: string[] = []; await mainDag.dataService.getUpstreamInputNodes(id, arr); const upstreamSampleNodes = await mainDag.dataService.getInputsSampleNodes(arr); - - this.modalManager.openModal(componentConfigDrawer.id, { - ...data, - graphNode, - upstreamNodes, - inputNodes, - upstreamSampleNodes, // 上游样本表 + // 右侧配置面板打开,切换算子的时候会渲染异常 ,暂先如此处理 + this.modalManager.closeModal(componentConfigDrawer.id); + setTimeout(() => { + this.modalManager.openModal(componentConfigDrawer.id, { + ...data, + graphNode, + upstreamNodes, + inputNodes, + upstreamSampleNodes, // 上游样本表 + }); }); }; @@ -219,7 +223,7 @@ export class GraphService implements GraphEventHandlerProtocol { onBlankClick() { this.logService.cancel(); - this.modalManager.closeAllModals(); + this.modalManager.closeAllModalsBut(quickConfigDrawer.id); } onEdgeRemoved() { diff --git a/apps/platform/src/modules/pipeline/pipeline-creation-view.tsx b/apps/platform/src/modules/pipeline/pipeline-creation-view.tsx index 7ab456b..c887e2f 100644 --- a/apps/platform/src/modules/pipeline/pipeline-creation-view.tsx +++ b/apps/platform/src/modules/pipeline/pipeline-creation-view.tsx @@ -34,7 +34,7 @@ export const QuickConfigEntry = (prop: { type: PipelineTemplateType }) => { > 快速配置 - ;如15s内未快速配置,后续可逐个配置 + ;如当前15s内未快速配置,后续可逐个配置 ); }; diff --git a/apps/platform/src/modules/pipeline/pipeline-protocol.tsx b/apps/platform/src/modules/pipeline/pipeline-protocol.tsx index 1c08768..1e495aa 100644 --- a/apps/platform/src/modules/pipeline/pipeline-protocol.tsx +++ b/apps/platform/src/modules/pipeline/pipeline-protocol.tsx @@ -5,11 +5,11 @@ import { ReactComponent as Psi } from '@/assets/template_type_psi.svg'; export enum PipelineTemplateType { BLANK = 'blank', - RISK = 'risk', // 金融风控 + RISK = 'risk', // 二分类建模 PSI = 'psi', // 联合圈人 PSI_TEE = 'psi-tee', // 联合圈人 PSI_TEE_GUIDE = 'psi-tee-guide', - RISK_GUIDE = 'risk-guide', // 金融风控 + RISK_GUIDE = 'risk-guide', // 二分类建模 PSI_GUIDE = 'psi-guide', TEE = 'TEE', // Tee TEE_GUIDE = 'tee-guide', // Tee guide diff --git a/apps/platform/src/modules/pipeline/pipeline-view.tsx b/apps/platform/src/modules/pipeline/pipeline-view.tsx index d2e9b9f..bf24907 100644 --- a/apps/platform/src/modules/pipeline/pipeline-view.tsx +++ b/apps/platform/src/modules/pipeline/pipeline-view.tsx @@ -95,10 +95,6 @@ export const PipelineViewComponent = () => { const viewInstance = useModel(PipelineView); const [searchParams] = useSearchParams(); - useEffect(() => { - viewInstance.setCurrentPipeline(dagId as string); - }, [dagId]); - useEffect(() => { const update = async () => { await viewInstance.refresh(); @@ -122,7 +118,7 @@ export const PipelineViewComponent = () => { } }; update(); - }, [projectId, dagId]); + }, [projectId]); /** * P2P模式下,判断当前训练流和项目能否编辑 diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-psi-guide.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-psi-guide.ts index 518c9bc..c1af527 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-psi-guide.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-psi-guide.ts @@ -81,17 +81,18 @@ export class TemplateGuidePSI extends Model implements PipelineTemplateContribut status: `STAGING`, }, { - outputs: [`${graphId}-node-3-output-0`], + outputs: [`${graphId}-node-3-output-0`, `${graphId}-node-3-output-1`], nodeDef: { attrPaths: [ - 'input/input_table_1/key', - 'input/input_table_2/key', + 'input/input_ds1/keys', + 'input/input_ds2/keys', 'protocol', 'sort_result', - 'allow_duplicate_keys', - 'allow_duplicate_keys/no/skip_duplicates_check', - 'ecdh_curve', - 'allow_duplicate_keys/no/receiver_parties', + 'receiver_parties', + 'allow_empty_result', + 'join_type', + 'input_ds1_keys_duplicated', + 'input_ds2_keys_duplicated', ], attrs: [ { @@ -110,22 +111,26 @@ export class TemplateGuidePSI extends Model implements PipelineTemplateContribut b: true, is_na: false, }, + { ss: ['alice', 'bob'], is_na: false }, { - s: 'no', + is_na: true, + }, + { + s: 'inner_join', is_na: false, }, { - is_na: true, + b: true, + is_na: false, }, { - s: 'CURVE_FOURQ', + b: true, is_na: false, }, - { ss: ['alice', 'bob'], is_na: false }, ], domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', }, inputs: [`${graphId}-node-1-output-0`, `${graphId}-node-2-output-0`], codeName: `data_prep/psi`, diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-psi.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-psi.ts index 76872b8..de532f7 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-psi.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-psi.ts @@ -82,19 +82,20 @@ export class TemplatePSI extends Model implements PipelineTemplateContribution { status: `STAGING`, }, { - outputs: [`${graphId}-node-3-output-0`], + outputs: [`${graphId}-node-3-output-0`, `${graphId}-node-3-output-1`], nodeDef: { ...(receiverKey && senderKey ? { attrPaths: [ - 'input/input_table_1/key', - 'input/input_table_2/key', + 'input/input_ds1/keys', + 'input/input_ds2/keys', 'protocol', 'sort_result', - 'allow_duplicate_keys', - 'allow_duplicate_keys/no/skip_duplicates_check', - 'ecdh_curve', - 'allow_duplicate_keys/no/receiver_parties', + 'receiver_parties', + 'allow_empty_result', + 'join_type', + 'input_ds1_keys_duplicated', + 'input_ds2_keys_duplicated', ], attrs: [ { @@ -114,29 +115,33 @@ export class TemplatePSI extends Model implements PipelineTemplateContribution { is_na: false, }, { - s: 'no', + ...receiverPSI, is_na: false, }, { is_na: true, }, { - s: 'CURVE_FOURQ', + s: 'inner_join', is_na: false, }, { - ...receiverPSI, + b: true, + is_na: false, + }, + { + b: true, is_na: false, }, ], domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', } : { domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', }), }, inputs: [`${graphId}-node-1-output-0`, `${graphId}-node-2-output-0`], diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-risk-guide.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-risk-guide.ts index b53d66e..1363a31 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-risk-guide.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-risk-guide.ts @@ -6,7 +6,7 @@ import { PipelineTemplateType } from '../pipeline-protocol'; export class TemplateGuideRisk extends Model implements PipelineTemplateContribution { type: PipelineTemplateType = PipelineTemplateType.RISK_GUIDE; - name = `金融风控`; + name = `二分类建模`; argsFilled = true; // TODO: this is place holder, to be replaced @@ -366,17 +366,18 @@ export class TemplateGuideRisk extends Model implements PipelineTemplateContribu status: `STAGING`, }, { - outputs: [`${graphId}-node-3-output-0`], + outputs: [`${graphId}-node-3-output-0`, `${graphId}-node-3-output-1`], nodeDef: { attrPaths: [ - 'input/input_table_1/key', - 'input/input_table_2/key', + 'input/input_ds1/keys', + 'input/input_ds2/keys', 'protocol', 'sort_result', - 'allow_duplicate_keys', - 'allow_duplicate_keys/no/skip_duplicates_check', - 'ecdh_curve', - 'allow_duplicate_keys/no/receiver_parties', + 'receiver_parties', + 'allow_empty_result', + 'join_type', + 'input_ds1_keys_duplicated', + 'input_ds2_keys_duplicated', ], attrs: [ { @@ -395,22 +396,26 @@ export class TemplateGuideRisk extends Model implements PipelineTemplateContribu b: true, is_na: false, }, + { ss: ['alice', 'bob'], is_na: false }, { - s: 'no', + is_na: true, + }, + { + s: 'inner_join', is_na: false, }, { - is_na: true, + b: true, + is_na: false, }, { - s: 'CURVE_FOURQ', + b: true, is_na: false, }, - { ss: ['alice', 'bob'], is_na: false }, ], domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', }, inputs: [`${graphId}-node-1-output-0`, `${graphId}-node-2-output-0`], codeName: `data_prep/psi`, diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-risk.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-risk.ts index 660a355..8eb0295 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-risk.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-risk.ts @@ -6,7 +6,7 @@ import { PipelineTemplateType } from '../pipeline-protocol'; export class TemplateRisk extends Model implements PipelineTemplateContribution { type: PipelineTemplateType = PipelineTemplateType.RISK; - name = `金融风控`; + name = `二分类建模`; argsFilled = false; description = '二分类训练流模板'; @@ -309,19 +309,20 @@ export class TemplateRisk extends Model implements PipelineTemplateContribution status: `STAGING`, }, { - outputs: [`${graphId}-node-3-output-0`], + outputs: [`${graphId}-node-3-output-0`, `${graphId}-node-3-output-1`], nodeDef: { ...(receiverKey && senderKey ? { attrPaths: [ - 'input/input_table_1/key', - 'input/input_table_2/key', + 'input/input_ds1/keys', + 'input/input_ds2/keys', 'protocol', 'sort_result', - 'allow_duplicate_keys', - 'allow_duplicate_keys/no/skip_duplicates_check', - 'ecdh_curve', - 'allow_duplicate_keys/no/receiver_parties', + 'receiver_parties', + 'allow_empty_result', + 'join_type', + 'input_ds1_keys_duplicated', + 'input_ds2_keys_duplicated', ], attrs: [ { @@ -341,29 +342,33 @@ export class TemplateRisk extends Model implements PipelineTemplateContribution is_na: false, }, { - s: 'no', + ...receiverPSI, is_na: false, }, { is_na: true, }, { - s: 'CURVE_FOURQ', + s: 'inner_join', is_na: false, }, { - ...receiverPSI, + b: true, + is_na: false, + }, + { + b: true, is_na: false, }, ], domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', } : { domain: 'data_prep', name: 'psi', - version: '0.0.8', + version: '1.0.0', }), }, inputs: [`${graphId}-node-1-output-0`, `${graphId}-node-2-output-0`], diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-tee-guide.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-tee-guide.ts index 5f81b23..4ed8caa 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-tee-guide.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-tee-guide.ts @@ -6,7 +6,7 @@ import { PipelineTemplateType } from '../pipeline-protocol'; export class TemplateGuidTEE extends Model implements PipelineTemplateContribution { type: PipelineTemplateType = PipelineTemplateType.TEE_GUIDE; - name = `金融风控`; + name = `二分类建模`; argsFilled = true; computeMode = ['TEE']; diff --git a/apps/platform/src/modules/pipeline/templates/pipeline-template-tee.ts b/apps/platform/src/modules/pipeline/templates/pipeline-template-tee.ts index db9a34b..4da64d8 100644 --- a/apps/platform/src/modules/pipeline/templates/pipeline-template-tee.ts +++ b/apps/platform/src/modules/pipeline/templates/pipeline-template-tee.ts @@ -6,7 +6,7 @@ import { PipelineTemplateType } from '../pipeline-protocol'; export class TemplateTEE extends Model implements PipelineTemplateContribution { type: PipelineTemplateType = PipelineTemplateType.TEE; - name = `金融风控`; + name = `二分类建模`; argsFilled = false; description = '二分类训练流模板'; computeMode = ['TEE']; diff --git a/apps/platform/src/services/secretpad/typings.d.ts b/apps/platform/src/services/secretpad/typings.d.ts index 11c2a5b..a44b521 100644 --- a/apps/platform/src/services/secretpad/typings.d.ts +++ b/apps/platform/src/services/secretpad/typings.d.ts @@ -839,6 +839,8 @@ manipulate, derived from the value returned by the back end in the uplink mouth jobId?: string; /** Graph node task status */ status?: GraphNodeTaskStatus; + /** Graph node task status process */ + statusProcess?: number; /** Graph node job parties */ parties?: Array; } diff --git a/packages/dag/src/actions/add-node.ts b/packages/dag/src/actions/add-node.ts index 45135f1..4b01fbc 100644 --- a/packages/dag/src/actions/add-node.ts +++ b/packages/dag/src/actions/add-node.ts @@ -20,6 +20,7 @@ export class AddNodeAction extends DAGContext implements ActionProtocol { x: pos.x, y: pos.y, status: nodeData.status, + statusProcess: nodeData.statusProcess, }; this.context.dataService.addNodes([meta]); } diff --git a/packages/dag/src/actions/change-status.ts b/packages/dag/src/actions/change-status.ts index ce46d90..be68740 100644 --- a/packages/dag/src/actions/change-status.ts +++ b/packages/dag/src/actions/change-status.ts @@ -1,6 +1,6 @@ import type { Graph } from '@antv/x6'; -import type { NodeStatus } from '../..'; +import type { NodeStatus } from '@/types'; import type { ActionProtocol } from './protocol'; import { ActionType } from './protocol'; diff --git a/packages/dag/src/actions/drag-node.ts b/packages/dag/src/actions/drag-node.ts index 17bd93b..cff9286 100644 --- a/packages/dag/src/actions/drag-node.ts +++ b/packages/dag/src/actions/drag-node.ts @@ -16,7 +16,7 @@ export class DragNodeAction extends DAGContext implements ActionProtocol { async handle( graph: Graph, dagId: string, - nodeData: Pick, + nodeData: Pick, e: MouseEvent, ) { if (!this.dnd) { @@ -28,7 +28,7 @@ export class DragNodeAction extends DAGContext implements ActionProtocol { } const maxNodeIndex = await this.context.requestService.getMaxNodeIndex(dagId); const nodeId = `${dagId}-node-${maxNodeIndex + 1}`; - const { label, codeName, status } = nodeData; + const { label, codeName, status, statusProcess } = nodeData; const outputs = await this.context.hookService.createResult(nodeId, codeName); const ports = await this.context.hookService.createPort(nodeId, codeName); const node = graph.createNode({ @@ -41,6 +41,7 @@ export class DragNodeAction extends DAGContext implements ActionProtocol { label, status: status || NodeStatus.default, outputs, + statusProcess: statusProcess || 0, }, }); this.dnd.start(node, e); diff --git a/packages/dag/src/actions/query-status.ts b/packages/dag/src/actions/query-status.ts index 4432463..00f2215 100644 --- a/packages/dag/src/actions/query-status.ts +++ b/packages/dag/src/actions/query-status.ts @@ -69,16 +69,18 @@ export class QueryStatusAction extends DAGContext implements ActionProtocol { } changeNodesStatus( - nodeStatus: { nodeId: string; status: NodeStatus }[], + nodeStatus: { nodeId: string; status: NodeStatus; statusProcess: number }[], graph: Graph, ) { - nodeStatus.forEach(({ nodeId, status }) => { + nodeStatus.forEach(({ nodeId, status, statusProcess }) => { const node = graph.getCellById(nodeId); if (node) { node.setData({ ...node.getData(), status, + statusProcess, }); + const edges = graph.getIncomingEdges(nodeId); edges?.forEach((edge) => { const sourceNodeId = edge.getSourceCellId(); diff --git a/packages/dag/src/actions/render.ts b/packages/dag/src/actions/render.ts index af591bb..479eab6 100644 --- a/packages/dag/src/actions/render.ts +++ b/packages/dag/src/actions/render.ts @@ -37,6 +37,7 @@ export class RenderAction extends DAGContext implements ActionProtocol { codeName: node.codeName, label: node.label, status: node.status, + statusProcess: node.statusProcess || 0, outputs, styles: node.styles, nodeDef: node.nodeDef, diff --git a/packages/dag/src/request/protocol.ts b/packages/dag/src/request/protocol.ts index 8426dbd..3bc46d1 100644 --- a/packages/dag/src/request/protocol.ts +++ b/packages/dag/src/request/protocol.ts @@ -2,7 +2,7 @@ import type { NodeStatus, GraphModel } from '../types'; export type RequestService = { queryStatus: (dagId: string) => Promise<{ - nodeStatus: { nodeId: string; status: NodeStatus }[]; + nodeStatus: { nodeId: string; status: NodeStatus; statusProcess: number }[]; finished: boolean; }>; queryDag: (dagId: string) => Promise; diff --git a/packages/dag/src/shapes/descriptions.tsx b/packages/dag/src/shapes/descriptions.tsx index 1ddb4a4..a40eefc 100644 --- a/packages/dag/src/shapes/descriptions.tsx +++ b/packages/dag/src/shapes/descriptions.tsx @@ -20,11 +20,12 @@ enum statusColorEnum { success = 'success', error = 'error', default = 'default', + processing = 'processing', } export const Description = (props: IProps) => { const { dagContext } = props; - const { id, status, outputs, codeName, showContinueRun } = props.data; + const { id, status, outputs, codeName, showContinueRun, statusProcess } = props.data; const { nodeNum } = parseNodeId(id); let statusDes = ''; let statusColor = ''; @@ -36,6 +37,7 @@ export const Description = (props: IProps) => { statusColor = statusColorEnum.error; } else if (status === NodeStatus.running) { statusDes = '执行中'; + statusColor = statusColorEnum.processing; } else if (status === NodeStatus.pending) { statusDes = '已提交'; } else if (status === NodeStatus.default) { @@ -72,7 +74,15 @@ export const Description = (props: IProps) => { status={statusColorEnum[statusColor as keyof typeof statusColorEnum]} /> )} - {statusDes} + {(status === NodeStatus.running || status === NodeStatus.stopped) && + statusProcess !== 0 ? ( + <> + {'已执行'} + {`${statusProcess}%`} + + ) : ( + {statusDes} + )} {showContinueRun && ( diff --git a/packages/dag/src/shapes/index.less b/packages/dag/src/shapes/index.less index 257c3fe..b08729c 100644 --- a/packages/dag/src/shapes/index.less +++ b/packages/dag/src/shapes/index.less @@ -8,6 +8,7 @@ border-left: 4px solid rgb(0 104 250 / 100%); background-color: #fff; box-shadow: 0 2px 5px 1px rgb(0 0 0 / 6%); + transition: background 0.5s; .icon { flex-shrink: 0; diff --git a/packages/dag/src/shapes/node.tsx b/packages/dag/src/shapes/node.tsx index bf95b82..3bf25aa 100644 --- a/packages/dag/src/shapes/node.tsx +++ b/packages/dag/src/shapes/node.tsx @@ -37,7 +37,7 @@ const DagNode = (props: { node: Node; graph: Graph }) => { const DAGContext = DAGGlobalContainer.get(graph) as DAGProtocol; const graphManager = DAGContext?.graphManager; const data = node.getData(); - const { id, status, label, codeName, styles } = data; + const { id, status, label, codeName, styles, statusProcess } = data; const statusName = NodeStatus[status]; const [domain] = codeName.split('/'); const { @@ -163,7 +163,21 @@ const DagNode = (props: { node: Node; graph: Graph }) => { overlayStyle={{ width: 258 }} destroyTooltipOnHide > -
+
{ComponentIcons[domain] || ComponentIcons['default']} @@ -190,6 +204,18 @@ const DagNode = (props: { node: Node; graph: Graph }) => { { opaque: isOpaque }, { hightlight: isHighlighted }, )} + style={{ + background: + status === NodeStatus.running || status === NodeStatus.stopped + ? `linear-gradient( + to right, + #e5f0ff 0%, + #e5f0ff ${statusProcess}%, + transparent ${statusProcess}%, + transparent 100% + )` + : '#fff', + }} > {ComponentIcons[domain] || ComponentIcons['default']} diff --git a/packages/dag/src/types/index.ts b/packages/dag/src/types/index.ts index ae6e88f..90e7396 100644 --- a/packages/dag/src/types/index.ts +++ b/packages/dag/src/types/index.ts @@ -24,6 +24,7 @@ export type GraphNode = { x: number; y: number; status: NodeStatus; + statusProcess: number; //node meta info and attrs nodeDef?: any; outputs?: GraphNodeOutput[]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbfba41..e7fec9d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -144,6 +144,9 @@ importers: react-syntax-highlighter: specifier: ^15.5.0 version: 15.5.0(react@18.3.1) + sql-formatter: + specifier: ^15.4.2 + version: 15.4.6 umi: specifier: ^4.0.64 version: 4.3.18(@babel/core@7.25.2)(@types/node@20.5.1)(@types/react@18.3.5)(eslint@8.57.0)(jest@29.7.0)(prettier@2.8.8)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@4.9.5)(webpack@5.94.0) @@ -8025,6 +8028,10 @@ packages: dependencies: path-type: 4.0.0 + /discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dev: false + /doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -9910,7 +9917,6 @@ packages: /get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} - dev: true /get-stream@3.0.0: resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} @@ -13366,6 +13372,10 @@ packages: resolution: {integrity: sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA==} dev: false + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: false + /mousetrap@1.6.5: resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==} dev: false @@ -13426,6 +13436,16 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + /nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + dependencies: + commander: 2.20.3 + moo: 0.5.2 + railroad-diagrams: 1.0.0 + randexp: 0.4.6 + dev: false + /needle@3.3.1: resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} engines: {node: '>= 4.4.x'} @@ -15241,10 +15261,22 @@ packages: resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} dev: false + /railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + dev: false + /ramda@0.29.0: resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} dev: true + /randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + dependencies: + discontinuous-range: 1.0.0 + ret: 0.1.15 + dev: false + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -16522,6 +16554,11 @@ packages: signal-exit: 3.0.7 dev: true + /ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + dev: false + /retry@0.10.1: resolution: {integrity: sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==} dev: true @@ -17104,6 +17141,15 @@ packages: /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + /sql-formatter@15.4.6: + resolution: {integrity: sha512-aH6kwvJpylljHqXe+zpie0Q5snL3uerDLLhjPEBjDCVK1NMRFq4nMJbuPJWYp08LaaaJJgBhShAdAfspcBYY0Q==} + hasBin: true + dependencies: + argparse: 2.0.1 + get-stdin: 8.0.0 + nearley: 2.20.1 + dev: false + /ssri@4.1.6: resolution: {integrity: sha512-WUbCdgSAMQjTFZRWvSPpauryvREEA+Krn19rx67UlJEJx/M192ZHxMmJXjZ4tkdFm+Sb0SXGlENeQVlA5wY7kA==} dependencies: