# 审批流程测试报告 **测试日期**: 2026-03-01 **测试版本**: feature/debug-feishu-message (commit: 1e2d337) **测试环境**: 生产环境 (45.153.130.80) **测试人员**: Claude --- ## 📋 测试环境配置 ### 服务器信息 ``` IP: 45.153.130.80 域名: uniservice-mobile.hnxinyouhe.com 后端: x-server-backend-prod (PHP 8.5.3) 数据库: 1Panel-mysql-XyxS (MySQL) ``` ### 节点配置 ``` 节点ID: 7 节点名称: 李佳博 节点类型: and_approve (会签) 所属流程: 2 (出差审批) 节点顺序: 0 ``` ### 测试用户 ``` 申请人: 李佳博 (ID: 1) 审批人: - 陈兰 (ID: 2) - 陈标 (ID: 3) ``` --- ## 🧪 测试场景 ### 场景1: 会签流程 - 所有人通过 #### 流程图 ``` 提交申请 → 审批人1通过 → 审批人2通过 → 订单完成 ↓ ↓(pending) ↓(approved) pending 1/2 已通过 2/2 已通过 ``` #### 测试步骤 | 步骤 | 操作 | 参数 | 预期结果 | 实际结果 | 状态 | |------|------|------|----------|----------|------| | 1 | 创建审批订单 | `form_data.approvers = [2, 3]`
`current_nodes = [7]`
`status = "pending"` | 订单ID创建成功 | 订单ID=14 | ✅ | | 2 | 审批人1通过 | `order_id = 14`
`approver_id = 2`
`action = "approve"` | 状态=pending
已审批=1/2 | 状态=pending
已审批=1/2 | ✅ | | 3 | 审批人2通过 | `order_id = 14`
`approver_id = 3`
`action = "approve"` | 状态=approved
已审批=2/2 | 状态=approved
已审批=2/2 | ✅ | | 4 | 验证审批记录 | `order_id = 14`
`action = "approve"` | 2条记录 | 2条记录 | ✅ | #### 审批记录 ``` ID 1: 陈兰 (ID:2) 在节点 7 审批通过 ID 2: 陈标 (ID:3) 在节点 7 审批通过 ``` #### 测试代码 ```php // 创建订单 $order = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => 100, 'title' => '会签测试', 'applicant_id' => 1, 'status' => 'pending', 'current_nodes' => [7], 'flow_id' => 2, 'form_data' => ['approvers' => [2, 3]], ]); // 审批人1通过 $service->approve(14, 2); // 审批人2通过 $service->approve(14, 3); ``` --- ### 场景2: 拒绝流程 - 任一人拒绝 #### 流程图 ``` 提交申请 → 审批人1拒绝 → 订单结束 ↓ ↓(rejected) pending 流程终止 ``` #### 测试步骤 | 步骤 | 操作 | 参数 | 预期结果 | 实际结果 | 状态 | |------|------|------|----------|----------|------| | 1 | 创建审批订单 | `form_data.approvers = [2, 3]`
`status = "pending"` | 订单ID创建成功 | 订单ID=15 | ✅ | | 2 | 审批人1拒绝 | `order_id = 15`
`approver_id = 2`
`action = "reject"`
`comment = "测试拒绝"` | 状态=rejected | 状态=rejected | ✅ | | 3 | 验证审批记录 | `order_id = 15`
`action = "reject"` | 1条拒绝记录 | 1条拒绝记录 | ✅ | #### 审批记录 ``` ID 1: 陈兰 (ID:2) 在节点 7 审批拒绝 - 原因: "测试拒绝" ``` #### 测试代码 ```php $order = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => 101, 'title' => '拒绝测试', 'applicant_id' => 1, 'status' => 'pending', 'current_nodes' => [7], 'flow_id' => 2, 'form_data' => ['approvers' => [2, 3]], ]); // 审批人1拒绝 $service->reject(15, 2, '测试拒绝'); ``` --- ### 场景3: 重复操作防护 - 同一人不能重复审批 #### 流程图 ``` 提交申请 → 审批人1通过 → 审批人1再次通过 ↓ ↓(success) ↓(异常) pending 记录已创建 抛出异常 ``` #### 测试步骤 | 步骤 | 操作 | 参数 | 预期结果 | 实际结果 | 状态 | |------|------|------|----------|----------|------| | 1 | 创建审批订单 | `form_data.approvers = [2, 3]`
`status = "pending"` | 订单ID创建成功 | 订单ID=16 | ✅ | | 2 | 审批人1第一次通过 | `order_id = 16`
`approver_id = 2` | 操作成功 | 操作成功 | ✅ | | 3 | 审批人1第二次通过 | `order_id = 16`
`approver_id = 2` | 抛出异常 | "您已经通过过此申请" | ✅ | #### 异常信息 ``` Exception: 您已经通过过此申请,无需重复操作 ``` #### 测试代码 ```php $order = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => 102, 'title' => '重复操作测试', 'applicant_id' => 1, 'status' => 'pending', 'current_nodes' => [7], 'flow_id' => 2, 'form_data' => ['approvers' => [2, 3]], ]); try { $service->approve(16, 2); // 第一次 - 成功 $service->approve(16, 2); // 第二次 - 异常 } catch (Exception $e) { // 捕获重复操作异常 } ``` --- ### 场景4: 撤销流程 - 申请人可撤销 #### 流程图 ``` 提交申请 → 申请人撤销 → 订单结束 ↓ ↓(cancelled) pending 流程终止 ``` #### 测试步骤 | 步骤 | 操作 | 参数 | 预期结果 | 实际结果 | 状态 | |------|------|------|----------|----------|------| | 1 | 创建审批订单 | `applicant_id = 1`
`status = "pending"` | 订单ID创建成功 | 订单ID=17 | ✅ | | 2 | 申请人撤销 | `order_id = 17`
`user_id = 1` | 状态=cancelled | 状态=cancelled | ✅ | | 3 | 验证撤销记录 | `order_id = 17`
`action = "cancel"` | 1条撤销记录 | 1条撤销记录 | ✅ | #### 审批记录 ``` ID 1: 李佳博 (ID:1) 撤销了申请 ``` #### 测试代码 ```php $order = ApprovalOrder::create([ 'business_type' => 'business_trip', 'business_id' => 103, 'title' => '撤销测试', 'applicant_id' => 1, 'status' => 'pending', 'current_nodes' => [7], 'flow_id' => 2, 'form_data' => ['approvers' => [2, 3]], ]); // 申请人撤销 $service->cancel(17, 1); ``` --- ## 📊 核心逻辑验证 ### 节点完成判断逻辑 **代码位置**: `ApprovalService::isNodeCompleted()` ```php private function isNodeCompleted(int $orderId, int $nodeId): bool { $node = ApprovalNode::findOrFail($nodeId); $records = ApprovalRecord::where('order_id', $orderId) ->where('node_id', $nodeId) ->where('action', 'approve') ->get(); // 从 form_data 获取实际审批人列表 $order = ApprovalOrder::find($orderId); $formData = $order->form_data ?? []; $approvers = $formData['approvers'] ?? []; if (!empty($approvers) && is_array($approvers[0] ?? null) && isset($approvers[0]['id'])) { $approverIds = array_column($approvers, 'id'); } else { $approverIds = $approvers; } if ($node->type === ApprovalNode::TYPE_AND_APPROVE) { // 会签:需要所有人通过 return count($records) >= count($approverIds); } elseif ($node->type === ApprovalNode::TYPE_OR_APPROVE) { // 或签:任一人通过即可 return count($records) >= 1; } else { // 单人审批:需要一人通过 return count($records) >= 1; } } ``` **验证结果**: ✅ 通过 | 节点类型 | 完成条件 | 测试结果 | |----------|----------|----------| | and_approve (会签) | 已审批人数 >= 总审批人数 | ✅ | | or_approve (或签) | 已审批人数 >= 1 | ⏳ 未测试 | | approver (单人) | 已审批人数 >= 1 | ⏳ 未测试 | --- ## 🔔 消息发送功能 ### 当前状态 - ⚠️ **飞书消息发送失败** (emoji 编码问题) ### 错误信息 ``` ErrCode: 200621; ErrMsg: parse card json err, please check whether the card json is correct ``` ### 原因分析 PHP 的 `json_encode()` 默认将 emoji 编码成 Unicode 转义序列: - `🔔` → `\ud83d\udd14` - `📋` → `\ud83d\udccb` 飞书 API 不接受这种编码格式。 ### 待修复项 1. 修改 `FeishuNotificationService` 的 JSON 编码选项 2. 使用 `JSON_UNESCAPED_UNICODE` 标志 --- ## 🗄️ 数据库表状态 ### feishu_message_log ```sql CREATE TABLE IF NOT EXISTS feishu_message_log ( id bigint unsigned NOT NULL AUTO_INCREMENT, action varchar(20) NOT NULL COMMENT '操作类型:send=发送, update=更新, update_card=更新卡片', order_id bigint unsigned NOT NULL COMMENT '审批订单ID', node_id bigint unsigned DEFAULT NULL COMMENT '审批节点ID', user_id bigint unsigned DEFAULT NULL COMMENT '用户ID', message_id varchar(64) DEFAULT NULL COMMENT '飞书消息ID', status varchar(10) NOT NULL COMMENT '状态:success=成功, failed=失败', request_data text DEFAULT NULL COMMENT '请求数据(JSON)', response_data text DEFAULT NULL COMMENT '响应数据(JSON)', error_message text DEFAULT NULL COMMENT '错误信息', created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_order_id (order_id), KEY idx_action (action), KEY idx_status (status), KEY idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='飞书消息日志表'; ``` **状态**: ✅ 已创建并包含 `updated_at` 字段 --- ## ✅ 测试结论 ### 通过的测试 1. ✅ 会签流程 - 所有人通过后订单完成 2. ✅ 拒绝流程 - 任一人拒绝后订单结束 3. ✅ 重复操作防护 - 同一人不能重复审批 4. ✅ 撤销流程 - 申请人可撤销申请 5. ✅ 审批记录正确保存 6. ✅ 节点完成判断逻辑正确 ### 待解决的问题 1. ⚠️ 飞书消息发送失败 (emoji 编码问题) 2. ⏳ 或签 (or_approve) 场景未测试 3. ⏳ 单人审批 (approver) 场景未测试 4. ⏳ 多节点流转未测试 ### 建议 1. 优先修复飞书消息 emoji 编码问题 2. 补充或签和单人审批场景测试 3. 测试多节点流转场景 4. 测试消息卡片更新功能(或签/单人审批完成后) --- ## 📝 附录 ### 测试文件位置 ``` /www/x-server-mobile/docs/TEST_REPORT_APPROVAL_20260301.md ``` ### 相关代码文件 ``` backend/app/service/ApprovalService.php # 审批服务 backend/app/service/FeishuNotificationService.php # 飞书通知服务 backend/app/model/ApprovalOrder.php # 审批订单模型 backend/app/model/ApprovalRecord.php # 审批记录模型 backend/app/model/ApprovalNode.php # 审批节点模型 backend/app/model/FeishuMessageLog.php # 消息日志模型 ``` ### 测试执行命令 ```bash # 会签流程测试 docker exec x-server-backend-prod php /app/test_approval_logic.php # 全场景测试 docker exec x-server-backend-prod php /app/test_approval_all.php ``` --- **报告生成时间**: 2026-03-01 09:18:00 **测试执行者**: Claude Code **审核状态**: 待审核