AlertCard.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <?php
  2. namespace Longyi\Feishu\Messages;
  3. /**
  4. * 构造飞书交互卡片。
  5. *
  6. * 卡片比纯文本贵不了多少,但值班的人一眼就能看到订单号、金额和原因,
  7. * 并且能直接点进后台,所以告警统一走卡片。
  8. */
  9. class AlertCard
  10. {
  11. protected array $fields = [];
  12. protected ?string $description = null;
  13. protected ?string $buttonUrl = null;
  14. protected ?string $buttonText = null;
  15. protected ?string $footer = null;
  16. public function __construct(
  17. protected string $title,
  18. protected string $color = 'red',
  19. ) {}
  20. public static function make(string $title, string $color = 'red'): static
  21. {
  22. return new static($title, $color);
  23. }
  24. /**
  25. * 正文首段,一般写这条告警发生了什么、需要人做什么。
  26. */
  27. public function description(?string $description): static
  28. {
  29. $this->description = $description;
  30. return $this;
  31. }
  32. /**
  33. * 追加一个「字段名: 值」。值为空时自动跳过,避免卡片出现空行。
  34. */
  35. public function field(string $label, mixed $value): static
  36. {
  37. if ($value === null || $value === '') {
  38. return $this;
  39. }
  40. $this->fields[] = ['label' => $label, 'value' => (string) $value];
  41. return $this;
  42. }
  43. public function button(?string $text, ?string $url): static
  44. {
  45. if ($text && $url) {
  46. $this->buttonText = $text;
  47. $this->buttonUrl = $url;
  48. }
  49. return $this;
  50. }
  51. public function footer(?string $footer): static
  52. {
  53. $this->footer = $footer;
  54. return $this;
  55. }
  56. /**
  57. * 输出可直接 POST 给 webhook 的消息体。
  58. */
  59. public function toMessage(): array
  60. {
  61. $elements = [];
  62. if ($this->description) {
  63. $elements[] = [
  64. 'tag' => 'div',
  65. 'text' => ['tag' => 'lark_md', 'content' => $this->description],
  66. ];
  67. }
  68. if ($this->fields) {
  69. $elements[] = [
  70. 'tag' => 'div',
  71. 'fields' => array_map(fn (array $field) => [
  72. 'is_short' => true,
  73. 'text' => [
  74. 'tag' => 'lark_md',
  75. 'content' => "**{$field['label']}**\n{$field['value']}",
  76. ],
  77. ], $this->fields),
  78. ];
  79. }
  80. if ($this->buttonUrl) {
  81. $elements[] = [
  82. 'tag' => 'action',
  83. 'actions' => [[
  84. 'tag' => 'button',
  85. 'text' => ['tag' => 'plain_text', 'content' => $this->buttonText],
  86. 'url' => $this->buttonUrl,
  87. 'type' => 'primary',
  88. ]],
  89. ];
  90. }
  91. if ($this->footer) {
  92. $elements[] = [
  93. 'tag' => 'note',
  94. 'elements' => [['tag' => 'plain_text', 'content' => $this->footer]],
  95. ];
  96. }
  97. return [
  98. 'msg_type' => 'interactive',
  99. 'card' => [
  100. 'config' => ['wide_screen_mode' => true],
  101. 'header' => [
  102. 'template' => $this->color,
  103. 'title' => ['tag' => 'plain_text', 'content' => $this->title],
  104. ],
  105. 'elements' => $elements,
  106. ],
  107. ];
  108. }
  109. }