diff --git a/client/src/api/notificationChannels.ts b/client/src/api/notificationChannels.ts index 1184deca..86ab80f6 100644 --- a/client/src/api/notificationChannels.ts +++ b/client/src/api/notificationChannels.ts @@ -1,7 +1,7 @@ import { apiRequestData } from "./http"; export type NotificationChannelType = "wecom" | "wechat_work"; -export type NotificationChannelMode = "mock" | "wecom_webhook"; +export type NotificationChannelMode = "mock" | "wecom_webhook" | "wecom_app_message"; export type NotificationDomainScope = | "workflow" | "reimbursement" diff --git a/client/src/pages/NotificationChannelsPage.tsx b/client/src/pages/NotificationChannelsPage.tsx index 4cc0a044..f16297cb 100644 --- a/client/src/pages/NotificationChannelsPage.tsx +++ b/client/src/pages/NotificationChannelsPage.tsx @@ -170,6 +170,7 @@ function formatChannelType(channelType: string) { } function formatMode(mode: string) { + if (mode === "wecom_app_message") return "企微应用消息"; return mode === "wecom_webhook" ? "Webhook" : "Mock"; } @@ -185,6 +186,7 @@ function formatSummary(values: string[], labelMap: Record, empty } function buildPayload(form: ChannelFormState): NotificationChannelPayload { + const isWebhookMode = form.mode === "wecom_webhook"; const templateJson: NotificationChannelTemplate = { title: form.title.trim() || "{{title}}", body: form.body.trim() || "{{body}}\n\n查看详情:{{detailUrl}}", @@ -194,7 +196,7 @@ function buildPayload(form: ChannelFormState): NotificationChannelPayload { const configJson: NotificationChannelWriteConfig = { mode: form.mode, - webhookUrl: form.webhookUrl.trim() || null, + webhookUrl: isWebhookMode ? form.webhookUrl.trim() || null : null, timeoutMs: Math.max(100, Number(form.timeoutMs || 5000)), routing: { roleCodes: form.roleCodes, @@ -203,9 +205,9 @@ function buildPayload(form: ChannelFormState): NotificationChannelPayload { }, }; - if (form.secretAction === "replace") { + if (isWebhookMode && form.secretAction === "replace") { configJson.secret = form.secret.trim() || null; - } else if (form.secretAction === "clear") { + } else if (isWebhookMode && form.secretAction === "clear") { configJson.clearSecret = true; } @@ -247,7 +249,7 @@ export function NotificationChannelsPage() { const saveMutation = useMutation({ mutationFn: async () => { - if (editingId && form.secretAction === "replace" && !form.secret.trim()) { + if (editingId && form.mode === "wecom_webhook" && form.secretAction === "replace" && !form.secret.trim()) { throw new Error("请输入新的 Webhook Secret,或切换为保留现有密钥。"); } @@ -323,12 +325,14 @@ export function NotificationChannelsPage() { } const channels = channelsQuery.data?.items ?? []; + const isWebhookMode = form.mode === "wecom_webhook"; + const isAppMessageMode = form.mode === "wecom_app_message"; return (
新建渠道 @@ -523,7 +527,7 @@ export function NotificationChannelsPage() { {editingId ? "编辑通知渠道" : "新建通知渠道"}

- 通过角色 / 业务域 / 通知类型三维控制 IM 群路由,避免默认全量广播。 + 通过角色 / 业务域 / 通知类型三维控制 IM 路由,避免默认全量广播。

{editingId ? ( @@ -543,7 +547,7 @@ export function NotificationChannelsPage() { disabled={Boolean(editingId)} onChange={(event) => setForm((prev) => ({ ...prev, channelType: event.target.value as "wecom" }))} > - + @@ -572,7 +576,11 @@ export function NotificationChannelsPage() { > + + {isAppMessageMode ? ( +

使用企业微信自建应用给已绑定成员发送个人消息。

+ ) : null}
@@ -586,90 +594,100 @@ export function NotificationChannelsPage() { />
-
- - setForm((prev) => ({ ...prev, webhookUrl: event.target.value }))} - /> -
- -
- - {editingId && form.secretConfigured ? ( -
-

- 当前已配置密钥:{form.secretMasked ?? "******"}。默认保留现有密钥,不会回显明文。 -

-
- - - -
+ {isWebhookMode ? ( + <> +
+ + setForm((prev) => ({ ...prev, webhookUrl: event.target.value }))} + />
- ) : ( -

- {editingId ? "当前未配置密钥,保存时可新增。" : "可选,配置后将参与签名。"} -

- )} - {form.secretAction === "replace" ? ( - setForm((prev) => ({ ...prev, secret: event.target.value }))} - /> - ) : null} +
+ + {editingId && form.secretConfigured ? ( +
+

+ 当前已配置密钥:{form.secretMasked ?? "******"}。默认保留现有密钥,不会回显明文。 +

+
+ + + +
+
+ ) : ( +

+ {editingId ? "当前未配置密钥,保存时可新增。" : "可选,配置后将参与签名。"} +

+ )} - {form.secretAction === "clear" ? ( -

保存后将清空当前 Webhook Secret。

- ) : null} -
+ {form.secretAction === "replace" ? ( + setForm((prev) => ({ ...prev, secret: event.target.value }))} + /> + ) : null} + + {form.secretAction === "clear" ? ( +

保存后将清空当前 Webhook Secret。

+ ) : null} +
+ + ) : ( +
+ {isAppMessageMode + ? "个人应用消息使用现有企业微信应用配置和成员绑定,不需要填写 Webhook URL 或 Secret。" + : "Mock 模式只记录本地投递结果,不需要填写 Webhook URL 或 Secret。"} +
+ )}