项目进展2026-09-17

志睿源官网|产品开源仓库链接 上线记录

产品详情页新增「开源仓库」区块:已开源的 5 个产品均可一键跳转 GitCode / Gitee 仓库,源码公开可查;侧栏入口与主体区锚点同步上线。

时间:2026-09-17 | 范围:产品详情页「开源仓库」区块 状态:已上线,本地与生产双向验收通过


一、需求与决策

老板原话:对官网上产品介绍里,或其他栏目,有关于我们开源项目的,要把开源的 gitee、gitcode 链接放出来。

三个决策点已确认:

决策选择
开源范围已开源的 5 个产品 + web-spa-demo 挂到 Fun.js(文案里注明它不是 Fun.js 框架本体)
展示位置只挂产品详情页(侧栏区块 + 主体区入口)
数据方式后台结构化字段,改完即时生效 —— 与 CMS 的数据驱动口径一致,老板可自行维护

二、仓库与产品的对应关系

已开源账号下的仓库,与站上产品的对应关系如下(17 条地址全部实测 HTTP 200,零死链):

站点产品仓库双平台
showtext.jsshowtext✅
nagentnagent-remote✅
nagentnagent(早期版本,仅 Gitee)单平台
MD 阅读器md-reader-web✅
MD 阅读器harmonyos-md-reader✅
MD 阅读器harmonyos-md-reader-lite✅
h5game 桌游辅助h5game-assistant✅
Fun.jsweb-spa-demo(参考实现,框架本体未开源)✅
官网 CMS 建站平台zry-cms✅

另有 3 个仓库不对应站上产品,本次未上站:bzm-webframework(自研 Node 全栈框架,仅在 CMS 正文里被提及)、encode-tools、node-web-server。


三、上线内容

数据字段

产品表新增 repos 字段,每行一条,格式:

名称 | 说明 | GitCode 地址 | Gitee 地址

说明与任一平台地址可留空 —— nagent 这类只在单平台开源的仓库照样能挂。后台产品编辑表单里可视化维护,改完刷新产品页即时生效。

前台呈现

  • 侧栏「开源仓库」区块:每个仓库显示名称、一句说明,下面两条链接;链接直接显示去掉协议头的完整地址(如 gitcode.com/xxx/yyy),访客能一眼看到地址、也能选中复制,而不是只看到一个平台名。
  • 主体区入口:标题下方状态行加「开源仓库 N 个 →」,点击锚到侧栏区块。
  • 没有仓库的产品整块不渲染,不会出现空壳区块。

安全处理

链接由前台直接输出到 href,因此在后端先做协议白名单 —— 只放行 http(s),javascript: / data: 一律丢弃;链接里也不允许出现引号与尖括号。前端再用 safeUrl() 做第二道防线,外链统一带 rel="noopener noreferrer"。

这一点不是形式主义:后台字段是人填的文本,若不做白名单,一条 javascript: 链接就能在访客浏览器里执行脚本。

文案守住事实

入口写的是「开源仓库 N 个」而非「已开源」。Fun.js 挂的是相关仓库(原生 JS SPA 参考实现),框架本体并未开源,措辞不能替产品打包票 —— 它的仓库说明里已明确写出这一点。

顺手去掉的重复

CMS 产品正文里原本手工写了一张「开源仓库」表格。侧栏区块上线后正文再列一遍就是重复,已把表格移除、保留原有的脱敏说明段落,链接统一由侧栏承载。


四、验收

测试

项目结果
来访统计解析31 / 31
采集与判定流水线53 / 53
跨文件契约103 / 103(原 79 条 + 本次新增 24 条)
静态资源引用SUCCESS
资源版本号对账SUCCESS(153 处引用、140 处去重全部一致)

本次新增的 24 条契约断言专门锁住这条链:后台表单字段 → 后端写库白名单 → JSON 序列化 → 后端读出口 → 前端消费点 → SQL 建列与数据。因为这条链上任何一处漏了都是静默失效(后台存了但前台不显示,或前台显示了但后台存不进去),光靠人眼审代码很容易漏。

断言做过负向验证:把后端白名单摘掉、把前端 safeUrl() 摘掉,断言各自失败 1 条 —— 证明它们真的能抓到回归,而不是摆设。

页面验收(CDP 实机,本地 + 生产各跑一遍)

  • 6 个有仓库的产品:区块、仓库数、链接数、href 指向、target、rel、链接文本、等宽字体、折行全部正确
  • 反例:无仓库的产品不出现区块与入口
  • 窄屏 390px:MD 阅读器(3 个仓库、地址最长)横向溢出 0px
  • 主体区入口点击后正确锚到区块
  • 后台表单字段:显示正常、回显 3 段数据、格式提示完整
  • 全程 console 零报错

地址核验

从生产接口导出全部 17 条仓库地址逐条访问,全部 200。


五、🔴 顺带发现:数据库连接泄漏(长期潜伏,与本次改动无关)

现象

生产接口日志里反复出现成对报错:

[DBM] getConnection attempt 1 failed: 获取数据库连接超时(5000ms)
[DBM] getConnection attempt 2 failed: Cannot read properties of null (reading 'getConnection')

站点表现为最长约 10 秒的接口失败窗口,然后自愈(下一个请求重建连接池)。

这不是本次改动引入的:改前一天的日志里就有完全相同的错误,属于长期潜伏缺陷,只是被本次的并发验收暴露出来。

定量证据

  • 连接池上限 10,实测被打到 10 条全部占用、可用 0 条
  • 近 3000 行日志统计:207 次借出,63 条连接借出后从未归还(每条都是「借 1 次、还 0 次」)
  • 同期 101 次「获取数据库连接超时」

根因(两处,都在框架的数据库管理层)

① 超时分支把连接漏掉了。 取连接用的是「业务取连接」与「5 秒超时」赛跑。超时赢下之后,业务侧那条取连接的动作仍会继续并最终拿到一条连接,但已经没有人接住它、也就永远不会归还 —— 每超时一次泄漏一条。

后果是自增强的:一次变慢 → 漏掉一条 → 池容量变小 → 更容易超时 → 直到 10 条全漏光。

② 重试时连接池已被置空,却直接又去用它。 重试用尽后代码销毁连接池并置空,但重试循环内部没有重新初始化,于是第二次尝试直接在空对象上取连接,抛出 Cannot read properties of null。

建议补丁

两处都是很小的改动:超时后把迟到兑现的那条连接归还回池(并在成功时清掉定时器);重试循环体第一步先确保连接池已初始化。

为什么没有直接动手:这个文件是框架公共文件,站点每一个请求都要经过它,改错的后果是全站不可用;且需先确认是否被其他进程共用。风险与影响面都超出本次「加链接」的范围,交由您批示后再改。

建议

在您批示前,可以先做一件低风险的事:给数据库连接数加一个监控(当前 16 / 上限 100,还有余量),一旦「借出未归还」的差值持续上涨就能提前发现,而不必等到接口报错。


六、部署与回滚

项值
上传文件18 个(16 个前端含版本号 + 2 个后端 + 1 个 SQL 脚本)
完整性服务器端与本地 双向 md5 全部一致
数据库升级脚本 20 条语句执行成功,复跑验证幂等
服务接口进程重启,6 个产品接口返回正确
缓存构建号已变更,老访客缓存会正确失效;版本号与文件内容哈希逐一对应
回滚点服务器 /data/_bak/ 保留本次改动前的前端包与后端包各一份

回滚方式:解包对应备份即可(SQL 只加了字段与数据,不回滚也不影响旧代码运行)。


七、待批示

  1. 数据库连接泄漏是否现在修 —— 建议修(两处小改动,但需您确认可以动框架公共文件)
  2. 是否需要把「已开源」做成更醒目的入口(例如产品列表卡片加标记、页脚加一行「开源仓库」)—— 本次按您的选择只挂了详情页
← 返回文章列表