7598e363f2
- 测试环境: 生产环境 (45.153.130.80) - 测试场景: 会签、拒绝、重复操作防护、撤销 - 测试结果: 4个场景全部通过 - 待解决问题: 飞书消息 emoji 编码问题
10 KiB
10 KiB
审批流程测试报告
测试日期: 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 = 14approver_id = 2action = "approve" |
状态=pending 已审批=1/2 |
状态=pending 已审批=1/2 |
✅ |
| 3 | 审批人2通过 | order_id = 14approver_id = 3action = "approve" |
状态=approved 已审批=2/2 |
状态=approved 已审批=2/2 |
✅ |
| 4 | 验证审批记录 | order_id = 14action = "approve" |
2条记录 | 2条记录 | ✅ |
审批记录
ID 1: 陈兰 (ID:2) 在节点 7 审批通过
ID 2: 陈标 (ID:3) 在节点 7 审批通过
测试代码
// 创建订单
$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 = 15approver_id = 2action = "reject"comment = "测试拒绝" |
状态=rejected | 状态=rejected | ✅ |
| 3 | 验证审批记录 | order_id = 15action = "reject" |
1条拒绝记录 | 1条拒绝记录 | ✅ |
审批记录
ID 1: 陈兰 (ID:2) 在节点 7 审批拒绝 - 原因: "测试拒绝"
测试代码
$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 = 16approver_id = 2 |
操作成功 | 操作成功 | ✅ |
| 3 | 审批人1第二次通过 | order_id = 16approver_id = 2 |
抛出异常 | "您已经通过过此申请" | ✅ |
异常信息
Exception: 您已经通过过此申请,无需重复操作
测试代码
$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 = 1status = "pending" |
订单ID创建成功 | 订单ID=17 | ✅ |
| 2 | 申请人撤销 | order_id = 17user_id = 1 |
状态=cancelled | 状态=cancelled | ✅ |
| 3 | 验证撤销记录 | order_id = 17action = "cancel" |
1条撤销记录 | 1条撤销记录 | ✅ |
审批记录
ID 1: 李佳博 (ID:1) 撤销了申请
测试代码
$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()
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 不接受这种编码格式。
待修复项
- 修改
FeishuNotificationService的 JSON 编码选项 - 使用
JSON_UNESCAPED_UNICODE标志
🗄️ 数据库表状态
feishu_message_log
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 字段
✅ 测试结论
通过的测试
- ✅ 会签流程 - 所有人通过后订单完成
- ✅ 拒绝流程 - 任一人拒绝后订单结束
- ✅ 重复操作防护 - 同一人不能重复审批
- ✅ 撤销流程 - 申请人可撤销申请
- ✅ 审批记录正确保存
- ✅ 节点完成判断逻辑正确
待解决的问题
- ⚠️ 飞书消息发送失败 (emoji 编码问题)
- ⏳ 或签 (or_approve) 场景未测试
- ⏳ 单人审批 (approver) 场景未测试
- ⏳ 多节点流转未测试
建议
- 优先修复飞书消息 emoji 编码问题
- 补充或签和单人审批场景测试
- 测试多节点流转场景
- 测试消息卡片更新功能(或签/单人审批完成后)
📝 附录
测试文件位置
/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 # 消息日志模型
测试执行命令
# 会签流程测试
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 审核状态: 待审核