一、为什么我们要自研?
市面上成熟的缺陷管理工具并不少——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;函数名小驼峰;组件函数以页面功能命名(如 loginForm、bugListPage)。
HTML:语义化标签,表单元素添加 label 与 placeholder;复杂逻辑下沉到 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 也在不断打磨。如果你也在考虑自研内部工具,希望这篇文章能给你一些参考。
没有最好的架构,只有最适合当下团队的架构。 保持迭代,保持务实,工具就会越来越顺手。
本文基于实际项目经验撰写,系统代码持续更新中。欢迎交流探讨。