Files
x-server-mobile/backend/test_message_logic_complete.php
root e13574f6f1 fix: 修复会签场景下消息卡片更新问题
- 添加 updateMessageCardsForPartialApprove 方法处理会签部分通过
- 会签一人通过后,其他人的消息卡片显示进度并保留按钮
- 修复 Dockerfile 构建顺序问题
- 修复前端类型定义问题

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:33:40 +08:00

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";