1. 页面概览

1.1 是什么

数据源页面(FoundryDataSourcesPage.vue)负责 Foundry 产品与外部数据库的连接管理。它是 V5 Stage 1(B1-6)「数据源路由解耦」的产物:此前 datasources 表与 CRUD 路由仅存在于 AIP 服务(18080),而 Foundry 是独立进程 + 独立库,AIP 停机时 Foundry 侧数据源页直接 404。B1-6 后 Foundry 后端(18081)自持 /datasources 全套路由,数据源 CRUD、连接测试、元数据导入不再经由 AIP——AIP 停机时本页仍完全可用

页面核心能力有四:新建 / 编辑数据源(连接参数管理,密码后端打码回显 ****)、测试连接(按请求体完整连接配置真实建连验证)、导入元数据(读取外部表结构入库,可选 AI 生成表/列业务描述)、删除数据源。其中「导入元数据」是任务化操作:后端 15 秒窗口内完成则直接返回结果(快路径),超时返回 202 受理 {task_id, status:'running', type},前端在结果弹窗内轮询进度条直到终态(success / failed / cancelled)。

页面依赖 platform/datasource 平台包的服务能力,在 foundry 库建表(AutoMigrate 见 bootstrap.go)并注册全套路由,响应统一套 foundry 风格 {code:0, data:{...}} 包装,数据在 res.data.data。API 走独立实例 foundryDsApi.js(baseURL /api/v1 → Vite 代理 → Foundry 18081),不修改 client.js / aipClient.js

1.2 核心价值

价值点说明
路由解耦Foundry 自持 /datasources,AIP 停机不影响数据源页可用性
连接测试按请求体完整连接配置真实建连,结果 JSON 弹窗展示,配置错误即时暴露
元数据导入任务化15s 快路径 + 202 受理轮询进度条,大库导入不卡死页面
AI 描述生成导入时可勾选「AI 生成描述」,调 LLM 为表/列生成业务描述(较慢)
密码打码编辑回显 ****,密码留空提交表示不改,避免明文泄漏
支撑数据域数据源是数据集拉数 / 同步引擎 / 质量画像 / 语义查询的连接底座

1.3 一句话总结

数据源页面把"连接外部数据库"变成可创建、可测试、可导入元数据、可维护的自助服务。

2. 访问入口

2.1 路由与菜单

路由 path/foundry/datasources
路由 nameFoundryDataSources
meta.titleFoundry 数据源
侧边栏入口FoundryLayout 侧边栏「数据源」(位于「SQL 工作台」之后、「数据血缘」之前)
前端源码action/web/src/views/FoundryDataSourcesPage.vue
API 客户端action/web/src/api/foundryDsApi.js

路由注册见 action/web/src/router/index.js

{ path: 'datasources', name: 'FoundryDataSources', component: FoundryDataSourcesPage, meta: { title: 'Foundry 数据源', requiresAuth: true } },

侧边栏入口见 action/web/src/views/FoundryLayout.vuemenuItems 数组 { path: '/foundry/datasources', label: '数据源' }

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

+--------------------------------------------------------------+
| Foundry 数据源    [☑ 导入时 AI 生成描述(调用 LLM,较慢)] [新建数据源] |
+--------------------------------------------------------------+
| [alert 操作结果提示条(可关闭)]                                    |
+--------------------------------------------------------------+
| [新建/编辑表单卡片(showCreateForm 时显示)]                        |
|  数据源名称* | 数据库类型* | 主机地址 | 端口 | 数据库名 | 用户名 | 密码 | 描述 |
|  [创建数据源/更新数据源] [取消]                                     |
+--------------------------------------------------------------+
| [数据源列表卡片]                                                  |
|  ID | 名称 | 类型 | 描述 | 连接信息 | 操作                          |
|  [测试连接] [导入元数据/导入中...] [编辑] [删除]                     |
+--------------------------------------------------------------+
| [结果弹窗(modal,resultModal.visible 时显示)]                    |
|  测试连接结果 / 导入元数据结果 / 导入元数据(任务 xxx)                |
|  进行中:进度条 + 百分比 · 任务执行中...  <pre>JSON 结果</pre>        |
+--------------------------------------------------------------+

各板块职责:

4. 交互元素详解

4.1 页头与列表

元素位置含义必填/默认操作效果触发的后端调用
「导入时 AI 生成描述(调用 LLM,较慢)」开关页头左侧导入元数据时是否调用 LLM 生成表/列业务描述默认关闭绑定 aiGenerate,导入时作为 query 参数POST /api/v1/datasources/:id/import-metadata?ai_generate_descriptions=true/false
「新建数据源」/「收起表单」按钮页头右侧展开/收起新建表单切换 showCreateForm
「测试连接」按钮列表操作列按请求体完整连接配置真实建连测试导入中禁用弹出「测试连接结果」JSONPOST /api/v1/datasources/:id/test
「导入元数据」/「导入中...」按钮列表操作列导入外部表结构到平台元数据导入中禁用并显示「导入中...」弹窗展示结果或进度条轮询POST /api/v1/datasources/:id/import-metadata?ai_generate_descriptions=...
「编辑」按钮列表操作列将数据源填入表单进入编辑模式密码留空、标题切「编辑数据源」PUT /api/v1/datasources/:id
「删除」按钮列表操作列删除数据源confirm 二次确认后删除DELETE /api/v1/datasources/:id

4.2 新建 / 编辑表单

字段含义必填/默认操作效果触发的后端调用
数据源名称数据源显示名必填,placeholder 如 sales_data_warehouse写入 dsForm.namePOST /api/v1/datasourcesPUT /api/v1/datasources/:id
数据库类型连接类型下拉必填,占位「请选择」;选项:PostgreSQL / MySQL / SQLite / SQL Server / Oracle / ClickHouse写入 dsForm.type(值为 POSTGRESQL/MYSQL/SQLITE/SQLSERVER/ORACLE/CLICKHOUSE)同上
主机地址数据库主机可空,placeholder localhost写入 dsForm.host → payload 中 connection_config.host同上
端口连接端口可空,type=number,placeholder 5432写入 dsForm.portconnection_config.port(空提交为 0)同上
数据库名目标库名可空,placeholder chatbi_forge_dev写入 dsForm.databaseconnection_config.database同上
用户名连接账号可空,placeholder postgres写入 dsForm.userconnection_config.user同上
密码连接密码编辑时留空表示不改,后端回显 ****写入 dsForm.passwordconnection_config.password(编辑留空则提交空串,后端保留原密码)同上
描述数据源业务描述可空,textarea rows=2写入 dsForm.descriptionconnection_config.description同上
「创建数据源」/「更新数据源」/「提交中...」按钮表单底部提交创建或更新提交中禁用并显示「提交中...」POST /api/v1/datasourcesPUT /api/v1/datasources/:id同上
「取消」按钮表单底部重置并收起表单调用 resetForm()

4.3 结果弹窗

元素含义
标题测试连接结果 / 导入元数据结果 / 导入元数据(任务 task_id 前 8 位...)
进度条taskRunning=true 时显示,宽度按 progress × 100%,下方 百分比 · progressMessage
JSON 内容<pre class="code-block"> 展示 JSON.stringify(payload, null, 2),最大高度 400px 可滚动
「关闭」按钮关闭弹窗并 stopPolling();点击遮罩(@click.self)同样关闭

5. 后端关联

5.1 API 客户端

foundryDsApi.js 为独立 axios 实例(B1-6 数据源路由解耦专用):

导出函数:

函数请求
listDataSources()GET /datasourcesdata.data_sources
getDataSource(id)GET /datasources/:id
createDataSource(payload)POST /datasources(201)
updateDataSource(id, payload)PUT /datasources/:id
deleteDataSource(id)DELETE /datasources/:iddata.deleted
testDataSource(id, payload)POST /datasources/:id/test(body 同创建:完整连接配置)
importMetadata(id, aiGenerate=false)POST /datasources/:id/import-metadata?ai_generate_descriptions=true/false
getTask(taskId)GET /system/tasks/:id

5.2 端点表

方法路径请求体响应要点
GET/api/v1/datasources{code:0, data:{data_sources:[...]}}(skip/limit 后端取 0/100)
POST/api/v1/datasources{name, type, connection_config:{host,port,user,password,database,description,active}}201 {code:0, data:ds}
GET/api/v1/datasources/:id{code:0, data:ds}
PUT/api/v1/datasources/:id{name, type, connection_config:{...}}(全字段更新语义){code:0, data:ds}
DELETE/api/v1/datasources/:id{code:0, data:{deleted:true}}
POST/api/v1/datasources/:id/test完整连接配置(body 同创建){code:0, data:{...连接测试结果}}
POST/api/v1/datasources/:id/import-metadataquery: ai_generate_descriptions200 快路径 data={...结果字段, task_id};202 data={task_id, status:'running', type:'foundry_import_metadata'}
GET/api/v1/system/tasks/:iddata={id,status,progress,message,result,error,...}

5.3 响应结构

数据源列表响应:

{
  "code": 0,
  "data": {
    "data_sources": [
      {
        "id": 1,
        "name": "sales_data_warehouse",
        "type": "POSTGRESQL",
        "connection_config": {
          "host": "localhost", "port": 5432, "user": "postgres",
          "password": "****", "database": "chatbi_forge_dev",
          "description": "销售数仓", "active": true
        }
      }
    ]
  }
}

导入元数据 202 受理响应:

{ "code": 0, "data": { "task_id": "task-uuid", "status": "running", "type": "foundry_import_metadata" } }

任务轮询响应(GET /system/tasks/:id):

{
  "code": 0,
  "data": {
    "id": "task-uuid", "type": "foundry_import_metadata",
    "status": "success", "progress": 1.0, "message": "元数据导入完成",
    "result": { "tables": 12, "columns": 108 }, "error": "", "finished_at": "..."
  }
}

5.4 关联模块表

后端包职责
platform/datasourceDataSourceService(CRUD/TestDataSourceConnection)+ MetadataGenerationService(GenerateMetadataFromDataSource)+ ConnectorCacheService
products/foundry/server(datasource_handlers.go)Foundry 侧数据源全套路由、任务化提交、{code:0,data} 包装
platform/task异步任务系统:foundry_import_metadata 任务类型、进度上报、终态判定
platform/connector连接器工厂(allow-list 白名单:默认仅放行 MYSQL/SQLSERVER/POSTGRESQL)
products/foundry/server(server.go)registerDatasourceRoutes 挂 protected 组、task.RegisterRoutes 挂 /system、bootstrap AutoMigrate

5.5 关键机制

数据源路由解耦(B1-6):Foundry 独立进程 + 独立库,datasources 表在 foundry 库经 bootstrap AutoMigrate 建立;复用 platform/datasource 服务构造(NewDataSourceServiceWithCache 带 5 分钟连接缓存)。AIP 停机不影响本页。

导入元数据任务化importMetadata handler 将 GenerateMetadataFromDataSource 包进 TaskManager.Submit(type=foundry_import_metadata,payload 含 data_source_id / ai_generate),15s 窗口内完成保持旧同步响应语义(结果字段 + task_id),超时返回 202 {task_id, status:'running', type}taskMgr 为 nil 或 Submit 失败时降级同步直执行。前端 handleImportMetadata 判断:payload.status === 'running' && payload.task_id 则弹窗进度条 + pollTask 轮询(1.5s,POLL_INTERVAL_MS=1500),否则直接 showResult 展示快路径结果。

任务轮询(pollTask)pollTimersetTimeout 链式调用而非 setInterval;单次轮询失败(catch)不中断、继续 setTimeout(tick, 1500) 重试;弹窗关闭(resultModal.visible=false)即 stopPolling;终态 success 时展示 {task_id, status:'success', result}fetchDataSources 刷新列表,failed/cancelled 时展示 {task_id, status, error: task.error || task.message || '任务未成功'}

密码打码回显:后端对密码字段回显打码为 ****;编辑时前端把密码留空(dsForm.password = ''),提交空串表示不改动(后端保留原密码)。

类型校验与连接器白名单:创建/更新时后端校验 name 必填、type 必填且为合法枚举(大小写不敏感);type 白名单含 POSTGRESQL/MYSQL/SQLSERVER/SQLITE/ORACLE/CLICKHOUSE/SNOWFLAKE/REDSHIFT/BIGQUERY/DATABRICKS/TRINO/DB2/OCEANBASE/TIDB。但能否真正建连取决于 connector 工厂 allow-list 授权(默认仅放行 MYSQL/SQLSERVER/POSTGRESQL,其余需 AllowAdvancedTypes 显式授权)。

6. 核心流程详解

6.1 新建 / 编辑数据源

  1. 点击「新建数据源」展开表单(showCreateForm = true)。
  2. 填写数据源名称(必填)、数据库类型(必填)、主机/端口/数据库名/用户名/密码/描述。
  3. 点击「创建数据源」(submitting=true 显示「提交中...」):组装 payload {name, type, connection_config:{host, port, user, password, database, description, active:true}}POST /datasources
  4. 成功提示「数据源创建成功」,resetForm() 收起表单并 fetchDataSources 刷新列表。
  5. 编辑模式:点「编辑」startEdit(ds) 把数据源字段回填表单(editingId = ds.id,密码置空),标题切「编辑数据源」;点「更新数据源」PUT /datasources/:id,成功提示「数据源更新成功」。

6.2 测试连接

  1. 点击行内「测试连接」。
  2. 前端组装 payload:{name: ds.name, type: ds.type, connection_config: ds.connection_config || {}}body 需完整连接配置,后端按 body 建连测试,而非仅用库内记录)。
  3. POST /datasources/:id/test → 成功 showResult('测试连接结果', JSON.stringify(resp?.data ?? resp, null, 2)) 弹窗展示;失败走 alert「测试连接失败:错误详情」。

6.3 导入元数据(任务化主流程)

  1. 可选:打开「导入时 AI 生成描述(调用 LLM,较慢)」开关(绑定 aiGenerate)。
  2. 点击行内「导入元数据」(按钮变为「导入中...」,importingId = ds.id 期间本行测试/导入按钮禁用)。
  3. importMetadata(ds.id, aiGenerate) 带 query ai_generate_descriptions=true/false 发请求:
  1. 轮询循环(1.5s setTimeout 链):getTask(taskId) → 更新 progress(0~1)/progressMessagestatus==='success' → 展示结果 JSON、stopPollingfetchDataSources 刷新;status==='failed'||'cancelled' → 展示错误 JSON、stopPolling;否则继续下一轮。
  2. finallyimportingId = null 恢复按钮。

6.4 删除数据源

confirm('确定要删除数据源"xxx"吗?此操作不可撤销。') 二次确认后 DELETE /datasources/:id,成功提示「数据源"xxx"已删除」并刷新列表。

7. 权限与安全

7.1 认证

7.2 数据级安全

7.3 写操作防护

8. 常见问题与排错

8.1 测试连接失败

  1. 读取 alert 或弹窗中的具体错误文本,区分网络不可达 / 认证失败 / 类型不支持;
  2. 核对表单中的 host/port/database/user/password 与目标库实际配置是否一致(注意测试连接按请求体配置建连,若编辑后未保存,请求体用的仍是列表里的 connection_config);
  3. 若报类型不支持,确认该类型是否在 connector 工厂 allow-list(默认仅 MYSQL/SQLSERVER/POSTGRESQL,SQLite 等需 AllowAdvancedTypes 显式授权);
  4. 用数据库客户端手工连接一次,排除库侧问题(账号锁、白名单 IP 等)。

8.2 导入元数据后 RAG / 查询仍按旧表名命中

  1. 确认本次导入任务终态为 success,且 result 中 tables/columns 数量符合预期;
  2. 在目标数据库确认表结构确实已变更(如删表/改列);
  3. 重新点击「导入元数据」触发全量重建(GenerateMetadataFromDataSource regenerateAll 传 false,但导入逻辑会按 table_schema_id 清旧后重建);
  4. 若检索侧仍有旧表名,检查 RAG 向量库是否需要重建索引(元数据入库后需触发对应索引刷新)。

8.3 导入元数据任务一直转圈 / 进度条不动

  1. 观察进度百分比与 message 是否在变化——在变说明任务在推进,耐心等待(AI 描述生成较慢);
  2. 不变则打开 DevTools Network 看 GET /system/tasks/:id 响应 status 是否仍为 running/queued;
  3. queued 长期不变说明 worker 未取走——检查后端日志是否有「异步任务提交失败,降级同步执行」告警;
  4. 确认弹窗标题中的 task_id 与 Network 中请求的 task_id 一致(避免旧轮询残留);必要时关闭弹窗重新点击「导入元数据」。

8.4 勾选 AI 生成描述但导入报错

  1. 查看错误文本是否为"LLM 服务未配置 / 不可用"类;
  2. 若需要 AI 描述,先配置 LLM 网关(.env 中的 deepseek / dashscope key)并重启 Foundry;
  3. 不需要 AI 描述时取消勾选后重新导入,确认普通导入正常。

9. 已知缺陷与边界

缺陷/边界说明
LLM 未装配时 AI 描述不可用ai_generate_descriptions=true 时元数据导入明确报错(诚实降级),需先配置 LLM 网关
双凭证推迟只读/写回双凭证建模推迟至 S5 Markings 批次评估,本批以独立数据源记录满足"解耦"
连接器 allow-list 限制默认仅放行 MYSQL/SQLSERVER/POSTGRESQL,SQLite/Oracle/ClickHouse 等需 AllowAdvancedTypes 显式授权
列表固定取 100 条GetAllDataSources(skip=0, limit=100),数据源超过 100 个时列表不完整(无分页控件)
前端表单无类型回显联动编辑时类型下拉直接回填 ds.type,未做 host/port 默认值联动
导入任务 result 仅展示快路径/轮询结果以 JSON 文本展示,不做图表化或落库状态梳理
更新为全字段语义PUT 时 name/type 必填,漏传会 400;密码留空表示不改(后端特殊处理)

10.2 后端文件

10.3 项目文档

10.4 相邻页面