自研缺陷跟踪管理系统:基于 restina-framework 与 Htmx + Alpine.js 的轻量级实践

一、为什么我们要自研?

市面上成熟的缺陷管理工具并不少——Jira、禅道、PingCode、Redmine……那为什么还要自己造轮子?

原因很现实:

成本:Jira 按人头收费,团队一扩张,账单就让人肉疼。 

冗余:很多工具功能庞大,但团队真正用到的可能不到 30%,剩下的全是”噪音”。

定制自由:自己的系统,流程怎么改、字段怎么加、权限怎么配,说了算。

于是,我决定自研一套轻量、可控、可扩展的缺陷跟踪管理系统。后端采用自研的 restina-framework,前端则走了一条”反主流”的技术路线。

二、技术选型:不走寻常路

后端:restina-framework

restina-framework 是我自研的 RESTful 后端框架,核心设计理念是轻量、约定优于配置、开箱即用。它承担了以下职责:

RESTful API 网关:统一路由分发、请求鉴权、错误处理。

JWT 认证与会话管理:基于 Token 的无状态认证,支持自动续期与 401 自动跳转。

RBAC 权限控制:细粒度的角色-资源-操作权限矩阵。

文件签名服务:对接阿里云 OSS,生成临时上传凭证,实现前端直传。

业务逻辑层:Bug 生命周期管理、项目协作、用户管理、数据统计等核心能力。

前端:Alpine.js + htmx + Tailwind CSS

是的,你没有看错——没有 Vue,没有 React,没有 Webpack。我们选择了一套”极简主义”前端技术栈:

技术角色为什么选它
Alpine.js响应式组件框架轻量(~15KB)、声明式、无需构建步骤,直接在 HTML 中写逻辑
htmx局部刷新用 HTML 属性驱动 AJAX,替代传统 SPA 的复杂状态管理
Tailwind CSS原子化样式无需写 CSS 文件,直接在 class 中组合样式,开发效率极高
Hash 路由页面导航简单可靠,无需服务器端配置 fallback

这套组合的核心优势是:零构建、零编译、零依赖地狱。开发者只需要一个浏览器和一个文本编辑器就能开始写代码。对于内部工具来说,这种”返璞归真”的架构反而更高效。

三、系统架构总览

整体采用轻量级 SPA(单页应用)架构,结构清晰,职责分明:

index.html(入口 + 全局布局)
├── src/js/
│   ├── app.js        → API 客户端、路由、全局事件、鉴权拦截
│   ├── pages.js      → 各页面的 Alpine 组件(登录、仪表盘、Bug 列表/详情等)
│   ├── utils.js      → 工具函数(标签渲染、时间格式化、Toast 通知等)
│   └── oss-upload.js → 阿里云 OSS 直传封装
└── src/pages/        → 页面 HTML 模板(按需加载)
    ├── login.html
    ├── dashboard.html
    ├── bugs/list.html
    ├── bugs/detail.html
    └── ...

请求流程

用户访问 #/bugs
    → 路由匹配模板路径
    → fetch 加载 bugs/list.html 注入 DOM
    → 初始化 Alpine 组件 bugListPage()
    → 调用 api.get('/bugs') 获取数据
    → 渲染列表 + 筛选 + 分页

鉴权流程

登录 → POST /auth/login → 获取 JWT Token
    → 存入 localStorage
    → 后续请求自动注入 Authorization Header
    → 401 响应 → 清理本地状态 → 跳转登录页

四、核心功能模块

1. 用户认证系统

登录流程:用户名密码登录,后端验证后返回 JWT Token 与用户基本信息。

Token 管理:Token 持久化存储在 localStorage,每次请求自动携带。

RBAC 权限:系统内置管理员、项目经理、开发人员、测试人员等角色,不同角色看到的功能菜单与操作按钮不同。

会话管理:Token 过期自动检测,401 响应触发全局重定向到登录页。

2. Bug 管理系统

这是系统的核心模块,覆盖缺陷的完整生命周期

Bug 列表:支持关键词搜索、状态筛选、优先级筛选、分页浏览。

Bug 创建/编辑:弹窗表单,支持标题、描述、项目关联、优先级、严重程度、指派人员等字段。

Bug 详情:基本信息、富文本描述、元数据、附件列表、评论交流。

状态流转:新建 → 已指派 → 处理中 → 已解决 → 已验证 → 已关闭,驳回操作需填写理由。

附件上传:集成阿里云 OSS 直传,支持图片预览、文件大小格式化、上传进度展示。

3. 数据统计仪表盘

统计卡片:待处理 Bug 数、今日新增、平均修复时长、严重缺陷占比等关键指标。

待办列表:根据用户角色动态展示”我创建的”、”指派给我的”、”待验证的”等任务。

趋势图表:Bug 新增/关闭趋势图,帮助团队掌握质量走势。

4. 项目与用户管理

项目管理:项目的 CRUD,成员关联,Bug 与项目的绑定。

用户管理:用户列表、搜索、创建/编辑/删除、角色分配,仅管理员可见。

5. 实时通知系统

基于 htmx 的局部刷新机制,配合后端 WebSocket 或轮询,实现 Bug 状态变更、新指派任务等实时提醒。

五、设计亮点

亮点一:OSS 直传,减轻服务器压力

传统的文件上传流程是”前端 → 后端服务器 → OSS”,文件经过后端转存,既消耗带宽又占用服务器资源。我们采用前端直传 OSS 的方案:

1、前端请求后端获取 OSS 签名(GET /upload/signature)。

2、前端通过 XMLHttpRequest 直接将文件上传到阿里云 OSS。

3、上传成功后,调用后端接口记录附件信息(POST /bugs/:id/attachments)。

这样后端只负责签发临时凭证和记录元数据,文件流量完全不经过应用服务器。

亮点二:Alpine.js 组件化开发

每个页面对应一个 Alpine 组件函数,职责单一,数据与方法分离:

// pages.js 中的 Bug 列表组件
function bugListPage() {
    return {
        bugs: [],
        filters: { keyword: '', status: '', priority: '' },
        pagination: { page: 1, per_page: 20, total: 0 },
        
        init() { this.loadBugs(); },
        
        async loadBugs() {
            const params = new URLSearchParams({ ...this.filters, ...this.pagination });
            const res = await api.get(`/bugs?${params}`);
            this.bugs = res.data;
            this.pagination.total = res.total;
        },
        
        async updateStatus(bugId, status) {
            await api.patch(`/bugs/${bugId}/status`, { status });
            this.loadBugs();
            showToast('状态更新成功', 'success');
        }
    };
}

模板中通过 x-data="bugListPage()" 直接绑定,简洁直观。

亮点三:多租户工作区

系统支持多工作区(Workspace)隔离,不同工作区的数据完全独立,适合一个系统服务多个团队或项目的场景。

六、性能优化策略

按需加载:页面模板通过路由懒加载,首屏只加载登录页或仪表盘,减少初始体积。

并行请求:仪表盘统计数据与待办列表使用 Promise.all 并行请求,缩短加载时间。

防抖搜索:关键词搜索输入使用 debounce,避免频繁请求。

CDN 加速:Tailwind、Alpine.js、htmx 均通过 CDN 引入,利用浏览器缓存。

DOM 优化:大列表使用 x-show/x-if 控制显隐,减少不必要的 DOM 操作。

七、开发规范与协作

代码规范

JavaScript:使用 const/let,避免 var;函数名小驼峰;组件函数以页面功能命名(如 loginFormbugListPage)。

HTML:语义化标签,表单元素添加 labelplaceholder;复杂逻辑下沉到 Alpine 组件,模板保持简洁。

CSS:优先使用 Tailwind 原子类,避免重复样式。

新功能开发流程

需求分析:明确功能边界、用户角色、权限、数据模型与接口契约。

设计阶段:确定页面归属、路由映射、Alpine 组件职责。

实现阶段:在 pages.js 中新增组件函数,编写 HTML 模板,必要时扩展 utils.js

联调自测:覆盖正常路径与异常路径(401、网络错误、空数据)。

代码审查:提交前确保可读性与一致性,提交信息清晰描述改动范围。

八、踩过的坑与经验

坑1:登录后仍跳转登录页

原因:Token 写入 localStorage 的时机与路由跳转存在竞态条件。 解决:登录成功后先 await 获取用户信息,再设置 Token,最后执行路由跳转,确保状态同步。

坑2:模板加载后脚本不执行

原因fetch 获取的 HTML 字符串注入 DOM 后,<script> 标签不会自动执行。 解决:手动提取模板中的 <script> 内容,创建新的 <script> 元素并 append 到 DOM,再调用 htmx.process() 重新处理。

坑3:OSS 上传跨域

原因:OSS Bucket 未配置 CORS 规则。 解决:在阿里云 OSS 控制台配置允许来源、允许方法和允许 Headers。


九、未来规划

组件抽取:将通用 UI(状态标签、优先级徽章、弹窗等)迁移至 src/components,提升复用性。

单元测试:为 utils.js 中的纯函数编写 Jest 测试用例。

E2E 测试:针对登录、创建 Bug、状态变更等关键流程编写端到端测试。

移动端适配:进一步完善响应式布局,支持手机端便捷操作。

数据导出:支持 Bug 列表导出为 Excel/PDF,方便周报与复盘。

Webhook 集成:对接 GitLab、Jenkins 等工具,实现代码提交自动关联 Bug、构建失败自动创建缺陷卡片。

十、写在最后

自研缺陷管理系统,本质上是在做一道权衡题:用一定的开发成本,换取长期的可控性、灵活性和成本优势。对于有一定技术积累的团队来说,这是一笔划算的投资。

我们的系统目前还在持续迭代中,restina-framework 也在不断打磨。如果你也在考虑自研内部工具,希望这篇文章能给你一些参考。

没有最好的架构,只有最适合当下团队的架构。 保持迭代,保持务实,工具就会越来越顺手。

本文基于实际项目经验撰写,系统代码持续更新中。欢迎交流探讨。

0

发表回复