From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001
From: wwf <yearningwang@iqtogether.com>
Date: 星期三, 04 六月 2025 15:17:49 +0800
Subject: [PATCH] 初始化

---
 app/components/workflow/hooks/use-workflow.ts |  179 +++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 130 insertions(+), 49 deletions(-)

diff --git a/app/components/workflow/hooks/use-workflow.ts b/app/components/workflow/hooks/use-workflow.ts
index 99dce4d..2eafb4a 100644
--- a/app/components/workflow/hooks/use-workflow.ts
+++ b/app/components/workflow/hooks/use-workflow.ts
@@ -1,9 +1,13 @@
 import {
   useCallback,
+  useEffect,
   useMemo,
+  useState,
 } from 'react'
+import dayjs from 'dayjs'
 import { uniqBy } from 'lodash-es'
 import { useTranslation } from 'react-i18next'
+import { useContext } from 'use-context-selector'
 import {
   getIncomers,
   getOutgoers,
@@ -36,17 +40,24 @@
 import { CUSTOM_NOTE_NODE } from '../note-node/constants'
 import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
 import { useNodesExtraData } from './use-nodes-data'
+import { useWorkflowTemplate } from './use-workflow-template'
 import { useStore as useAppStore } from '@/app/components/app/store'
+import {
+  fetchNodesDefaultConfigs,
+  fetchPublishedWorkflow,
+  fetchWorkflowDraft,
+  syncWorkflowDraft,
+} from '@/service/workflow'
+import type { FetchWorkflowDraftResponse } from '@/types/workflow'
 import {
   fetchAllBuiltInTools,
   fetchAllCustomTools,
   fetchAllWorkflowTools,
 } from '@/service/tools'
+import I18n from '@/context/i18n'
 import { CollectionType } from '@/app/components/tools/types'
 import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants'
-import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants'
-import { basePath } from '@/utils/var'
-import { canFindTool } from '@/utils'
+import { useWorkflowConfig } from '@/service/use-workflow'
 
 export const useIsChatMode = () => {
   const appDetail = useAppStore(s => s.appDetail)
@@ -56,9 +67,12 @@
 
 export const useWorkflow = () => {
   const { t } = useTranslation()
+  const { locale } = useContext(I18n)
   const store = useStoreApi()
   const workflowStore = useWorkflowStore()
+  const appId = useStore(s => s.appId)
   const nodesExtraData = useNodesExtraData()
+  const { data: workflowConfig } = useWorkflowConfig(appId)
   const setPanelWidth = useCallback((width: number) => {
     localStorage.setItem('workflow-node-panel-width', `${width}`)
     workflowStore.setState({ panelWidth: width })
@@ -74,7 +88,7 @@
     const currentNode = nodes.find(node => node.id === nodeId)
 
     if (currentNode?.parentId)
-      startNode = nodes.find(node => node.parentId === currentNode.parentId && (node.type === CUSTOM_ITERATION_START_NODE || node.type === CUSTOM_LOOP_START_NODE))
+      startNode = nodes.find(node => node.parentId === currentNode.parentId && node.type === CUSTOM_ITERATION_START_NODE)
 
     if (!startNode)
       return []
@@ -103,7 +117,7 @@
 
     list.push(...incomers)
 
-    return uniqBy(list, 'id').filter((item: Node) => {
+    return uniqBy(list, 'id').filter((item) => {
       return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type)
     })
   }, [store])
@@ -150,7 +164,7 @@
 
     const length = list.length
     if (length) {
-      return uniqBy(list, 'id').reverse().filter((item: Node) => {
+      return uniqBy(list, 'id').reverse().filter((item) => {
         return SUPPORT_OUTPUT_VARS_NODE.includes(item.data.type)
       })
     }
@@ -224,15 +238,6 @@
     return nodes.filter(node => node.parentId === nodeId)
   }, [store])
 
-  const getLoopNodeChildren = useCallback((nodeId: string) => {
-    const {
-      getNodes,
-    } = store.getState()
-    const nodes = getNodes()
-
-    return nodes.filter(node => node.parentId === nodeId)
-  }, [store])
-
   const isFromStartNode = useCallback((nodeId: string) => {
     const { getNodes } = store.getState()
     const nodes = getNodes()
@@ -274,7 +279,7 @@
       setNodes(newNodes)
     }
 
-    // eslint-disable-next-line react-hooks/exhaustive-deps
+  // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [store])
 
   const isVarUsedInNodes = useCallback((varSelector: ValueSelector) => {
@@ -327,7 +332,6 @@
       parallelList,
       hasAbnormalEdges,
     } = getParallelInfo(nodes, edges, parentNodeId)
-    const { workflowConfig } = workflowStore.getState()
 
     if (hasAbnormalEdges)
       return false
@@ -343,7 +347,7 @@
     }
 
     return true
-  }, [t, workflowStore])
+  }, [t, workflowStore, workflowConfig?.parallel_depth_limit])
 
   const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => {
     const {
@@ -391,6 +395,10 @@
     return !hasCycle(targetNode)
   }, [store, nodesExtraData, checkParallelLimit])
 
+  const formatTimeFromNow = useCallback((time: number) => {
+    return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow()
+  }, [locale])
+
   const getNode = useCallback((nodeId?: string) => {
     const { getNodes } = store.getState()
     const nodes = getNodes()
@@ -412,10 +420,10 @@
     checkNestedParallelLimit,
     isValidConnection,
     isFromStartNode,
+    formatTimeFromNow,
     getNode,
     getBeforeNodeById,
     getIterationNodeChildren,
-    getLoopNodeChildren,
   }
 }
 
@@ -426,12 +434,6 @@
     if (type === 'builtin') {
       const buildInTools = await fetchAllBuiltInTools()
 
-      if (basePath) {
-        buildInTools.forEach((item) => {
-          if (typeof item.icon == 'string' && !item.icon.includes(basePath))
-            item.icon = `${basePath}${item.icon}`
-        })
-      }
       workflowStore.setState({
         buildInTools: buildInTools || [],
       })
@@ -454,6 +456,108 @@
 
   return {
     handleFetchAllTools,
+  }
+}
+
+export const useWorkflowInit = () => {
+  const workflowStore = useWorkflowStore()
+  const {
+    nodes: nodesTemplate,
+    edges: edgesTemplate,
+  } = useWorkflowTemplate()
+  const { handleFetchAllTools } = useFetchToolsData()
+  const appDetail = useAppStore(state => state.appDetail)!
+  const setSyncWorkflowDraftHash = useStore(s => s.setSyncWorkflowDraftHash)
+  const [data, setData] = useState<FetchWorkflowDraftResponse>()
+  const [isLoading, setIsLoading] = useState(true)
+  useEffect(() => {
+    workflowStore.setState({ appId: appDetail.id })
+  }, [appDetail.id, workflowStore])
+
+  const handleGetInitialWorkflowData = useCallback(async () => {
+    try {
+      const res = await fetchWorkflowDraft(`/apps/${appDetail.id}/workflows/draft`)
+      setData(res)
+      workflowStore.setState({
+        envSecrets: (res.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => {
+          acc[env.id] = env.value
+          return acc
+        }, {} as Record<string, string>),
+        environmentVariables: res.environment_variables?.map(env => env.value_type === 'secret' ? { ...env, value: '[__HIDDEN__]' } : env) || [],
+        // #TODO chatVar sync#
+        conversationVariables: res.conversation_variables || [],
+      })
+      setSyncWorkflowDraftHash(res.hash)
+      setIsLoading(false)
+    }
+    catch (error: any) {
+      if (error && error.json && !error.bodyUsed && appDetail) {
+        error.json().then((err: any) => {
+          if (err.code === 'draft_workflow_not_exist') {
+            workflowStore.setState({ notInitialWorkflow: true })
+            syncWorkflowDraft({
+              url: `/apps/${appDetail.id}/workflows/draft`,
+              params: {
+                graph: {
+                  nodes: nodesTemplate,
+                  edges: edgesTemplate,
+                },
+                features: {
+                  retriever_resource: { enabled: true },
+                },
+                environment_variables: [],
+                conversation_variables: [],
+              },
+            }).then((res) => {
+              workflowStore.getState().setDraftUpdatedAt(res.updated_at)
+              handleGetInitialWorkflowData()
+            })
+          }
+        })
+      }
+    }
+  }, [appDetail, nodesTemplate, edgesTemplate, workflowStore, setSyncWorkflowDraftHash])
+
+  useEffect(() => {
+    handleGetInitialWorkflowData()
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [])
+
+  const handleFetchPreloadData = useCallback(async () => {
+    try {
+      const nodesDefaultConfigsData = await fetchNodesDefaultConfigs(`/apps/${appDetail?.id}/workflows/default-workflow-block-configs`)
+      const publishedWorkflow = await fetchPublishedWorkflow(`/apps/${appDetail?.id}/workflows/publish`)
+      workflowStore.setState({
+        nodesDefaultConfigs: nodesDefaultConfigsData.reduce((acc, block) => {
+          if (!acc[block.type])
+            acc[block.type] = { ...block.config }
+          return acc
+        }, {} as Record<string, any>),
+      })
+      workflowStore.getState().setPublishedAt(publishedWorkflow?.created_at)
+    }
+    catch (e) {
+
+    }
+  }, [workflowStore, appDetail])
+
+  useEffect(() => {
+    handleFetchPreloadData()
+    handleFetchAllTools('builtin')
+    handleFetchAllTools('custom')
+    handleFetchAllTools('workflow')
+  }, [handleFetchPreloadData, handleFetchAllTools])
+
+  useEffect(() => {
+    if (data) {
+      workflowStore.getState().setDraftUpdatedAt(data.updated_at)
+      workflowStore.getState().setToolPublished(data.tool_published)
+    }
+  }, [data, workflowStore])
+
+  return {
+    data,
+    isLoading,
   }
 }
 
@@ -505,7 +609,7 @@
         targetTools = customTools
       else
         targetTools = workflowTools
-      return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon
+      return targetTools.find(toolWithProvider => toolWithProvider.id === data.provider_id)?.icon
     }
   }, [data, buildInTools, customTools, workflowTools])
 
@@ -532,28 +636,5 @@
   }, [iterationId, store])
   return {
     isNodeInIteration,
-  }
-}
-
-export const useIsNodeInLoop = (loopId: string) => {
-  const store = useStoreApi()
-
-  const isNodeInLoop = useCallback((nodeId: string) => {
-    const {
-      getNodes,
-    } = store.getState()
-    const nodes = getNodes()
-    const node = nodes.find(node => node.id === nodeId)
-
-    if (!node)
-      return false
-
-    if (node.parentId === loopId)
-      return true
-
-    return false
-  }, [loopId, store])
-  return {
-    isNodeInLoop,
   }
 }

--
Gitblit v1.8.0