diff --git a/admin-web/screenshots/02-bot-created.png b/admin-web/screenshots/02-bot-created.png new file mode 100644 index 0000000..12199cf Binary files /dev/null and b/admin-web/screenshots/02-bot-created.png differ diff --git a/admin-web/src/views/ChannelAccount.vue b/admin-web/src/views/ChannelAccount.vue index 701f1eb..d490799 100644 --- a/admin-web/src/views/ChannelAccount.vue +++ b/admin-web/src/views/ChannelAccount.vue @@ -287,6 +287,19 @@ function resetDrawerForm() { drawerForm.extInfo = '' } +function parseBotExtInfo(extInfo: string | undefined): string { + if (!extInfo) return '' + try { + const parsed = JSON.parse(extInfo) + if (parsed && typeof parsed === 'object' && parsed.webhook_url) { + return parsed.webhook_url + } + } catch { + // fallback to legacy plain URL + } + return extInfo +} + function fillDrawerForm(row: any) { drawerForm.accountType = row.accountType ?? 1 drawerForm.channelType = row.channelType ?? 'wecom' @@ -295,7 +308,7 @@ function fillDrawerForm(row: any) { drawerForm.accountName = row.accountName ?? '' drawerForm.sessionArchiveEnabled = row.sessionArchiveEnabled ?? 0 drawerForm.serverHost = row.serverHost ?? '' - drawerForm.extInfo = row.extInfo ?? '' + drawerForm.extInfo = row.accountType === 6 ? parseBotExtInfo(row.extInfo) : (row.extInfo ?? '') } function openCreateDialog() { @@ -327,9 +340,17 @@ async function loadSubjects() { } } +function buildAccountPayload(form: typeof dialogForm) { + const payload: any = { ...form } + if (form.accountType === 6 && form.extInfo && form.extInfo.trim()) { + payload.extInfo = JSON.stringify({ webhook_url: form.extInfo.trim() }) + } + return payload +} + async function handleSave() { try { - await createChannelAccount(dialogForm) + await createChannelAccount(buildAccountPayload(dialogForm)) ElMessage.success('创建成功') dialogVisible.value = false await loadAccounts() @@ -341,7 +362,7 @@ async function handleSave() { async function handleDrawerSave() { if (!currentAccount.value?.id) return try { - await updateChannelAccount(currentAccount.value.id, drawerForm) + await updateChannelAccount(currentAccount.value.id, buildAccountPayload(drawerForm)) ElMessage.success('更新成功') drawerVisible.value = false await loadAccounts() diff --git a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java index 3e171b3..cfaeb5d 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java @@ -791,6 +791,22 @@ public class SendService { } } + @SuppressWarnings("unchecked") + private String extractWeComBotWebhookUrl(String extInfo) { + if (!StringUtils.hasText(extInfo)) { + return null; + } + try { + Map parsed = JsonUtils.fromJson(extInfo, Map.class); + if (parsed != null && parsed.get("webhook_url") != null) { + return String.valueOf(parsed.get("webhook_url")); + } + } catch (Exception e) { + // fallback: treat legacy ext_info as plain URL string + } + return extInfo; + } + private RetryConfig loadRetryConfig(String tenantCode, SendRequest request) { String channelTypeStr = extractChannelType(request.getChannelStrategy()); PolicyGroup policyGroup = loadPolicyGroup(tenantCode, channelTypeStr); @@ -989,7 +1005,7 @@ public class SendService { } if (account.getAccountType() != null && account.getAccountType() == AccountType.WECOM_BOT.getCode() && StringUtils.hasText(account.getExtInfo())) { - extra.put("webhook_url", account.getExtInfo()); + extra.put("webhook_url", extractWeComBotWebhookUrl(account.getExtInfo())); } } extra.put("record_id", record.getId());