fix: 修复会签场景下消息卡片更新问题
- 添加 updateMessageCardsForPartialApprove 方法处理会签部分通过 - 会签一人通过后,其他人的消息卡片显示进度并保留按钮 - 修复 Dockerfile 构建顺序问题 - 修复前端类型定义问题 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-2
@@ -18,13 +18,14 @@ COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy composer files
|
||||
# Copy composer files and necessary directories first
|
||||
COPY composer.json composer.lock* ./
|
||||
COPY config ./config/
|
||||
|
||||
# Install dependencies
|
||||
RUN composer install --no-interaction --optimize-autoloader
|
||||
|
||||
# Copy application files
|
||||
# Copy remaining application files
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
|
||||
@@ -110,8 +110,43 @@ class ApprovalService
|
||||
]);
|
||||
|
||||
// 判断节点是否完成
|
||||
if ($this->isNodeCompleted($order->id, $currentNode->id)) {
|
||||
// 对于或签和单人审批,更新其他审批人的消息卡片
|
||||
$nodeCompleted = $this->isNodeCompleted($order->id, $currentNode->id);
|
||||
|
||||
// 会签场景:即使节点未完成,也要更新其他审批人的消息卡片(显示进度)
|
||||
if (!$nodeCompleted && $currentNode->type === ApprovalNode::TYPE_AND_APPROVE) {
|
||||
$approver = User::find($approverId);
|
||||
$approverName = $approver ? $approver->name : '未知';
|
||||
|
||||
// 获取已通过人数和总人数
|
||||
$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;
|
||||
}
|
||||
$totalCount = count($approverIds);
|
||||
|
||||
$approvedRecords = ApprovalRecord::where('order_id', $order->id)
|
||||
->where('node_id', $currentNode->id)
|
||||
->where('action', 'approve')
|
||||
->get();
|
||||
$approvedCount = count($approvedRecords);
|
||||
|
||||
if ($this->notificationService) {
|
||||
$this->notificationService->updateMessageCardsForPartialApprove(
|
||||
$order->id,
|
||||
$currentNode->id,
|
||||
$approverId,
|
||||
$approverName,
|
||||
$approvedCount,
|
||||
$totalCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($nodeCompleted) {
|
||||
// 节点完成:对于或签和单人审批,更新其他审批人的消息卡片
|
||||
if ($currentNode->type === ApprovalNode::TYPE_OR_APPROVE || $currentNode->type === 'approver') {
|
||||
$approver = User::find($approverId);
|
||||
$approverName = $approver ? $approver->name : '未知';
|
||||
|
||||
@@ -789,6 +789,169 @@ class FeishuNotificationService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会签节点的消息卡片(部分审批人已通过)
|
||||
* 用于会签场景:一人通过后,更新其他人的消息卡片,显示等待其他人审批
|
||||
*
|
||||
* @param int $orderId 订单ID
|
||||
* @param int $nodeId 节点ID
|
||||
* @param int $excludeUserId 排除的用户ID(已经操作过的用户)
|
||||
* @param string $operatorName 操作人姓名
|
||||
* @param int $approvedCount 已通过人数
|
||||
* @param int $totalCount 总审批人数
|
||||
* @return bool
|
||||
*/
|
||||
public function updateMessageCardsForPartialApprove(int $orderId, int $nodeId, int $excludeUserId, string $operatorName, int $approvedCount, int $totalCount): bool
|
||||
{
|
||||
try {
|
||||
// 获取该节点的所有消息(排除已操作的用户)
|
||||
$messages = \app\model\FeishuMessage::where('order_id', $orderId)
|
||||
->where('node_id', $nodeId)
|
||||
->where('user_id', '!=', $excludeUserId)
|
||||
->where('status', \app\model\FeishuMessage::STATUS_ACTIVE)
|
||||
->get();
|
||||
|
||||
if ($messages->isEmpty()) {
|
||||
error_log("📭 没有需要更新的消息: order_id={$orderId}, node_id={$nodeId}");
|
||||
return true;
|
||||
}
|
||||
|
||||
$token = $this->getAccessToken();
|
||||
$remainingCount = $totalCount - $approvedCount;
|
||||
|
||||
foreach ($messages as $message) {
|
||||
try {
|
||||
// 构建更新后的卡片(保留操作按钮,但更新状态提示)
|
||||
$updatedCard = [
|
||||
'config' => [
|
||||
'wide_screen_mode' => false,
|
||||
],
|
||||
'header' => [
|
||||
'title' => [
|
||||
'content' => '⏳ 会签进行中',
|
||||
'tag' => 'plain_text',
|
||||
],
|
||||
'template' => 'blue',
|
||||
],
|
||||
'elements' => [
|
||||
[
|
||||
'tag' => 'div',
|
||||
'text' => [
|
||||
'content' => "**{$operatorName}** 已通过审批\n\n📊 进度: {$approvedCount}/{$totalCount} 人已通过,还剩 {$remainingCount} 人等待审批",
|
||||
'tag' => 'lark_md',
|
||||
],
|
||||
],
|
||||
[
|
||||
'tag' => 'action',
|
||||
'actions' => [
|
||||
[
|
||||
'tag' => 'button',
|
||||
'text' => [
|
||||
'content' => '通过',
|
||||
'tag' => 'plain_text',
|
||||
],
|
||||
'type' => 'primary',
|
||||
'size' => 'medium',
|
||||
'value' => [
|
||||
'action_type' => 'approve',
|
||||
'order_id' => (int)$orderId,
|
||||
],
|
||||
],
|
||||
[
|
||||
'tag' => 'button',
|
||||
'text' => [
|
||||
'content' => '拒绝',
|
||||
'tag' => 'plain_text',
|
||||
],
|
||||
'type' => 'danger',
|
||||
'size' => 'medium',
|
||||
'value' => [
|
||||
'action_type' => 'reject',
|
||||
'order_id' => (int)$orderId,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$requestData = [
|
||||
'message_id' => $message->message_id,
|
||||
'card' => $updatedCard,
|
||||
'approved_count' => $approvedCount,
|
||||
'total_count' => $totalCount,
|
||||
];
|
||||
|
||||
$contentJson = json_encode($updatedCard, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$response = HttpClient::httpPostJson(
|
||||
"https://open.feishu.cn/open-apis/im/v1/messages/{$message->message_id}/update",
|
||||
[
|
||||
'msg_type' => 'interactive',
|
||||
'content' => $contentJson,
|
||||
],
|
||||
['content'],
|
||||
[
|
||||
'Authorization: Bearer ' . $token,
|
||||
]
|
||||
);
|
||||
|
||||
$code = $response['code'] ?? -1;
|
||||
if ($code === 0) {
|
||||
error_log("✅ 会签消息卡片更新成功: message_id={$message->message_id}, approved={$approvedCount}/{$totalCount}");
|
||||
|
||||
// 记录成功日志
|
||||
\app\model\FeishuMessageLog::log(
|
||||
\app\model\FeishuMessageLog::ACTION_UPDATE_CARD,
|
||||
$orderId,
|
||||
$nodeId,
|
||||
$message->user_id,
|
||||
$message->message_id,
|
||||
\app\model\FeishuMessageLog::STATUS_SUCCESS,
|
||||
$requestData,
|
||||
$response
|
||||
);
|
||||
} else {
|
||||
error_log("❌ 会签消息卡片更新失败: message_id={$message->message_id}, error=" . json_encode($response));
|
||||
|
||||
// 记录失败日志
|
||||
\app\model\FeishuMessageLog::log(
|
||||
\app\model\FeishuMessageLog::ACTION_UPDATE_CARD,
|
||||
$orderId,
|
||||
$nodeId,
|
||||
$message->user_id,
|
||||
$message->message_id,
|
||||
\app\model\FeishuMessageLog::STATUS_FAILED,
|
||||
$requestData,
|
||||
$response,
|
||||
$response['msg'] ?? 'Unknown error'
|
||||
);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("❌ 更新会签消息卡片异常: message_id={$message->message_id}, error=" . $e->getMessage());
|
||||
|
||||
// 记录异常日志
|
||||
\app\model\FeishuMessageLog::log(
|
||||
\app\model\FeishuMessageLog::ACTION_UPDATE_CARD,
|
||||
$orderId,
|
||||
$nodeId,
|
||||
$message->user_id,
|
||||
$message->message_id,
|
||||
\app\model\FeishuMessageLog::STATUS_FAILED,
|
||||
null,
|
||||
null,
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
error_log('❌ updateMessageCardsForPartialApprove 异常: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文本消息(用于自审批场景)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/support/bootstrap.php';
|
||||
|
||||
$order = \app\model\ApprovalOrder::find(27);
|
||||
if ($order) {
|
||||
echo "订单ID: " . $order->id . "\n";
|
||||
echo "标题: " . $order->title . "\n";
|
||||
echo "申请人ID: " . $order->applicant_id . "\n";
|
||||
echo "状态: " . $order->status . "\n";
|
||||
echo "当前节点: " . json_encode($order->current_nodes) . "\n";
|
||||
echo "流程ID: " . $order->flow_id . "\n";
|
||||
echo "\nform_data:\n";
|
||||
print_r($order->form_data);
|
||||
|
||||
echo "\n\n审批记录:\n";
|
||||
$records = \app\model\ApprovalRecord::where('order_id', 27)->get();
|
||||
foreach ($records as $record) {
|
||||
echo "ID: {$record->id}, 节点ID: {$record->node_id}, 审批人: {$record->approver_id}, 操作: {$record->action}\n";
|
||||
}
|
||||
|
||||
echo "\n\n飞书消息映射:\n";
|
||||
$messages = \app\model\FeishuMessage::where('order_id', 27)->get();
|
||||
foreach ($messages as $msg) {
|
||||
echo "ID: {$msg->id}, 节点ID: {$msg->node_id}, 用户ID: {$msg->user_id}, 消息ID: {$msg->message_id}, 状态: {$msg->status}\n";
|
||||
}
|
||||
} else {
|
||||
echo "订单不存在\n";
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 会签场景测试脚本
|
||||
*
|
||||
* 测试场景:
|
||||
* 1. 选择3个审批人提交申请
|
||||
* 2. 第一个审批人通过 → 状态仍为 pending
|
||||
* 3. 第二个审批人通过 → 状态仍为 pending
|
||||
* 4. 第三个审批人通过 → 状态变为 approved
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/support/bootstrap.php';
|
||||
|
||||
use app\model\User;
|
||||
use app\model\ApprovalFlow;
|
||||
use app\model\ApprovalNode;
|
||||
use app\model\ApprovalOrder;
|
||||
use app\model\ApprovalRecord;
|
||||
use app\model\BusinessTrip;
|
||||
use app\service\BusinessTripService;
|
||||
use app\service\ApprovalService;
|
||||
use support\Db;
|
||||
|
||||
echo "=== 会签场景测试 ===\n\n";
|
||||
|
||||
// 获取测试用户
|
||||
$applicant = User::where('status', 1)->first();
|
||||
if (!$applicant) {
|
||||
echo "❌ 没有找到用户\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "👤 申请人: {$applicant->name} (ID: {$applicant->id})\n";
|
||||
|
||||
// 获取审批人
|
||||
$approvers = User::where('status', 1)
|
||||
->where('id', '!=', $applicant->id)
|
||||
->limit(3)
|
||||
->get();
|
||||
|
||||
if ($approvers->count() < 3) {
|
||||
echo "❌ 审批人数量不足,需要至少3个\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "👥 审批人: " . $approvers->pluck('name')->join(', ') . "\n\n";
|
||||
|
||||
Db::transaction(function () use ($applicant, $approvers) {
|
||||
$businessTripService = new \app\service\BusinessTripService();
|
||||
|
||||
// 1. 创建草稿
|
||||
echo "【步骤1】创建草稿\n";
|
||||
$draft = $businessTripService->createDraft($applicant->id, [
|
||||
'reason' => '会签测试',
|
||||
'itineraries' => [
|
||||
[
|
||||
'start_time' => date('Y-m-d H:i:s'),
|
||||
'end_time' => date('Y-m-d H:i:s', strtotime('+1 day')),
|
||||
'departure' => '长沙',
|
||||
'destination' => '北京',
|
||||
'transport' => '飞机',
|
||||
],
|
||||
],
|
||||
]);
|
||||
echo "✅ 草稿创建成功: ID={$draft->id}\n\n";
|
||||
|
||||
// 2. 提交申请
|
||||
echo "【步骤2】提交申请(会签,3个审批人)\n";
|
||||
$approverIds = $approvers->pluck('id')->toArray();
|
||||
$businessTrip = $businessTripService->submit($applicant->id, $draft->id, $approverIds, []);
|
||||
|
||||
$approvalOrder = $businessTrip->approvalOrder;
|
||||
echo "✅ 申请提交成功\n";
|
||||
echo " 订单ID: {$approvalOrder->id}\n";
|
||||
echo " 当前状态: {$approvalOrder->status}\n";
|
||||
echo " 当前节点: " . json_encode($approvalOrder->current_nodes) . "\n";
|
||||
echo " 审批人: " . json_encode($approvalOrder->form_data['approvers'] ?? []) . "\n\n";
|
||||
|
||||
// 3. 第一个审批人通过
|
||||
echo "【步骤3】第一个审批人通过\n";
|
||||
$approver1 = $approvers[0];
|
||||
$approvalService = new \app\service\ApprovalService();
|
||||
$order1 = $approvalService->approve($approvalOrder->id, $approver1->id);
|
||||
echo " 审批人: {$approver1->name}\n";
|
||||
echo " 订单状态: {$order1->status}\n";
|
||||
echo " 审批记录数: " . ApprovalRecord::where('order_id', $order1->id)->where('action', 'approve')->count() . "\n";
|
||||
echo " 预期: 状态仍为 pending(需要其他人审批)\n\n";
|
||||
|
||||
// 4. 第二个审批人通过
|
||||
echo "【步骤4】第二个审批人通过\n";
|
||||
$approver2 = $approvers[1];
|
||||
$order2 = $approvalService->approve($approvalOrder->id, $approver2->id);
|
||||
echo " 审批人: {$approver2->name}\n";
|
||||
echo " 订单状态: {$order2->status}\n";
|
||||
echo " 审批记录数: " . ApprovalRecord::where('order_id', $order2->id)->where('action', 'approve')->count() . "\n";
|
||||
echo " 预期: 状态仍为 pending(需要最后一人审批)\n\n";
|
||||
|
||||
// 5. 第三个审批人通过
|
||||
echo "【步骤5】第三个审批人通过\n";
|
||||
$approver3 = $approvers[2];
|
||||
$order3 = $approvalService->approve($approvalOrder->id, $approver3->id);
|
||||
echo " 审批人: {$approver3->name}\n";
|
||||
echo " 订单状态: {$order3->status}\n";
|
||||
echo " 审批记录数: " . ApprovalRecord::where('order_id', $order3->id)->where('action', 'approve')->count() . "\n";
|
||||
echo " 预期: 状态变为 approved(所有人都已审批)\n\n";
|
||||
});
|
||||
|
||||
// 6. 检查消息日志
|
||||
echo "【检查】消息日志\n";
|
||||
$logCount = \app\model\FeishuMessageLog::where('order_id', $approvalOrder->id)->count();
|
||||
echo " 消息日志数量: {$logCount}\n";
|
||||
|
||||
if ($logCount > 0) {
|
||||
$logs = \app\model\FeishuMessageLog::where('order_id', $approvalOrder->id)
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
foreach ($logs as $log) {
|
||||
echo " - {$log->action}: {$log->status} (user_id={$log->user_id})\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n=== 测试完成 ===\n";
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 审批和消息模块测试脚本
|
||||
*
|
||||
* 使用方法:docker exec x-server-backend php /app/test_approval_message.php
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/support/bootstrap.php';
|
||||
|
||||
use app\model\User;
|
||||
use app\model\ApprovalFlow;
|
||||
use app\model\ApprovalNode;
|
||||
use app\model\ApprovalOrder;
|
||||
use app\model\ApprovalRecord;
|
||||
use app\model\BusinessTrip;
|
||||
use app\model\FeishuMessage;
|
||||
use app\model\FeishuMessageLog;
|
||||
use app\service\ApprovalService;
|
||||
use app\service\FeishuNotificationService;
|
||||
use support\Db;
|
||||
|
||||
echo "=== 审批和消息模块测试 ===\n\n";
|
||||
|
||||
// 测试1:检查基础数据
|
||||
echo "【测试1】检查基础数据\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
$userCount = User::where('status', 1)->count();
|
||||
echo "✅ 用户数量: {$userCount}\n";
|
||||
|
||||
$flow = ApprovalFlow::where('business_type', 'business_trip')->where('status', 1)->first();
|
||||
if ($flow) {
|
||||
echo "✅ 审批流程: {$flow->name} (ID: {$flow->id})\n";
|
||||
|
||||
$nodes = ApprovalNode::where('flow_id', $flow->id)->get();
|
||||
foreach ($nodes as $node) {
|
||||
echo " - 节点: {$node->name}, 类型: {$node->type}, 审批人: " . json_encode($node->approvers) . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "❌ 未找到审批流程\n";
|
||||
}
|
||||
|
||||
// 测试2:检查现有审批订单
|
||||
echo "\n【测试2】检查现有审批订单\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
$pendingOrders = ApprovalOrder::where('status', 'pending')->get();
|
||||
echo "✅ 待审批订单数量: {$pendingOrders->count()}\n";
|
||||
|
||||
foreach ($pendingOrders as $order) {
|
||||
$applicant = User::find($order->applicant_id);
|
||||
$formData = $order->form_data ?? [];
|
||||
$approvers = $formData['approvers'] ?? [];
|
||||
|
||||
echo " 订单 {$order->id}: {$order->title}\n";
|
||||
echo " 申请人: {$applicant->name}\n";
|
||||
echo " 审批人: " . json_encode($approvers) . "\n";
|
||||
echo " 当前节点: " . json_encode($order->current_nodes) . "\n";
|
||||
|
||||
// 检查审批记录
|
||||
$records = ApprovalRecord::where('order_id', $order->id)->get();
|
||||
echo " 审批记录数量: {$records->count()}\n";
|
||||
|
||||
// 检查消息映射
|
||||
$messages = FeishuMessage::where('order_id', $order->id)->get();
|
||||
echo " 消息映射数量: {$messages->count()}\n";
|
||||
}
|
||||
|
||||
// 测试3:验证节点完成逻辑
|
||||
echo "\n【测试3】验证节点完成逻辑\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
// 使用反射来测试私有方法
|
||||
$approvalService = new ApprovalService();
|
||||
$reflection = new ReflectionClass($approvalService);
|
||||
$method = $reflection->getMethod('isNodeCompleted');
|
||||
$method->setAccessible(true);
|
||||
|
||||
// 测试订单132(待审批,节点7)
|
||||
echo "订单132(节点7,会签):\n";
|
||||
try {
|
||||
$isCompleted = $method->invoke($approvalService, 132, 7);
|
||||
echo " 节点是否完成: " . ($isCompleted ? '是' : '否') . "\n";
|
||||
|
||||
// 查看审批记录
|
||||
$records = ApprovalRecord::where('order_id', 132)
|
||||
->where('node_id', 7)
|
||||
->where('action', 'approve')
|
||||
->get();
|
||||
echo " 已审批人数: {$records->count()}\n";
|
||||
|
||||
// 查看form_data中的审批人
|
||||
$order = ApprovalOrder::find(132);
|
||||
$formData = $order->form_data ?? [];
|
||||
$approvers = $formData['approvers'] ?? [];
|
||||
echo " 需要审批人数: " . count($approvers) . "\n";
|
||||
echo " 审批人列表: " . json_encode($approvers) . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo " ❌ 错误: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
// 测试4:检查消息映射
|
||||
echo "\n【测试4】检查消息映射\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
$feishuMessages = FeishuMessage::all();
|
||||
echo "消息映射总数: {$feishuMessages->count()}\n";
|
||||
|
||||
// 测试5:检查消息日志
|
||||
echo "\n【测试5】检查消息日志\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
$logCount = FeishuMessageLog::count();
|
||||
echo "消息日志总数: {$logCount}\n";
|
||||
|
||||
// 测试6:验证或签场景配置
|
||||
echo "\n【测试6】验证不同节点类型配置\n";
|
||||
echo "-------------------------------------------\n";
|
||||
|
||||
echo "当前需要测试的场景:\n";
|
||||
echo "1. 单人审批 (approver): 1人通过即可\n";
|
||||
echo "2. 会签 (and_approve): 所有人通过\n";
|
||||
echo "3. 或签 (or_approve): 1人通过即可\n";
|
||||
|
||||
echo "\n建议测试步骤:\n";
|
||||
echo "-------------------------------------------\n";
|
||||
echo "1. 修改节点类型为 'approver' 或 'or_approve'\n";
|
||||
echo "2. 选择多个审批人提交申请\n";
|
||||
echo "3. 测试第一个审批人通过\n";
|
||||
echo "4. 检查消息日志和消息映射\n";
|
||||
echo "5. 验证其他审批人的消息是否更新\n";
|
||||
|
||||
echo "\n=== 测试完成 ===\n";
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/support/bootstrap.php';
|
||||
|
||||
use app\model\ApprovalOrder;
|
||||
use app\service\ApprovalService;
|
||||
|
||||
echo "====================================\n";
|
||||
echo "测试审批详情 - 实际审批人显示\n";
|
||||
echo "====================================\n\n";
|
||||
|
||||
$service = new ApprovalService();
|
||||
|
||||
// 测试获取一个有实际审批人的订单详情
|
||||
$order = ApprovalOrder::whereNotNull('form_data')
|
||||
->whereNotNull('form_data->approvers')
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
if (!$order) {
|
||||
echo "没有找到包含 form_data.approvers 的订单\n";
|
||||
echo "创建一个测试订单...\n";
|
||||
|
||||
// 创建测试订单
|
||||
$order = ApprovalOrder::create([
|
||||
'business_type' => 'business_trip',
|
||||
'business_id' => 9999,
|
||||
'title' => '实际审批人测试',
|
||||
'applicant_id' => 1,
|
||||
'status' => 'pending',
|
||||
'current_nodes' => [7],
|
||||
'flow_id' => 2,
|
||||
'form_data' => [
|
||||
'reason' => '测试实际审批人显示',
|
||||
'approvers' => [
|
||||
['id' => 4, 'name' => '闫继光00022'],
|
||||
['id' => 79, 'name' => '测试用户79']
|
||||
],
|
||||
'cc_users' => [
|
||||
['id' => 1, 'name' => '李佳博']
|
||||
],
|
||||
'start_date' => '2026-03-01',
|
||||
'end_date' => '2026-03-05',
|
||||
'destination' => '北京',
|
||||
],
|
||||
]);
|
||||
|
||||
echo "创建测试订单 ID: {$order->id}\n";
|
||||
}
|
||||
|
||||
echo "测试订单 ID: {$order->id}\n";
|
||||
echo "标题: {$order->title}\n";
|
||||
echo "\n";
|
||||
|
||||
// 测试 getDetail 方法
|
||||
echo "调用 getDetail 方法...\n";
|
||||
$detail = $service->getDetail($order->id);
|
||||
|
||||
echo "\n========== 订单基本信息 ==========\n";
|
||||
echo "ID: " . $detail['id'] . "\n";
|
||||
echo "标题: " . $detail['title'] . "\n";
|
||||
echo "申请人: " . ($detail['applicant']['name'] ?? 'N/A') . "\n";
|
||||
echo "状态: " . $detail['status'] . "\n";
|
||||
|
||||
echo "\n========== 实际审批人 ==========\n";
|
||||
if (isset($detail['actual_approvers'])) {
|
||||
foreach ($detail['actual_approvers'] as $approver) {
|
||||
echo "- ID: {$approver['id']}, 姓名: {$approver['name']}\n";
|
||||
}
|
||||
} else {
|
||||
echo "无实际审批人数据\n";
|
||||
}
|
||||
|
||||
echo "\n========== 实际抄送人 ==========\n";
|
||||
if (isset($detail['actual_cc_users'])) {
|
||||
foreach ($detail['actual_cc_users'] as $ccUser) {
|
||||
echo "- ID: {$ccUser['id']}, 姓名: {$ccUser['name']}\n";
|
||||
}
|
||||
} else {
|
||||
echo "无实际抄送人数据\n";
|
||||
}
|
||||
|
||||
echo "\n========== 节点信息(含实际审批人) ==========\n";
|
||||
if (isset($detail['flow']['nodes'])) {
|
||||
foreach ($detail['flow']['nodes'] as $node) {
|
||||
echo "\n节点 ID: {$node['id']}, 名称: {$node['name']}, 类型: {$node['type']}\n";
|
||||
|
||||
if (isset($node['actual_approvers'])) {
|
||||
echo " 实际审批人:\n";
|
||||
foreach ($node['actual_approvers'] as $approver) {
|
||||
echo " - ID: {$approver['id']}, 姓名: {$approver['name']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($node['approver_statuses'])) {
|
||||
echo " 审批状态:\n";
|
||||
foreach ($node['approver_statuses'] as $status) {
|
||||
$userName = $status['user']['name'] ?? 'Unknown';
|
||||
$actionName = $status['action_name'];
|
||||
echo " - {$userName}: {$actionName}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($node['actual_cc_users'])) {
|
||||
echo " 实际抄送人:\n";
|
||||
foreach ($node['actual_cc_users'] as $ccUser) {
|
||||
echo " - ID: {$ccUser['id']}, 姓名: {$ccUser['name']}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n====================================\n";
|
||||
echo "测试完成\n";
|
||||
echo "====================================\n";
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 测试 emoji 编码修复
|
||||
* 验证 HttpClient 能正确处理 emoji 字符
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/support/bootstrap.php';
|
||||
|
||||
use App\Util\HttpClient;
|
||||
|
||||
echo "====================================\n";
|
||||
echo "测试 Emoji 编码修复\n";
|
||||
echo "====================================\n\n";
|
||||
|
||||
// 测试1: 验证 JSON 编码包含 emoji
|
||||
echo "测试1: JSON 编码测试\n";
|
||||
echo "----------------------------------------\n";
|
||||
|
||||
$testData = [
|
||||
'title' => '🔔 审批通知',
|
||||
'content' => '📋 您有一个新的审批申请待处理',
|
||||
'status' => '✅ 通过',
|
||||
'elements' => [
|
||||
['tag' => 'text', 'text' => '📅 时间: 2026-03-01'],
|
||||
['tag' => 'text', 'text' => '👤 申请人: 张三'],
|
||||
]
|
||||
];
|
||||
|
||||
echo "原始数据:\n";
|
||||
print_r($testData);
|
||||
echo "\n";
|
||||
|
||||
// 使用 JSON_UNESCAPED_UNICODE 编码
|
||||
$encoded = json_encode($testData, JSON_UNESCAPED_UNICODE);
|
||||
echo "JSON_UNESCAPED_UNICODE 编码结果:\n";
|
||||
echo $encoded . "\n\n";
|
||||
|
||||
// 检查 emoji 是否被转义
|
||||
$hasEscapedEmoji = strpos($encoded, '\\u') !== false;
|
||||
if ($hasEscapedEmoji) {
|
||||
echo "❌ FAIL: Emoji 被转义了!\n";
|
||||
} else {
|
||||
echo "✅ PASS: Emoji 保持原样\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
// 测试2: 验证 HttpClient 的编码
|
||||
echo "测试2: HttpClient 编码测试\n";
|
||||
echo "----------------------------------------\n";
|
||||
|
||||
// 使用反射访问 HttpClient 的 httpPost 方法,但不实际发送请求
|
||||
// 我们只验证 JSON 编码部分
|
||||
|
||||
$testPayload = [
|
||||
'receive_id' => 'test_user',
|
||||
'msg_type' => 'interactive',
|
||||
'content' => [
|
||||
'card' => [
|
||||
'header' => [
|
||||
'title' => [
|
||||
'content' => '🔔 审批通知',
|
||||
'tag' => 'plain_text'
|
||||
]
|
||||
],
|
||||
'elements' => [
|
||||
['tag' => 'text', 'text' => '📋 请审批']
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
echo "测试 payload:\n";
|
||||
echo json_encode($testPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n\n";
|
||||
|
||||
// 模拟 HttpClient 的编码过程
|
||||
$jsonPayload = json_encode($testPayload, JSON_UNESCAPED_UNICODE);
|
||||
echo "HttpClient 编码后的 JSON:\n";
|
||||
echo $jsonPayload . "\n\n";
|
||||
|
||||
// 验证 emoji 没有被转义
|
||||
$emojis = ['🔔', '📋', '✅', '📅', '👤'];
|
||||
$allEmojiIntact = true;
|
||||
foreach ($emojis as $emoji) {
|
||||
if (strpos($jsonPayload, $emoji) === false) {
|
||||
echo "❌ Emoji {$emoji} 未找到或被转义\n";
|
||||
$allEmojiIntact = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($allEmojiIntact) {
|
||||
echo "✅ PASS: 所有 emoji 保持原样\n";
|
||||
} else {
|
||||
echo "❌ FAIL: 部分 emoji 被转义\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
// 测试3: 对比默认编码和修复后的编码
|
||||
echo "测试3: 编码方式对比\n";
|
||||
echo "----------------------------------------\n";
|
||||
|
||||
$testString = ['emoji' => '🔔 通知'];
|
||||
|
||||
$defaultEncoded = json_encode($testString);
|
||||
$fixedEncoded = json_encode($testString, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
echo "默认编码: " . $defaultEncoded . "\n";
|
||||
echo "修复编码: " . $fixedEncoded . "\n\n";
|
||||
|
||||
if ($defaultEncoded !== $fixedEncoded) {
|
||||
echo "检测到差异:\n";
|
||||
echo "- 默认编码将 emoji 转义为 Unicode 序列\n";
|
||||
echo "- 修复后编码保持 emoji 原样\n";
|
||||
|
||||
if (strpos($fixedEncoded, '🔔') !== false) {
|
||||
echo "\n✅ PASS: 修复成功\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
echo "====================================\n";
|
||||
echo "测试完成\n";
|
||||
echo "====================================\n";
|
||||
@@ -0,0 +1,580 @@
|
||||
<?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";
|
||||
Reference in New Issue
Block a user