1. 页面概览

1.1 是什么

代码仓库(CodeRepo)是 LightFoundry 的 SQL 模板/宏复用能力(V5 Stage 6,B5-2),对应 CodeRepoPage.vue。它以三张表(fcr_repos / fcr_files / fcr_revisions)承载三类仓库(snippet 片段 / template 模板 / macro 宏),提供仓库管理、文件树 + 编辑器、版本历史与宏管理四块能力。P0 范围刻意不做 git 协议,采用「同 (repo_id, path) 新版本覆盖」的简化版本日志:文件内容更新时旧内容写入 fcr_revisions 归档、文件版本 +1,最新内容始终在 fcr_files

页面的特色在「宏」:宏仓库(repo_type=macro)中每个文件即一个宏(path=宏名),SQL 中出现 {{macro:name}} 时在 SQL 工作台 Execute 前由后端自动展开(宏表优先,未命中原样传,最多迭代 maxMacroDepth=5 层防循环引用)。本页提供「宏展开调试」验证展开结果。

1.2 核心价值

能力说明
三类仓库snippet(代码片段)/ template(SQL 模板)/ macro(宏定义),名称全局唯一
文件版本覆盖保存同 path 新版本覆盖(version+1),旧内容自动归档进版本历史;内容未变仅更新元信息不产生新版本
版本历史与恢复按 version 倒序列出归档版本,可查看内容、一键恢复(当前内容作为新版本归档)
宏自动展开SQL 中 {{macro:name}} 在 Execute 前展开;宏表优先、未命中原样传、最多 5 层迭代
宏展开调试本页直接 POST /coderepo/macros/resolve 验证宏引用结果
五种文件语言sql / python / js / yaml / go,默认 sql

1.3 一句话总结

代码仓库页是 Foundry 的轻量代码资产库——仓库管类型、文件管版本、宏管复用,{{macro:name}} 一句引用即可把通用 SQL 片段复用到任意工作台查询。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

端口:18081(Foundry)。API 前缀:/api(baseURL /api/v1),如 GET /api/v1/coderepo/reposPOST /api/v1/coderepo/macros/resolve

3. 界面布局

代码仓库
└─ 提示条(alert,右上角「关闭」)
└─ 卡片「仓库列表」(共 N 个)
   ├─ 按钮「+ 新建仓库」/「收起」
   ├─ 新建/编辑表单(名称 * / 类型 * / 描述 + 保存/取消)
   ├─ 搜索行:搜索仓库(名称/描述/类型)+「共 N 个,命中 M 个」
   ├─ 表格:名称 / 类型 / 描述 / 文件数(按需载入)/ 操作(编辑/删除)
   ├─ 本地分页:上一页 / 第 x / y 页(共 N 条)/ 下一页(仅命中数 > 每页 10 条时显示)
   └─ 诚实提示:仓库列表接口无搜索/分页参数、文件数按需查询
└─ [选中仓库] 三栏网格(340px 文件树 + 编辑器 + 版本历史)
   ├─ 左:{{name}} · 文件(N 个)+ 「+ 新建文件」+ 文件列表(语言/路径/版本 + 编辑/历史/删除)
   ├─ 中:编辑器(路径 disabled / 语言下拉 / 内容 textarea + 保存(vN+1))
   └─ 右:版本历史(N 条归档 + 查看/恢复 + 归档预览)
└─ 卡片「宏管理」
   ├─ 左:宏清单(当前 macro 仓库的宏文件)
   └─ 右:宏展开调试(textarea + 「展开预览」+ 展开结果/错误)

各板块职责:

4. 交互元素详解

4.1 仓库列表与表单

元素含义必填/默认操作效果后端调用
按钮「+ 新建仓库」/「收起」展开/收起仓库表单展开时重置表单(emptyRepoForm,类型默认 snippet)
输入「名称 *」仓库名称,全局唯一必填placeholder「如 销售 SQL 模板」POST /coderepo/repos
下拉「类型 *」仓库类型必填,默认 snippetsnippet 片段 / template 模板 / macro 宏同上
输入「描述」用途说明选填随 payload 提交同上
按钮「保存」(busy「提交中...」)提交新建/更新新建走 POST 并自动选中新仓库;编辑走 PUT;成功后刷新列表POST /coderepo/repos、PUT /coderepo/repos/:id
按钮「取消」收起表单仅关闭表单
行内「编辑」填表进入编辑态复用表单,repoForm.id 有值 → PUT
行内「删除」删除仓库confirm「确定删除仓库"xxx"吗?其全部文件与版本历史将一并删除。」;若删除当前仓库同时清空文件/编辑器态DELETE /coderepo/repos/:id
输入「搜索仓库」名称/描述/类型纯前端过滤输入即过滤 repos,并显示「共 N 个,命中 M 个」;无匹配显示「没有匹配的仓库。」无(前端过滤)
行内「载入」(文件数列)按需查询该仓库文件数点后请求该仓库文件列表并回填数量(加载中「查询中...」);选中该仓库后自动已知GET /coderepo/repos/:id/files
「上一页」/「下一页」仓库列表本地分页(每页 10 条)setRepoPage 切片 filteredRepos无(纯前端切片)

仓库列表列:名称(link 样式点击选中)、类型(snippet 蓝 / template 绿 / macro 橙 tag)、描述、文件数、操作。空态「暂无仓库(后端可能未就绪)。」。

文件数按需加载(消除首屏 N+1)fetchRepos 只请求 GET /coderepo/repos不再对每个仓库追加一次 GET /coderepo/repos/:id/files。文件数单元格逻辑:选中仓库(selectRepoloadFiles)已取回其文件列表,直接显示文件数(免额外请求);其余仓库显示「载入」链接按钮,点 loadFileCount(r) 才按需请求该仓库文件列表并回填数量,加载中显示「查询中...」;后端无批量文件计数端点(见 5.5),故无法一次取回全部计数;按需加载后首屏只有 1 个请求。

搜索与分页(渲染层)fetchRepos 取回全量后,filteredRepos 按名称/描述/类型做纯前端关键词过滤(大小写不敏感),pagedRepos 按固定每页 10 条切片;分页条仅在命中数 > 10 时显示。搜索/分页均无后端调用(GET /coderepo/repos 不接受查询参数,见 5.2)。

4.2 文件树与编辑器

元素含义必填/默认操作效果后端调用
按钮「+ 新建文件」显示文件路径表单emptyFileForm(语言默认 sql)
输入「文件路径 *」仓库内唯一路径必填placeholder「如 monthly_report.sql」;同 (repo_id,path) 已存在返回 DUPLICATE_ENTRYPOST /coderepo/repos/:id/files
文件行语言 tag + path + v{version}点击或「编辑」打开到编辑器
「历史」打开版本历史加载该文件归档GET /coderepo/files/:id/revisions
「删除」删除文件confirm「确定删除文件"xxx"吗?其版本历史将一并删除。」DELETE /coderepo/files/:id
输入「路径 path」当前文件路径编辑时禁用只读展示
下拉「语言 language」文件语言默认 sqlsql / python / js / yaml / goPUT /coderepo/files/:id
文本域「内容 content(保存时新版本覆盖,旧内容进版本历史)」文件内容placeholder「SQL 模板 / 宏内容 / 代码片段...」同上
按钮「保存(vN+1)」提交保存保存后 alert「已保存,新版本 v{version}」并刷新文件树PUT /coderepo/files/:id
按钮「关闭」退出编辑清空 currentFile 与表单

4.3 版本历史与宏管理

元素含义操作效果后端调用
版本历史列表归档版本(version 倒序)每行显示 v{n} + 内容前 40 字 + 操作者/时间GET /coderepo/files/:id/revisions
「查看」预览归档内容下方 sample-box 展示
「恢复」恢复为归档内容confirm「确定将文件恢复到 v{n} 吗?(当前内容将作为新版本归档)」;恢复后当前内容以新版本归档PUT /coderepo/files/:id
宏清单当前 macro 仓库的宏文件文件路径即宏名,显示 macro tag + path + 内容前 60 字
「宏展开调试」区输入 SQL 含 {{macro:name}}点「展开预览」POST 解析,结果/错误展示POST /coderepo/macros/resolve
按钮「展开预览」/「展开中...」触发宏展开成功显示「展开结果:」,失败红字错误POST /coderepo/macros/resolve

5. 后端关联

5.1 API 客户端

本页使用 action/web/src/api/client.jsbaseURL: '/api/v1'timeout: 30000、aip_token Bearer 注入、401 跳登录),无自定义导出函数。

5.2 端点表

方法路径(前缀 /api/v1)请求体用途
GET/coderepo/repos—(无搜索/分页参数仓库列表(created_at 倒序,全量返回)
POST/coderepo/repos{name, description, repo_type}创建仓库(name 唯一,类型合法)
GET/coderepo/repos/:id仓库详情(含文件列表)
PUT/coderepo/repos/:id{name?, description?, repo_type?}更新仓库(指针语义)
DELETE/coderepo/repos/:id删除仓库(级联文件+版本历史)
GET/coderepo/repos/:id/files仓库文件列表(path 升序)
POST/coderepo/repos/:id/files{path, content, language}添加文件(同 repo+path 重复报 DUPLICATE_ENTRY)
GET/coderepo/files/:id文件详情
PUT/coderepo/files/:id{content?, language?}更新文件(content 变化 version+1 并归档旧版)
DELETE/coderepo/files/:id删除文件(级联版本历史)
GET/coderepo/files/:id/revisions版本历史(version 倒序)
POST/coderepo/macros/resolve{sql}宏展开调试,返回 {sql}

5.3 响应结构

仓库列表 GET /api/v1/coderepo/repos

{ "code": 0, "data": { "repos": [
  { "id": "uuid", "name": "销售 SQL 模板", "description": "月度报表", "repo_type": "template",
    "owner": "u1", "created_at": "..." }
], "total": 1 } }

文件列表 GET /api/v1/coderepo/repos/:id/files

{ "code": 0, "data": { "files": [
  { "id": "uuid", "repo_id": "uuid", "path": "monthly_report.sql",
    "content": "SELECT ...", "language": "sql", "version": 3,
    "updated_by": "u1", "updated_at": "..." }
], "total": 1 } }

宏展开 POST /api/v1/coderepo/macros/resolve 请求与响应:

{ "sql": "SELECT {{macro:amount}} FROM orders" }
→ { "code": 0, "data": { "sql": "SELECT COALESCE(amount,0) FROM orders" } }

5.4 关联模块表

后端包/文件职责
foundry/coderepo/service.goRepo/File CRUD、版本归档、ResolveMacros 宏展开
foundry/coderepo/models.gofcr_repos / fcr_files / fcr_revisions 模型与常量
foundry/coderepo/rest.go路由注册与 handler(maxJSONBodyBytes 1MB)
foundry/querySQL 工作台 Execute 前由接线挂 ResolveMacros(本包不触碰 query 内部)

5.5 关键机制

6. 核心流程详解

6.1 主流程:定义宏并在 SQL 中复用

  1. 建宏仓库:点「+ 新建仓库」,名称填如「公共宏」,类型选 macro 宏,保存。
  2. 加宏文件:进入仓库(自动加载文件树),点「+ 新建文件」,路径填 amount(= 宏名),内容填如 COALESCE(amount,0),语言 sql,创建。文件创建成功提示「文件创建成功 v1」。
  3. 调试展开:在「宏展开调试」输入 SELECT {{macro:amount}} FROM orders,点「展开预览」,得到 SELECT COALESCE(amount,0) FROM orders
  4. 工作台复用:在 SQL 工作台执行含 {{macro:amount}} 的 SQL,Execute 前自动展开。
  5. 迭代更新:编辑器修改宏内容并「保存(v2)」,旧内容进版本历史;右侧历史可查看 v1 并可「恢复」。

6.2 版本历史管理

  1. 文件树点「历史」→ GET /coderepo/files/:id/revisions 按版本倒序列出归档。
  2. 「查看」预览某版本内容;「恢复」将该版本内容写回(当前内容先归档为新版本)。
  3. 未覆盖保存过的文件无历史(「暂无历史版本(尚未覆盖保存过)。」)。

6.3 状态与终态语义

文件版本为线性递增(1,2,3...),最新版本常驻 fcr_files,历史版本在 fcr_revisions;恢复操作不覆盖原版本号而是「当前内容归档为 vN+1、目标内容成为 vN+2」。宏展开为同步请求(30 秒 timeout),macroBusy 期间按钮禁用;展开失败红字显示 error 并保留输入。

7. 权限与安全

8. 常见问题与排错

8.1 创建文件报「文件已存在」

现象:新建文件返回 DUPLICATE_ENTRY「文件已存在: 仓库/路径」。原因:同 (repo_id, path) 已存在;文件版本覆盖走「编辑保存」(PUT)而非新建。排查步骤:1) 确认该路径已存在于文件树;2) 若要更新内容,在文件树点「编辑」打开到编辑器后「保存(vN+1)」;3) 若确认要新文件,改用不同的 path。

8.2 保存文件后版本不变

现象:编辑后保存,alert 提示新版本但 v{version} 未 +1。原因:UpdateFile 仅在 content 实际变化时归档+升版;只改语言或内容未变时仅更新元信息。排查步骤:1) 确认内容确实发生了变化;2) 只改语言不升版属预期;3) 用「历史」确认归档是否新增。

8.3 宏展开后 {{macro:name}} 原样保留

现象:点「展开预览」结果中宏引用未替换。原因:宏名未命中任何 macro 仓库文件(未命中原样传是设计语义);或宏仓库类型不是 macro。排查步骤:1) 确认存在 repo_type=macro 的仓库;2) 确认该仓库内有 path=宏名的文件(文件路径即宏名,注意大小写与空格);3) 确认引用语法为 {{macro:name}}(name 仅允许字母/数字/下划线/点/横线);4) 展开调试输入与宏内容逐一比对。

8.4 宏展开死循环或异常中断

现象:宏 A 引用宏 B、宏 B 又引用宏 A 时,展开结果停留在第 5 层不再替换。原因:maxMacroDepth=5 的迭代上限触发(防循环引用的兜底,不做错误上报)。排查步骤:1) 确认这是预期的安全机制而非错误;2) 检查宏定义是否存在循环引用(宏内容里引用了自身或互相引用);3) 修正宏内容避免循环;4) 若业务确实需要更深嵌套,需后端调整 maxMacroDepth 常量。

8.5 仓库删除后仍在页面

现象:删除仓库后列表仍显示或编辑器仍显示旧文件。原因:删除的是非当前仓库时前端只刷新列表;删除当前仓库时前端已清空 currentRepo/files/currentFile。排查步骤:1) 删除当前仓库后前端应清空选中态——若未清空,刷新页面确认;2) 直接调用 GET /api/v1/coderepo/repos 验证后端已删;3) 若为缓存,重新加载页面。

9. 已知缺陷与边界

项目说明
不做 git 协议P0 范围刻意简化:无分支/合并/diff/多人协作,仅线性版本覆盖
版本恢复是「新版本」恢复归档内容会生成新版本号,不覆盖既有历史
宏未命中原样传拼写错误不报错,仅靠调试工具发现
maxMacroDepth=5循环引用在 5 层后静默截断,无显式告警
文件计数无批量端点后端无一次返回多仓文件数的聚合端点(coderepo/service.go:86-92rest.go:148-157),前端已改为按需加载(选中仓库自动载入或点「载入」);未载入的仓库文件数显示「载入」链接而非数量
列表无搜索/分页参数GET /coderepo/repos 无查询参数(coderepo/service.go:86-92rest.go:62-71),仓库搜索/分页为前端渲染层实现,仍一次全量拉取;文件树仍全量渲染(未加分页)
内容字节比较content 完全相等时不产生新版本(仅元信息更新)
body 1MB 上限decodeJSON 限制,超大文件/宏内容保存会被拒绝