禅道二次开发实战:从零构建自定义字段模块
1. 为什么需要自定义字段模块
在项目管理工具的实际使用中,标准功能往往无法完全满足团队的个性化需求。就拿测试用例管理来说,不同团队对测试方式的分类标准可能完全不同——有的团队需要区分手工测试和自动化测试,有的团队可能需要更细粒度的划分(比如接口自动化、UI自动化等)。这时候,自定义字段就成了刚需。
我去年给一个金融团队做禅道定制时就遇到过这种情况。他们测试用例需要记录"测试环境类型"(开发环境、测试环境、生产环境)和"测试数据准备方式"(自动生成、手动准备、混合模式),这些字段在标准版里根本找不到。通过二次开发添加这些字段后,他们的测试效率提升了30%以上。
自定义字段的开发看似简单,实际上涉及到前后端全栈的改造:
- 数据库层面要新增字段
- 后端要处理字段的CRUD逻辑
- 前端要渲染表单和展示数据
- 多语言要支持字段标签
- 甚至还要考虑权限控制和数据校验
2. 开发前的准备工作
2.1 环境搭建要点
建议使用Docker快速搭建开发环境,避免污染本地PHP环境。这是我验证过的docker-compose配置:
version: '3' services: zentao: image: easysoft/zentao:16.4 ports: - "8080:80" volumes: - ./data:/www/zentaopms - ./module:/www/zentaopms/module environment: - MYSQL_HOST=db - MYSQL_USER=root - MYSQL_PASSWORD=123456 db: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORD=123456 - MYSQL_DATABASE=zentao关键目录结构要记牢:
module/ └── testcase/ ├── control.php # 控制器 ├── model.php # 模型 ├── view/ # 视图模板 └── ext/ # 扩展目录 ├── control/ ├── model/ ├── view/ ├── lang/ ├── js/ └── css/2.2 开发工具推荐
VSCode搭配这些插件效率翻倍:
- PHP Intelephense(代码提示)
- PHP Debug(XDebug调试)
- SQLTools(数据库管理)
- GitLens(版本控制)
调试技巧:在代码里加die(json_encode($var))比var_dump更友好,特别是调试数组时。
3. 核心开发步骤详解
3.1 数据库改造
先给zt_case表添加字段(建议用迁移脚本而不是直接操作):
-- /db/migrate/20230801_add_execType_to_case.sql ALTER TABLE `zt_case` ADD COLUMN `execType` VARCHAR(30) NOT NULL DEFAULT 'manual' COMMENT '测试方式', ADD INDEX `idx_execType` (`execType`);重要提示:字段命名要遵循禅道规范(全小写+驼峰),长度根据实际需求设置,别忘了加索引。
3.2 后端开发实战
control扩展有个坑要注意——参数默认值处理。这是正确的扩展方式:
// module/testcase/ext/control/create.php class myTestcase extends testcase { public function create($productID, $branch = '', $moduleID = 0, $from = '', $param = 0, $storyID = 0, $extras = '') { // 设置默认值 $this->view->execType = $this->post->execType ?: 'manual'; // 必须调用父类方法 parent::create(...func_get_args()); // 后置处理(如果需要) $this->loadModel('action')->create('case', $caseID, 'Opened'); } }model扩展更推荐用钩子方式:
// module/testcase/ext/model/hook/create.test.php if($this->post->execType === 'auto') { $this->app->loadLang('testcase'); return sprintf($lang->testcase->autoTestNotice, $this->post->title); }3.3 前端开发技巧
View扩展有两种实用方案:
- 覆盖扩展(适合大面积修改):
// module/testcase/ext/view/create.html.php <div class="input-group"> <span class="input-group-addon"><?php echo $lang->testcase->execType;?></span> <?php echo html::select('execType', $lang->testcase->execTypeList, $execType, "class='form-control'");?> </div>- 钩子扩展(适合小范围插入):
// module/testcase/ext/view/create.test.html.hook.php <script> $(function(){ $('#stageBox').after(`<div class="input-group"> <span class="input-group-addon">测试方式</span> <select name="execType" class="form-control"> <option value="manual">手工测试</option> <option value="auto">自动化测试</option> </select> </div>`); }); </script>CSS扩展推荐用BEM命名规范:
/* module/testcase/ext/css/create/test.css */ .testcase-create__execType { margin-top: 15px; } .testcase-create__execType-label { color: #3c8dbc; }4. 常见问题解决方案
4.1 字段不显示问题排查
按照这个检查清单排查:
- 检查ext目录结构是否正确
- 确认文件名大小写(Linux区分大小写)
- 查看PHP错误日志(/tmp/php_errors.log)
- 清除禅道缓存(rm -rf /tmp/ztcache/*)
- 检查浏览器控制台是否有JS错误
4.2 数据保存失败处理
我遇到过三种典型情况:
- 字段长度超限 → 修改数据库字段类型
- 未通过模型验证 → 检查model的validate规则
- 权限不足 → 检查用户角色权限配置
调试方法:在model的save方法前加die(json_encode($this->post)),查看提交的数据结构。
4.3 多语言支持方案
lang扩展要特别注意键名冲突:
// module/testcase/ext/lang/zh-cn/testcase.php $lang->testcase->execType = '测试方式'; $lang->testcase->execTypeList = array('' => '', 'manual' => '手工', 'auto' => '自动'); $lang->testcase->execTypeError = '请选择测试方式'; // 英文版 // module/testcase/ext/lang/en/testcase.php $lang->testcase->execType = 'Test Type'; $lang->testcase->execTypeList = array('' => '', 'manual' => 'Manual', 'auto' => 'Auto');5. 扩展与优化建议
5.1 批量操作支持
如果需要在批量编辑页也支持这个字段,要扩展batchEdit方法:
// module/testcase/ext/control/batchedit.php class myTestcase extends testcase { public function batchEdit($productID = 0) { $this->view->execTypes = $this->lang->testcase->execTypeList; parent::batchEdit($productID); } }对应的view扩展:
<!-- module/testcase/ext/view/batchedit.html.php --> <tr> <th><?php echo $lang->testcase->execType;?></th> <td><?php echo html::select('execType', $execTypes, '', "class='form-control'");?></td> </tr>5.2 报表统计集成
让自定义字段参与统计分析的技巧:
-- 测试方式统计报表SQL SELECT execType, COUNT(*) as total, ROUND(COUNT(*)/(SELECT COUNT(*) FROM zt_case WHERE deleted='0')*100,2) as percent FROM zt_case WHERE deleted='0' GROUP BY execType;5.3 权限控制方案
限制某些角色修改测试方式字段:
// module/testcase/ext/control/create.php if($this->app->user->role == 'qa' && $this->post->execType == 'auto') { die(js::alert('QA无权创建自动化用例')); }更完善的方案是扩展权限模块,这里不再展开。
