diff --git a/frontend/admin/src/i18n/index.ts b/frontend/admin/src/i18n/index.ts index 1b029618..22580831 100644 --- a/frontend/admin/src/i18n/index.ts +++ b/frontend/admin/src/i18n/index.ts @@ -3173,7 +3173,7 @@ const messages = { channel: '测试渠道', scene: '测试场景', target: '发送目标', - targetPlaceholder: '填写邮箱或 Telegram Chat ID', + targetPlaceholder: '填写邮箱、Telegram Chat ID 或飞书接收 ID', useFirstRecipient: '使用首个已配置接收人', hint: '测试发送会根据所选场景自动填充示例订单、充值或库存告警内容。', sendAction: '发送测试通知', @@ -3234,6 +3234,28 @@ const messages = { recipients: '接收人 Chat ID', recipientsPlaceholder: '每行一个 Chat ID,支持负数群组 ID', }, + feishu: { + title: '飞书推送', + subtitle: '使用飞书自建应用机器人发送通知(非群自定义 Webhook 机器人)', + enabled: '启用飞书通知', + appID: 'App ID', + appIDPlaceholder: '例如:cli_xxxxxxxxxxxxxxxx', + appSecret: 'App Secret', + appSecretPlaceholder: '填写飞书应用 App Secret', + appSecretHintKeep: '当前已配置 App Secret,留空将保持不变', + appSecretHintEmpty: '当前尚未配置 App Secret', + receiveIDType: '接收 ID 类型', + receiveIDTypes: { + chatID: '群聊 Chat ID(chat_id)', + openID: '用户 Open ID(open_id)', + userID: '用户 User ID(user_id)', + unionID: '用户 Union ID(union_id)', + email: '用户邮箱(email)', + }, + recipients: '接收 ID', + recipientsPlaceholder: '每行一个与所选类型匹配的接收 ID', + permissionHint: '飞书应用需开启机器人能力、发布版本并授予 im:message:send_as_bot 权限;向群聊发送时还需先将机器人加入群聊。', + }, }, scenes: { title: '通知场景', @@ -7550,7 +7572,7 @@ const messages = { channel: '測試渠道', scene: '測試場景', target: '發送目標', - targetPlaceholder: '填寫郵箱或 Telegram Chat ID', + targetPlaceholder: '填寫郵箱、Telegram Chat ID 或飛書接收 ID', useFirstRecipient: '使用首個已配置接收人', hint: '測試發送會依所選場景自動填入示例訂單、儲值或庫存告警內容。', sendAction: '發送測試通知', @@ -7611,6 +7633,28 @@ const messages = { recipients: '接收人 Chat ID', recipientsPlaceholder: '每行一個 Chat ID,支援負數群組 ID', }, + feishu: { + title: '飛書推送', + subtitle: '使用飛書自建應用機器人發送通知(非群自訂 Webhook 機器人)', + enabled: '啟用飛書通知', + appID: 'App ID', + appIDPlaceholder: '例如:cli_xxxxxxxxxxxxxxxx', + appSecret: 'App Secret', + appSecretPlaceholder: '填寫飛書應用 App Secret', + appSecretHintKeep: '目前已配置 App Secret,留空將保持不變', + appSecretHintEmpty: '目前尚未配置 App Secret', + receiveIDType: '接收 ID 類型', + receiveIDTypes: { + chatID: '群聊 Chat ID(chat_id)', + openID: '使用者 Open ID(open_id)', + userID: '使用者 User ID(user_id)', + unionID: '使用者 Union ID(union_id)', + email: '使用者郵箱(email)', + }, + recipients: '接收 ID', + recipientsPlaceholder: '每行一個與所選類型相符的接收 ID', + permissionHint: '飛書應用需開啟機器人能力、發佈版本並授予 im:message:send_as_bot 權限;向群聊發送時還需先將機器人加入群聊。', + }, }, scenes: { title: '通知場景', @@ -11927,7 +11971,7 @@ const messages = { channel: 'Test channel', scene: 'Test scene', target: 'Send target', - targetPlaceholder: 'Enter an email address or Telegram Chat ID', + targetPlaceholder: 'Enter an email, Telegram Chat ID, or Feishu recipient ID', useFirstRecipient: 'Use first configured recipient', hint: 'The selected scene automatically fills sample order, recharge, or inventory alert content.', sendAction: 'Send test notification', @@ -11988,6 +12032,28 @@ const messages = { recipients: 'Recipient Chat IDs', recipientsPlaceholder: 'One Chat ID per line, negative group IDs supported', }, + feishu: { + title: 'Feishu Notifications', + subtitle: 'Send through a Feishu custom-app bot (not a group custom webhook bot)', + enabled: 'Enable Feishu notifications', + appID: 'App ID', + appIDPlaceholder: 'e.g. cli_xxxxxxxxxxxxxxxx', + appSecret: 'App Secret', + appSecretPlaceholder: 'Enter the Feishu app secret', + appSecretHintKeep: 'An App Secret is configured. Leave blank to keep it.', + appSecretHintEmpty: 'No App Secret is configured yet.', + receiveIDType: 'Recipient ID type', + receiveIDTypes: { + chatID: 'Group Chat ID (chat_id)', + openID: 'User Open ID (open_id)', + userID: 'User ID (user_id)', + unionID: 'User Union ID (union_id)', + email: 'User email (email)', + }, + recipients: 'Recipient IDs', + recipientsPlaceholder: 'One recipient ID matching the selected type per line', + permissionHint: 'Enable bot capability, publish the app, and grant im:message:send_as_bot. Add the bot to a group before sending to that chat.', + }, }, scenes: { title: 'Notification Scenes', diff --git a/frontend/admin/src/views/admin/Notifications.vue b/frontend/admin/src/views/admin/Notifications.vue index 79994864..15e773cf 100644 --- a/frontend/admin/src/views/admin/Notifications.vue +++ b/frontend/admin/src/views/admin/Notifications.vue @@ -59,6 +59,14 @@ const notificationData = reactive({ enabled: false, recipients_text: '', }, + feishu: { + enabled: false, + app_id: '', + app_secret: '', + has_app_secret: false, + receive_id_type: 'chat_id', + recipients_text: '', + }, }, scenes: { wallet_recharge_success: true, @@ -138,6 +146,9 @@ const currentChannelTargets = computed(() => { if (testForm.channel === 'telegram') { return splitRecipients(notificationData.channels.telegram.recipients_text) } + if (testForm.channel === 'feishu') { + return splitRecipients(notificationData.channels.feishu.recipients_text) + } return splitRecipients(notificationData.channels.email.recipients_text) }) @@ -174,10 +185,17 @@ const fetchSettings = async () => { const notifChannels = notification.channels as Record> | undefined const notifEmail = notifChannels?.email const notifTelegram = notifChannels?.telegram + const notifFeishu = notifChannels?.feishu notificationData.channels.email.enabled = !!notifEmail?.enabled notificationData.channels.email.recipients_text = joinRecipients(notifEmail?.recipients) notificationData.channels.telegram.enabled = !!notifTelegram?.enabled notificationData.channels.telegram.recipients_text = joinRecipients(notifTelegram?.recipients) + notificationData.channels.feishu.enabled = !!notifFeishu?.enabled + notificationData.channels.feishu.app_id = String(notifFeishu?.app_id || '') + notificationData.channels.feishu.app_secret = '' + notificationData.channels.feishu.has_app_secret = !!notifFeishu?.has_app_secret + notificationData.channels.feishu.receive_id_type = String(notifFeishu?.receive_id_type || 'chat_id') + notificationData.channels.feishu.recipients_text = joinRecipients(notifFeishu?.recipients) const notifScenes = notification.scenes as Record | undefined notificationData.scenes.wallet_recharge_success = !!notifScenes?.wallet_recharge_success @@ -249,6 +267,7 @@ const changeNotificationLogPage = (page: number) => { const notificationChannelLabel = (value: string) => { if (value === 'telegram') return t('admin.settings.notification.channels.telegram.title') + if (value === 'feishu') return t('admin.settings.notification.channels.feishu.title') return t('admin.settings.notification.channels.email.title') } @@ -349,6 +368,7 @@ onMounted(async () => { {{ t('admin.settings.notification.channels.email.title') }} {{ t('admin.settings.notification.channels.telegram.title') }} + {{ t('admin.settings.notification.channels.feishu.title') }} @@ -412,6 +432,7 @@ onMounted(async () => { {{ t('admin.settings.notification.logs.filters.allChannels') }} {{ t('admin.settings.notification.channels.email.title') }} {{ t('admin.settings.notification.channels.telegram.title') }} + {{ t('admin.settings.notification.channels.feishu.title') }} + +
+ + +

+ {{ t(form.channels.feishu.has_app_secret + ? 'admin.settings.notification.channels.feishu.appSecretHintKeep' + : 'admin.settings.notification.channels.feishu.appSecretHintEmpty') }} +

+
+ +
+
+ + +
+
+ +