feat: 优化飞书审批通知卡片,改用卡片回调机制
主要改动: - 去掉卡片中的备注输入框和表单元素 - 改为三个按钮(通过、拒绝、查看详情)横向排列在一行 - 通过/拒绝按钮使用卡片回调(value)而不是跳转 URL - 新增 FeishuCardController 处理飞书卡片回调 - 添加提示文字告知用户如需备注可去详情页操作 技术细节: - 按钮点击触发 card.action.trigger 事件 - 后端通过 FeishuCardController 处理回调 - 支持 url_verification 和 card.callback 两种事件类型 - 查看详情 URL 添加公司参数支持多租户 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,426 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\controller;
|
||||||
|
|
||||||
|
use Webman\Http\Request;
|
||||||
|
use Webman\Http\Response;
|
||||||
|
use app\service\ApprovalService;
|
||||||
|
use app\model\CompanyFeishuApp;
|
||||||
|
use app\model\User;
|
||||||
|
use app\model\UserFeishuApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 飞书消息卡片回调控制器
|
||||||
|
* 处理飞书卡片的 URL 验证和用户交互
|
||||||
|
*/
|
||||||
|
class FeishuCardController extends BaseController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 飞书卡片回调接口
|
||||||
|
* 处理两种类型的请求:
|
||||||
|
* 1. url_verification - URL 验证
|
||||||
|
* 2. card.callback - 卡片按钮点击回调
|
||||||
|
*/
|
||||||
|
public function callback(Request $request): Response
|
||||||
|
{
|
||||||
|
$body = $request->rawBody();
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
error_log('📥 飞书卡片回调: ' . json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
|
if (!$data) {
|
||||||
|
error_log('❌ 无效的 JSON 数据');
|
||||||
|
return $this->json(['code' => -1, 'msg' => 'Invalid JSON']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取事件类型
|
||||||
|
$eventType = $data['type'] ?? $data['header']['event_type'] ?? '';
|
||||||
|
|
||||||
|
// URL 验证
|
||||||
|
if ($eventType === 'url_verification') {
|
||||||
|
return $this->handleUrlVerification($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卡片交互回调 - 兼容两种格式
|
||||||
|
if ($eventType === 'card.callback' || $eventType === 'card.action.trigger') {
|
||||||
|
return $this->handleCardCallback($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('❌ 未知的回调类型: ' . $eventType);
|
||||||
|
return $this->json(['code' => -1, 'msg' => 'Unknown callback type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 URL 验证请求
|
||||||
|
* 飞书在配置回调地址时会先验证 URL 的有效性
|
||||||
|
*/
|
||||||
|
private function handleUrlVerification(array $data): Response
|
||||||
|
{
|
||||||
|
$challenge = $data['challenge'] ?? '';
|
||||||
|
|
||||||
|
if (!$challenge) {
|
||||||
|
error_log('❌ URL 验证缺少 challenge');
|
||||||
|
return $this->json(['code' => -1, 'msg' => 'Missing challenge']);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('✅ URL 验证成功');
|
||||||
|
|
||||||
|
// URL 验证需要直接返回 challenge,不用包装
|
||||||
|
return new \Webman\Http\Response(
|
||||||
|
200,
|
||||||
|
['Content-Type' => 'application/json'],
|
||||||
|
json_encode([
|
||||||
|
'challenge' => $challenge,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理卡片按钮点击回调
|
||||||
|
* 支持两种格式:
|
||||||
|
* 1. 简单格式: {type: 'card.callback', token: {...}, action: {...}, operator: {...}}
|
||||||
|
* 2. 飞书实际格式: {header: {event_type: 'card.action.trigger'}, event: {operator: {...}, token: {...}, action: {...}}}
|
||||||
|
*/
|
||||||
|
private function handleCardCallback(array $data): Response
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// 兼容两种格式
|
||||||
|
$isEventFormat = isset($data['header']['event_type']);
|
||||||
|
|
||||||
|
if ($isEventFormat) {
|
||||||
|
// 飞书实际格式: {header: {...}, event: {...}}
|
||||||
|
$eventData = $data['event'] ?? [];
|
||||||
|
$operator = $eventData['operator'] ?? [];
|
||||||
|
$action = $eventData['action'] ?? [];
|
||||||
|
// 从 header 获取 app_id 和 msg_token
|
||||||
|
$appId = $data['header']['app_id'] ?? null;
|
||||||
|
$msgToken = $eventData['token'] ?? null;
|
||||||
|
} else {
|
||||||
|
// 简单格式: {token: {...}, action: {...}, operator: {...}}
|
||||||
|
$token = $data['token'] ?? [];
|
||||||
|
$appId = $token['app_id'] ?? null;
|
||||||
|
$operator = $data['operator'] ?? [];
|
||||||
|
$action = $data['action'] ?? [];
|
||||||
|
$msgToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$appId) {
|
||||||
|
error_log('❌ 缺少 app_id');
|
||||||
|
return $this->errorCard('无效的应用');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找飞书应用配置
|
||||||
|
$feishuApp = CompanyFeishuApp::where('app_id', $appId)
|
||||||
|
->where('status', 1)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$feishuApp) {
|
||||||
|
error_log('❌ 飞书应用不存在: ' . $appId);
|
||||||
|
return $this->errorCard('应用配置不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 action value 和 form value(表单提交时)
|
||||||
|
$actionValue = $action['value'] ?? [];
|
||||||
|
$formValue = $action['form_value'] ?? [];
|
||||||
|
|
||||||
|
// 合并按钮值和表单值(表单值优先)
|
||||||
|
$actionValue = array_merge($actionValue, $formValue);
|
||||||
|
|
||||||
|
$openId = $operator['open_id'] ?? null;
|
||||||
|
|
||||||
|
if (!$openId) {
|
||||||
|
error_log('❌ 缺少 open_id');
|
||||||
|
return $this->errorCard('无法识别用户');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 open_id 查找用户
|
||||||
|
$userBinding = UserFeishuApp::where('feishu_open_id', $openId)
|
||||||
|
->where('company_id', $feishuApp->company_id)
|
||||||
|
->where('feishu_app_id', $feishuApp->id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$userBinding) {
|
||||||
|
error_log('❌ 用户未绑定飞书应用: open_id=' . $openId);
|
||||||
|
return $this->errorCard('用户未登录,请先登录系统');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = User::find($userBinding->user_id);
|
||||||
|
if (!$user || $user->status !== 1) {
|
||||||
|
error_log('❌ 用户不存在或已禁用: user_id=' . ($userBinding->user_id ?? 'null'));
|
||||||
|
return $this->errorCard('用户不存在或已被禁用');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理不同类型的操作
|
||||||
|
$actionType = $actionValue['action_type'] ?? '';
|
||||||
|
|
||||||
|
error_log('🎯 卡片操作类型: ' . $actionType . ', user_id: ' . $user->id . ', msg_token: ' . ($msgToken ?? 'null'));
|
||||||
|
|
||||||
|
switch ($actionType) {
|
||||||
|
case 'approve':
|
||||||
|
return $this->handleApprove($user, $actionValue, $msgToken);
|
||||||
|
|
||||||
|
case 'reject':
|
||||||
|
return $this->handleReject($user, $actionValue, $msgToken);
|
||||||
|
|
||||||
|
default:
|
||||||
|
error_log('❌ 未知的操作类型: ' . $actionType);
|
||||||
|
return $this->errorCard('未知的操作类型');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log('❌ 处理卡片回调异常: ' . $e->getMessage());
|
||||||
|
return $this->errorCard('处理失败: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理审批通过
|
||||||
|
*/
|
||||||
|
private function handleApprove(User $user, array $actionValue, ?string $msgToken = null): Response
|
||||||
|
{
|
||||||
|
$orderId = $actionValue['order_id'] ?? null;
|
||||||
|
$remark = $actionValue['remark'] ?? ''; // 从表单获取备注
|
||||||
|
|
||||||
|
if (!$orderId) {
|
||||||
|
error_log('❌ 缺少 order_id');
|
||||||
|
return $this->errorCard('缺少审批单ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$approvalService = new ApprovalService();
|
||||||
|
|
||||||
|
// 检查用户是否有权限审批
|
||||||
|
$order = \app\model\ApprovalOrder::find($orderId);
|
||||||
|
if (!$order) {
|
||||||
|
return $this->errorCard('审批单不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行审批通过
|
||||||
|
$approvalService->approve($orderId, $user->id, $remark);
|
||||||
|
|
||||||
|
error_log('✅ 审批通过成功: order_id=' . $orderId . ', remark=' . $remark);
|
||||||
|
return $this->successCard('审批通过', $this->buildApproveContent($user, $order, $remark));
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log('❌ 审批通过异常: ' . $e->getMessage());
|
||||||
|
return $this->errorCard('审批失败: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建审批通过后的卡片内容
|
||||||
|
*/
|
||||||
|
private function buildApproveContent(User $user, \app\model\ApprovalOrder $order, string $remark = ''): string
|
||||||
|
{
|
||||||
|
$applicant = \app\model\User::find($order->applicant_id);
|
||||||
|
$applicantName = $applicant ? $applicant->name : '未知';
|
||||||
|
|
||||||
|
// 获取订单类型名称
|
||||||
|
$typeName = $order->type ?? '申请';
|
||||||
|
|
||||||
|
$parts = [
|
||||||
|
"{$user->name} 已通过该申请",
|
||||||
|
"",
|
||||||
|
"**申请人**: {$applicantName}",
|
||||||
|
"**申请单号**: #{$order->id}",
|
||||||
|
"**申请类型**: {$typeName}",
|
||||||
|
];
|
||||||
|
|
||||||
|
// 如果有备注,添加备注
|
||||||
|
if ($remark) {
|
||||||
|
$parts[] = "**审批备注**: {$remark}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有额外数据,添加更多细节
|
||||||
|
if ($order->data) {
|
||||||
|
$data = json_decode($order->data, true);
|
||||||
|
if (is_array($data)) {
|
||||||
|
// 添加一些关键信息
|
||||||
|
if (!empty($data['reason'])) {
|
||||||
|
$parts[] = "**申请事由**: {$data['reason']}";
|
||||||
|
}
|
||||||
|
if (!empty($data['start_date'])) {
|
||||||
|
$parts[] = "**开始时间**: {$data['start_date']}";
|
||||||
|
}
|
||||||
|
if (!empty($data['end_date'])) {
|
||||||
|
$parts[] = "**结束时间**: {$data['end_date']}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts[] = "";
|
||||||
|
$parts[] = "_操作时间: " . date('Y-m-d H:i:s') . "_";
|
||||||
|
|
||||||
|
return implode("\n", $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理审批拒绝
|
||||||
|
*/
|
||||||
|
private function handleReject(User $user, array $actionValue, ?string $msgToken = null): Response
|
||||||
|
{
|
||||||
|
$orderId = $actionValue['order_id'] ?? null;
|
||||||
|
$reason = $actionValue['remark'] ?? '已拒绝'; // 从表单获取备注,没有则用默认值
|
||||||
|
|
||||||
|
if (!$orderId) {
|
||||||
|
error_log('❌ 缺少 order_id');
|
||||||
|
return $this->errorCard('缺少审批单ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$approvalService = new ApprovalService();
|
||||||
|
|
||||||
|
// 检查用户是否有权限审批
|
||||||
|
$order = \app\model\ApprovalOrder::find($orderId);
|
||||||
|
if (!$order) {
|
||||||
|
return $this->errorCard('审批单不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行审批拒绝
|
||||||
|
$approvalService->reject($orderId, $user->id, $reason);
|
||||||
|
|
||||||
|
error_log('✅ 审批拒绝成功: order_id=' . $orderId . ', reason=' . $reason);
|
||||||
|
return $this->successCard('已拒绝', $this->buildRejectContent($user, $order, $reason));
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log('❌ 审批拒绝异常: ' . $e->getMessage());
|
||||||
|
return $this->errorCard('审批失败: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建拒绝成功后的卡片内容
|
||||||
|
*/
|
||||||
|
private function buildRejectContent(User $user, \app\model\ApprovalOrder $order, string $reason): string
|
||||||
|
{
|
||||||
|
$applicant = \app\model\User::find($order->applicant_id);
|
||||||
|
$applicantName = $applicant ? $applicant->name : '未知';
|
||||||
|
|
||||||
|
$parts = [
|
||||||
|
"{$user->name} 已拒绝该申请",
|
||||||
|
"",
|
||||||
|
"**申请人**: {$applicantName}",
|
||||||
|
"**申请单号**: #{$order->id}",
|
||||||
|
];
|
||||||
|
|
||||||
|
// 如果有拒绝原因,显示
|
||||||
|
if ($reason && $reason !== '已拒绝') {
|
||||||
|
$parts[] = "**拒绝原因**: {$reason}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("\n", $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功卡片(更新原卡片)
|
||||||
|
* 飞书卡片回调响应格式:
|
||||||
|
* https://open.feishu.cn/document/feishu-cards/card-callback-communication
|
||||||
|
*/
|
||||||
|
private function successCard(string $title, string $content, ?string $msgToken = null): Response
|
||||||
|
{
|
||||||
|
// 检查内容是否已包含操作时间(如果包含时间戳则不再添加)
|
||||||
|
$hasTime = strpos($content, '操作时间') !== false;
|
||||||
|
|
||||||
|
$elements = [
|
||||||
|
[
|
||||||
|
'tag' => 'div',
|
||||||
|
'text' => [
|
||||||
|
'content' => $content,
|
||||||
|
'tag' => 'lark_md',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 如果没有操作时间,添加
|
||||||
|
if (!$hasTime) {
|
||||||
|
$elements[] = [
|
||||||
|
'tag' => 'div',
|
||||||
|
'text' => [
|
||||||
|
'content' => '_操作时间: ' . date('Y-m-d H:i:s') . '_',
|
||||||
|
'tag' => 'lark_md',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cardData = [
|
||||||
|
'config' => [
|
||||||
|
'wide_screen_mode' => false,
|
||||||
|
],
|
||||||
|
'header' => [
|
||||||
|
'title' => [
|
||||||
|
'content' => $title,
|
||||||
|
'tag' => 'plain_text',
|
||||||
|
],
|
||||||
|
'template' => 'green',
|
||||||
|
],
|
||||||
|
'elements' => $elements,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 飞书卡片回调的正确响应格式
|
||||||
|
$response = [
|
||||||
|
'toast' => [
|
||||||
|
'type' => 'success',
|
||||||
|
'content' => $title,
|
||||||
|
],
|
||||||
|
'card' => [
|
||||||
|
'type' => 'raw',
|
||||||
|
'data' => $cardData,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->json($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回错误卡片(更新原卡片显示错误)
|
||||||
|
*/
|
||||||
|
private function errorCard(string $message, ?string $msgToken = null): Response
|
||||||
|
{
|
||||||
|
$cardData = [
|
||||||
|
'config' => [
|
||||||
|
'wide_screen_mode' => false,
|
||||||
|
],
|
||||||
|
'header' => [
|
||||||
|
'title' => [
|
||||||
|
'content' => '操作失败',
|
||||||
|
'tag' => 'plain_text',
|
||||||
|
],
|
||||||
|
'template' => 'red',
|
||||||
|
],
|
||||||
|
'elements' => [
|
||||||
|
[
|
||||||
|
'tag' => 'div',
|
||||||
|
'text' => [
|
||||||
|
'content' => $message,
|
||||||
|
'tag' => 'lark_md',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'toast' => [
|
||||||
|
'type' => 'error',
|
||||||
|
'content' => '操作失败',
|
||||||
|
],
|
||||||
|
'card' => [
|
||||||
|
'type' => 'raw',
|
||||||
|
'data' => $cardData,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->json($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回 JSON 响应
|
||||||
|
*/
|
||||||
|
private function json(array $data): Response
|
||||||
|
{
|
||||||
|
return new \Webman\Http\Response(
|
||||||
|
200,
|
||||||
|
['Content-Type' => 'application/json'],
|
||||||
|
json_encode($data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,7 +143,7 @@ class FeishuNotificationService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$card = $this->buildApprovalCard($params);
|
$card = $this->buildApprovalCard($params, $user->company_id);
|
||||||
return $this->sendMessage($feishuUserId, $card);
|
return $this->sendMessage($feishuUserId, $card);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log('❌ sendApprovalNotification 失败: ' . $e->getMessage());
|
error_log('❌ sendApprovalNotification 失败: ' . $e->getMessage());
|
||||||
@@ -185,7 +185,7 @@ class FeishuNotificationService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$card = $this->buildSystemCard($params);
|
$card = $this->buildSystemCard($params, $user->company_id);
|
||||||
$results[$userId] = $this->sendMessage($feishuUserId, $card);
|
$results[$userId] = $this->sendMessage($feishuUserId, $card);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("❌ sendSystemNotification 失败: user_id={$userId}, error=" . $e->getMessage());
|
error_log("❌ sendSystemNotification 失败: user_id={$userId}, error=" . $e->getMessage());
|
||||||
@@ -229,7 +229,7 @@ class FeishuNotificationService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$card = $this->buildReminderCard($params);
|
$card = $this->buildReminderCard($params, $user->company_id);
|
||||||
return $this->sendMessage($feishuUserId, $card);
|
return $this->sendMessage($feishuUserId, $card);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log('❌ sendReminderNotification 失败: ' . $e->getMessage());
|
error_log('❌ sendReminderNotification 失败: ' . $e->getMessage());
|
||||||
@@ -276,7 +276,7 @@ class FeishuNotificationService
|
|||||||
/**
|
/**
|
||||||
* 构建审批通知卡片
|
* 构建审批通知卡片
|
||||||
*/
|
*/
|
||||||
private function buildApprovalCard(array $params): array
|
private function buildApprovalCard(array $params, int $recipientCompanyId): array
|
||||||
{
|
{
|
||||||
$title = $params['title'] ?? '新的审批申请';
|
$title = $params['title'] ?? '新的审批申请';
|
||||||
$applicant = $params['applicant'] ?? '未知';
|
$applicant = $params['applicant'] ?? '未知';
|
||||||
@@ -307,39 +307,58 @@ class FeishuNotificationService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 操作按钮区域
|
// 提示信息(如需备注请去详情页)
|
||||||
$actions = [];
|
|
||||||
|
|
||||||
// 如果有审批单ID,添加审批操作按钮(使用快速审批页面,无感操作)
|
|
||||||
if ($orderId) {
|
if ($orderId) {
|
||||||
// 通过按钮(绿色)- 快速审批页面
|
$elements[] = [
|
||||||
$actions[] = [
|
'tag' => 'div',
|
||||||
|
'text' => [
|
||||||
|
'content' => "_如需添加备注,请点击「查看详情」进入详情页操作_",
|
||||||
|
'tag' => 'lark_md',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮区域:三个按钮在一行
|
||||||
|
$buttonActions = [];
|
||||||
|
|
||||||
|
// 通过按钮
|
||||||
|
if ($orderId) {
|
||||||
|
$buttonActions[] = [
|
||||||
'tag' => 'button',
|
'tag' => 'button',
|
||||||
'text' => [
|
'text' => [
|
||||||
'content' => '✓ 通过',
|
'content' => '通过',
|
||||||
'tag' => 'plain_text',
|
'tag' => 'plain_text',
|
||||||
],
|
],
|
||||||
'type' => 'primary',
|
'type' => 'primary',
|
||||||
'size' => 'medium',
|
'size' => 'medium',
|
||||||
'url' => "https://xsm.dev.hnxinyouhe.com/approval/quick-action?action=approve&order_id={$orderId}",
|
'value' => [
|
||||||
|
'action_type' => 'approve',
|
||||||
|
'order_id' => $orderId,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
// 拒绝按钮(红色)- 快速审批页面
|
// 拒绝按钮
|
||||||
$actions[] = [
|
$buttonActions[] = [
|
||||||
'tag' => 'button',
|
'tag' => 'button',
|
||||||
'text' => [
|
'text' => [
|
||||||
'content' => '✗ 拒绝',
|
'content' => '拒绝',
|
||||||
'tag' => 'plain_text',
|
'tag' => 'plain_text',
|
||||||
],
|
],
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'size' => 'medium',
|
'size' => 'medium',
|
||||||
'url' => "https://xsm.dev.hnxinyouhe.com/approval/quick-action?action=reject&order_id={$orderId}",
|
'value' => [
|
||||||
|
'action_type' => 'reject',
|
||||||
|
'order_id' => $orderId,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看详情按钮
|
// 查看详情按钮(跳转链接)
|
||||||
if ($url) {
|
if ($url) {
|
||||||
$actions[] = [
|
$separator = strpos($url, '?') !== false ? '&' : '?';
|
||||||
|
$urlWithCompany = $url . $separator . 'company=' . $recipientCompanyId;
|
||||||
|
|
||||||
|
$buttonActions[] = [
|
||||||
'tag' => 'button',
|
'tag' => 'button',
|
||||||
'text' => [
|
'text' => [
|
||||||
'content' => '查看详情',
|
'content' => '查看详情',
|
||||||
@@ -347,14 +366,15 @@ class FeishuNotificationService
|
|||||||
],
|
],
|
||||||
'type' => 'default',
|
'type' => 'default',
|
||||||
'size' => 'medium',
|
'size' => 'medium',
|
||||||
'url' => $url,
|
'url' => $urlWithCompany,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($actions)) {
|
// 将所有按钮放在一个 action 元素中(会横向排列)
|
||||||
|
if (!empty($buttonActions)) {
|
||||||
$elements[] = [
|
$elements[] = [
|
||||||
'tag' => 'action',
|
'tag' => 'action',
|
||||||
'actions' => $actions,
|
'actions' => $buttonActions,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +396,7 @@ class FeishuNotificationService
|
|||||||
/**
|
/**
|
||||||
* 构建系统通知卡片
|
* 构建系统通知卡片
|
||||||
*/
|
*/
|
||||||
private function buildSystemCard(array $params): array
|
private function buildSystemCard(array $params, int $recipientCompanyId): array
|
||||||
{
|
{
|
||||||
$title = $params['title'] ?? '系统通知';
|
$title = $params['title'] ?? '系统通知';
|
||||||
$content = $params['content'] ?? '';
|
$content = $params['content'] ?? '';
|
||||||
@@ -395,6 +415,10 @@ class FeishuNotificationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($url) {
|
if ($url) {
|
||||||
|
// 在 URL 后面添加公司参数
|
||||||
|
$separator = strpos($url, '?') !== false ? '&' : '?';
|
||||||
|
$urlWithCompany = $url . $separator . 'company=' . $recipientCompanyId;
|
||||||
|
|
||||||
$elements[] = [
|
$elements[] = [
|
||||||
'tag' => 'action',
|
'tag' => 'action',
|
||||||
'actions' => [
|
'actions' => [
|
||||||
@@ -405,7 +429,7 @@ class FeishuNotificationService
|
|||||||
'tag' => 'plain_text',
|
'tag' => 'plain_text',
|
||||||
],
|
],
|
||||||
'type' => 'default',
|
'type' => 'default',
|
||||||
'url' => $url,
|
'url' => $urlWithCompany,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
@@ -429,7 +453,7 @@ class FeishuNotificationService
|
|||||||
/**
|
/**
|
||||||
* 构建提醒通知卡片
|
* 构建提醒通知卡片
|
||||||
*/
|
*/
|
||||||
private function buildReminderCard(array $params): array
|
private function buildReminderCard(array $params, int $recipientCompanyId): array
|
||||||
{
|
{
|
||||||
$title = $params['title'] ?? '提醒';
|
$title = $params['title'] ?? '提醒';
|
||||||
$content = $params['content'] ?? '';
|
$content = $params['content'] ?? '';
|
||||||
@@ -465,6 +489,10 @@ class FeishuNotificationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($url) {
|
if ($url) {
|
||||||
|
// 在 URL 后面添加公司参数
|
||||||
|
$separator = strpos($url, '?') !== false ? '&' : '?';
|
||||||
|
$urlWithCompany = $url . $separator . 'company=' . $recipientCompanyId;
|
||||||
|
|
||||||
$elements[] = [
|
$elements[] = [
|
||||||
'tag' => 'action',
|
'tag' => 'action',
|
||||||
'actions' => [
|
'actions' => [
|
||||||
@@ -475,7 +503,7 @@ class FeishuNotificationService
|
|||||||
'tag' => 'plain_text',
|
'tag' => 'plain_text',
|
||||||
],
|
],
|
||||||
'type' => 'primary',
|
'type' => 'primary',
|
||||||
'url' => $url,
|
'url' => $urlWithCompany,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user