e13574f6f1
- 添加 updateMessageCardsForPartialApprove 方法处理会签部分通过 - 会签一人通过后,其他人的消息卡片显示进度并保留按钮 - 修复 Dockerfile 构建顺序问题 - 修复前端类型定义问题 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
581 lines
18 KiB
PHP
581 lines
18 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 飞书消息逻辑完整测试
|
|
* 测试所有消息发送场景
|
|
*/
|
|
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
require_once __DIR__ . '/support/bootstrap.php';
|
|
|
|
use app\model\ApprovalOrder;
|
|
use app\model\ApprovalFlow;
|
|
use app\model\ApprovalNode;
|
|
use app\model\ApprovalRecord;
|
|
use app\model\FeishuMessage;
|
|
use app\model\FeishuMessageLog;
|
|
use app\model\User;
|
|
use app\service\ApprovalService;
|
|
use app\service\FeishuNotificationService;
|
|
use support\Db;
|
|
|
|
echo "====================================\n";
|
|
echo "飞书消息逻辑完整测试\n";
|
|
echo "====================================\n\n";
|
|
|
|
// 测试配置
|
|
$testApplicantId = 1; // 李佳博
|
|
$testApprover1 = 2; // 陈兰
|
|
$testApprover2 = 3; // 陈标
|
|
$testApprover3 = 4; // 闫继光00022
|
|
|
|
// 检查测试用户的飞书 ID
|
|
function checkFeishuUserId($userId) {
|
|
$user = User::find($userId);
|
|
if (!$user) {
|
|
echo "❌ 用户 ID {$userId} 不存在\n";
|
|
return false;
|
|
}
|
|
if (empty($user->feishu_user_id)) {
|
|
echo "⚠️ 用户 {$user->name} (ID:{$userId}) 没有绑定飞书 ID\n";
|
|
return false;
|
|
}
|
|
echo "✅ 用户 {$user->name} (ID:{$userId}) 飞书 ID: {$user->feishu_user_id}\n";
|
|
return true;
|
|
}
|
|
|
|
echo "检查测试用户:\n";
|
|
echo "----------------------------------------\n";
|
|
$user1 = checkFeishuUserId($testApplicantId);
|
|
$user2 = checkFeishuUserId($testApprover1);
|
|
$user3 = checkFeishuUserId($testApprover2);
|
|
$user4 = checkFeishuUserId($testApprover3);
|
|
|
|
if (!$user1 || !$user2 || !$user3) {
|
|
echo "\n⚠️ 部分用户没有绑定飞书 ID,将跳过实际 API 调用测试\n";
|
|
$skipApiTest = true;
|
|
} else {
|
|
$skipApiTest = false;
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// 测试场景
|
|
$testResults = [];
|
|
|
|
// ========== 测试1: 单人审批通知 ==========
|
|
echo "【测试1】单人审批通知消息\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
// 查找单人审批节点
|
|
$singleApproverNode = ApprovalNode::where('type', 'approver')->first();
|
|
if (!$singleApproverNode) {
|
|
$singleApproverNode = ApprovalNode::create([
|
|
'flow_id' => 2,
|
|
'name' => '单人审批测试',
|
|
'type' => 'approver',
|
|
'sequence' => 0,
|
|
]);
|
|
}
|
|
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2001,
|
|
'title' => '单人审批消息测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$singleApproverNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}, 审批人={$testApprover1}\n";
|
|
|
|
// 发送审批通知
|
|
$notificationService = new FeishuNotificationService();
|
|
$result = $notificationService->sendApprovalNotification([
|
|
'user_id' => $testApprover1,
|
|
'applicant_id' => $testApplicantId,
|
|
'title' => '新的审批申请',
|
|
'applicant' => User::find($testApplicantId)->name ?? '未知',
|
|
'type' => '审批测试',
|
|
'content' => '单人审批消息测试',
|
|
'url' => 'https://uniservice-mobile.hnxinyouhe.com/approval/detail/' . $order->id,
|
|
'order_id' => $order->id,
|
|
'node_id' => $singleApproverNode->id,
|
|
]);
|
|
|
|
Db::rollBack(); // 回滚测试数据
|
|
|
|
if ($result) {
|
|
echo "✅ 单人审批通知发送成功\n";
|
|
$testResults['test1'] = 'PASS';
|
|
} else {
|
|
echo "⚠️ 单人审批通知发送失败\n";
|
|
$testResults['test1'] = 'SKIP';
|
|
}
|
|
|
|
// 检查消息日志
|
|
$logs = FeishuMessageLog::where('order_id', $order->id)
|
|
->where('action', 'send')
|
|
->get();
|
|
|
|
echo "消息日志记录: {$logs->count()} 条\n";
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试1失败: " . $e->getMessage() . "\n";
|
|
$testResults['test1'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试2: 会签审批通知 ==========
|
|
echo "【测试2】会签审批通知消息\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$andApproveNode = ApprovalNode::where('type', 'and_approve')->first();
|
|
if (!$andApproveNode) {
|
|
$andApproveNode = ApprovalNode::create([
|
|
'flow_id' => 2,
|
|
'name' => '会签测试',
|
|
'type' => 'and_approve',
|
|
'sequence' => 0,
|
|
]);
|
|
}
|
|
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2002,
|
|
'title' => '会签消息测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$andApproveNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1, $testApprover2]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}, 审批人=[2, 3]\n";
|
|
|
|
$notificationService = new FeishuNotificationService();
|
|
|
|
// 发送给两个审批人
|
|
$result1 = $notificationService->sendApprovalNotification([
|
|
'user_id' => $testApprover1,
|
|
'applicant_id' => $testApplicantId,
|
|
'title' => '新的审批申请',
|
|
'applicant' => User::find($testApplicantId)->name ?? '未知',
|
|
'type' => '会签测试',
|
|
'content' => '会签消息测试',
|
|
'url' => 'https://uniservice-mobile.hnxinyouhe.com/approval/detail/' . $order->id,
|
|
'order_id' => $order->id,
|
|
'node_id' => $andApproveNode->id,
|
|
]);
|
|
|
|
$result2 = $notificationService->sendApprovalNotification([
|
|
'user_id' => $testApprover2,
|
|
'applicant_id' => $testApplicantId,
|
|
'title' => '新的审批申请',
|
|
'applicant' => User::find($testApplicantId)->name ?? '未知',
|
|
'type' => '会签测试',
|
|
'content' => '会签消息测试',
|
|
'url' => 'https://uniservice-mobile.hnxinyouhe.com/approval/detail/' . $order->id,
|
|
'order_id' => $order->id,
|
|
'node_id' => $andApproveNode->id,
|
|
]);
|
|
|
|
Db::rollBack();
|
|
|
|
if ($result1 && $result2) {
|
|
echo "✅ 会签审批通知发送成功(2人)\n";
|
|
$testResults['test2'] = 'PASS';
|
|
} else {
|
|
echo "⚠️ 会签审批通知发送失败\n";
|
|
$testResults['test2'] = 'SKIP';
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试2失败: " . $e->getMessage() . "\n";
|
|
$testResults['test2'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试3: 或签审批通知 + 消息更新 ==========
|
|
echo "【测试3】或签审批通知 + 消息卡片更新\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$orApproveNode = ApprovalNode::where('type', 'or_approve')->first();
|
|
if (!$orApproveNode) {
|
|
$orApproveNode = ApprovalNode::create([
|
|
'flow_id' => 2,
|
|
'name' => '或签测试',
|
|
'type' => 'or_approve',
|
|
'sequence' => 0,
|
|
]);
|
|
}
|
|
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2003,
|
|
'title' => '或签消息测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$orApproveNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1, $testApprover2, $testApprover3]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}, 审批人=[2, 3, 4]\n";
|
|
|
|
$notificationService = new FeishuNotificationService();
|
|
|
|
// 发送给三个审批人
|
|
$messageIds = [];
|
|
foreach ([$testApprover1, $testApprover2, $testApprover3] as $approverId) {
|
|
$result = $notificationService->sendApprovalNotification([
|
|
'user_id' => $approverId,
|
|
'applicant_id' => $testApplicantId,
|
|
'title' => '新的审批申请',
|
|
'applicant' => User::find($testApplicantId)->name ?? '未知',
|
|
'type' => '或签测试',
|
|
'content' => '或签消息测试',
|
|
'url' => 'https://uniservice-mobile.hnxinyouhe.com/approval/detail/' . $order->id,
|
|
'order_id' => $order->id,
|
|
'node_id' => $orApproveNode->id,
|
|
]);
|
|
|
|
// 获取消息ID(从日志中)
|
|
$log = FeishuMessageLog::where('order_id', $order->id)
|
|
->where('user_id', $approverId)
|
|
->where('action', 'send')
|
|
->orderBy('id', 'desc')
|
|
->first();
|
|
|
|
if ($log && $log->message_id) {
|
|
$messageIds[$approverId] = $log->message_id;
|
|
}
|
|
}
|
|
|
|
echo "发送了 " . count($messageIds) . " 条消息\n";
|
|
|
|
// 第一人审批通过
|
|
$approvalService = new ApprovalService();
|
|
$approvalService->approve($order->id, $testApprover1);
|
|
|
|
echo "审批人 {$testApprover1} 通过\n";
|
|
$order->refresh();
|
|
|
|
echo "订单状态: {$order->status}\n";
|
|
|
|
// 检查是否触发了消息更新
|
|
$updateLogs = FeishuMessageLog::where('order_id', $order->id)
|
|
->where('action', 'update')
|
|
->get();
|
|
|
|
echo "消息更新日志: {$updateLogs->count()} 条\n";
|
|
|
|
Db::rollBack();
|
|
|
|
if ($order->status === 'approved') {
|
|
echo "✅ 或签审批完成正确\n";
|
|
$testResults['test3_status'] = 'PASS';
|
|
} else {
|
|
echo "❌ 或签审批状态错误: {$order->status}\n";
|
|
$testResults['test3_status'] = 'FAIL';
|
|
}
|
|
|
|
if ($updateLogs->count() > 0 || count($messageIds) === 0) {
|
|
// 如果没有消息ID(没有飞书ID),更新日志为0是正常的
|
|
$testResults['test3'] = 'PASS';
|
|
} else {
|
|
$testResults['test3'] = 'SKIP';
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试3失败: " . $e->getMessage() . "\n";
|
|
$testResults['test3'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试4: 审批结果通知(通过) ==========
|
|
echo "【测试4】审批结果通知(通过)\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$andApproveNode = ApprovalNode::where('type', 'and_approve')->first();
|
|
if (!$andApproveNode) {
|
|
echo "跳过:没有会签节点\n";
|
|
$testResults['test4'] = 'SKIP';
|
|
} else {
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2004,
|
|
'title' => '结果通知测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$andApproveNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1, $testApprover2]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}\n";
|
|
|
|
$approvalService = new ApprovalService();
|
|
$approvalService->approve($order->id, $testApprover1);
|
|
echo "审批人 {$testApprover1} 通过\n";
|
|
|
|
$approvalService->approve($order->id, $testApprover2);
|
|
echo "审批人 {$testApprover2} 通过\n";
|
|
|
|
$order->refresh();
|
|
echo "订单状态: {$order->status}\n";
|
|
|
|
// 检查结果通知
|
|
$resultLogs = FeishuMessageLog::where('order_id', $order->id)
|
|
->whereIn('action', ['result', 'send'])
|
|
->get();
|
|
|
|
echo "结果通知日志: {$resultLogs->count()} 条\n";
|
|
|
|
Db::rollBack();
|
|
|
|
if ($order->status === 'approved') {
|
|
echo "✅ 订单状态正确\n";
|
|
$testResults['test4'] = 'PASS';
|
|
} else {
|
|
echo "❌ 订单状态错误\n";
|
|
$testResults['test4'] = 'FAIL';
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试4失败: " . $e->getMessage() . "\n";
|
|
$testResults['test4'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试5: 审批结果通知(拒绝) ==========
|
|
echo "【测试5】审批结果通知(拒绝)\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$andApproveNode = ApprovalNode::where('type', 'and_approve')->first();
|
|
if (!$andApproveNode) {
|
|
echo "跳过:没有会签节点\n";
|
|
$testResults['test5'] = 'SKIP';
|
|
} else {
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2005,
|
|
'title' => '拒绝通知测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$andApproveNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1, $testApprover2]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}\n";
|
|
|
|
$approvalService = new ApprovalService();
|
|
$approvalService->reject($order->id, $testApprover1, '测试拒绝');
|
|
|
|
echo "审批人 {$testApprover1} 拒绝\n";
|
|
|
|
$order->refresh();
|
|
echo "订单状态: {$order->status}\n";
|
|
|
|
Db::rollBack();
|
|
|
|
if ($order->status === 'rejected') {
|
|
echo "✅ 拒绝状态正确\n";
|
|
$testResults['test5'] = 'PASS';
|
|
} else {
|
|
echo "❌ 拒绝状态错误\n";
|
|
$testResults['test5'] = 'FAIL';
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试5失败: " . $e->getMessage() . "\n";
|
|
$testResults['test5'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试6: 撤销通知 ==========
|
|
echo "【测试6】撤销通知\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$andApproveNode = ApprovalNode::where('type', 'and_approve')->first();
|
|
if (!$andApproveNode) {
|
|
echo "跳过:没有会签节点\n";
|
|
$testResults['test6'] = 'SKIP';
|
|
} else {
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2006,
|
|
'title' => '撤销通知测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$andApproveNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => [$testApprover1, $testApprover2]],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}\n";
|
|
|
|
$approvalService = new ApprovalService();
|
|
$approvalService->cancel($order->id, $testApplicantId);
|
|
|
|
echo "申请人撤销订单\n";
|
|
|
|
$order->refresh();
|
|
echo "订单状态: {$order->status}\n";
|
|
|
|
Db::rollBack();
|
|
|
|
if ($order->status === 'cancelled') {
|
|
echo "✅ 撤销状态正确\n";
|
|
$testResults['test6'] = 'PASS';
|
|
} else {
|
|
echo "❌ 撤销状态错误\n";
|
|
$testResults['test6'] = 'FAIL';
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试6失败: " . $e->getMessage() . "\n";
|
|
$testResults['test6'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试7: 抄送通知 ==========
|
|
echo "【测试7】抄送通知\n";
|
|
echo "========================================\n";
|
|
|
|
try {
|
|
Db::beginTransaction();
|
|
|
|
$notifyNode = ApprovalNode::where('type', 'notify')->first();
|
|
if (!$notifyNode) {
|
|
echo "跳过:没有通知节点\n";
|
|
$testResults['test7'] = 'SKIP';
|
|
} else {
|
|
$order = ApprovalOrder::create([
|
|
'business_type' => 'business_trip',
|
|
'business_id' => 2007,
|
|
'title' => '抄送测试',
|
|
'applicant_id' => $testApplicantId,
|
|
'status' => 'pending',
|
|
'current_nodes' => [$notifyNode->id],
|
|
'flow_id' => 2,
|
|
'form_data' => ['approvers' => []],
|
|
]);
|
|
|
|
echo "创建订单: ID={$order->id}\n";
|
|
|
|
$notificationService = new FeishuNotificationService();
|
|
$result = $notificationService->sendCcNotification([
|
|
'user_id' => $testApprover1,
|
|
'title' => '抄送通知',
|
|
'applicant' => User::find($testApplicantId)->name ?? '未知',
|
|
'type' => '抄送测试',
|
|
'content' => '这是一条抄送消息',
|
|
'url' => 'https://uniservice-mobile.hnxinyouhe.com/approval/detail/' . $order->id,
|
|
'order_id' => $order->id,
|
|
'node_id' => $notifyNode->id,
|
|
]);
|
|
|
|
Db::rollBack();
|
|
|
|
if ($result) {
|
|
echo "✅ 抄送通知发送成功\n";
|
|
$testResults['test7'] = 'PASS';
|
|
} else {
|
|
echo "⚠️ 抄送通知发送失败\n";
|
|
$testResults['test7'] = 'SKIP';
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
Db::rollBack();
|
|
echo "❌ 测试7失败: " . $e->getMessage() . "\n";
|
|
$testResults['test7'] = 'FAIL';
|
|
}
|
|
|
|
echo "\n";
|
|
|
|
// ========== 测试总结 ==========
|
|
echo "====================================\n";
|
|
echo "测试总结\n";
|
|
echo "====================================\n\n";
|
|
|
|
$passed = 0;
|
|
$failed = 0;
|
|
$skipped = 0;
|
|
|
|
foreach ($testResults as $test => $result) {
|
|
$status = $result === 'PASS' ? '✅ PASS' : ($result === 'FAIL' ? '❌ FAIL' : '⏭️ SKIP');
|
|
echo "{$status}: {$test}\n";
|
|
|
|
if ($result === 'PASS') $passed++;
|
|
elseif ($result === 'FAIL') $failed++;
|
|
else $skipped++;
|
|
}
|
|
|
|
echo "\n";
|
|
echo "总计: " . count($testResults) . " 个测试\n";
|
|
echo "通过: {$passed}\n";
|
|
echo "失败: {$failed}\n";
|
|
echo "跳过: {$skipped}\n";
|
|
|
|
echo "\n";
|
|
|
|
// ========== 检查消息日志表 ==========
|
|
echo "====================================\n";
|
|
echo "消息日志数据检查\n";
|
|
echo "====================================\n\n";
|
|
|
|
// 因为上面都回滚了,检查最近的真实日志
|
|
$allLogs = FeishuMessageLog::orderBy('id', 'desc')->limit(10)->get();
|
|
|
|
echo "最近 10 条消息日志:\n";
|
|
foreach ($allLogs as $log) {
|
|
$statusIcon = $log->status === 'success' ? '✅' : '❌';
|
|
echo "{$statusIcon} [ID:{$log->id}] action={$log->action}, order_id={$log->order_id}, status={$log->status}\n";
|
|
if ($log->error_message) {
|
|
echo " 错误: {$log->error_message}\n";
|
|
}
|
|
}
|
|
|
|
echo "\n";
|
|
echo "====================================\n";
|
|
echo "测试完成\n";
|
|
echo "====================================\n";
|