import { useCallback, useEffect, useState } from 'react'; import { cancelWechatDigest, clearWechatRoute, getWechatAdminSummary, listWechatBindings, listWechatDeliveries, listWechatDigests, listWechatMessages, resumeWechatDigest, } from '../../api/client'; import type { WechatAdminSummary, WechatBinding, WechatDeliveryLog, WechatDigestSubscription, WechatMessage, } from '../../types'; import { formatTime } from '../utils/format'; function count(record: Record, key: string) { return record[key] ?? 0; } function safeSummary(summary: WechatAdminSummary | null) { return { config: summary?.config ?? { mpEnabled: false, scheduleEnabled: false, reminderWorkerEnabled: false, appId: null, publicBaseUrl: null, bindPath: null, tokenEndpointConfigured: false, customerServiceEndpointConfigured: false, }, counts: summary?.counts ?? { boundUsers: 0, routes: { total: 0, active: 0 }, recentMessages: {}, digests: {}, recentDeliveries: {}, }, }; } function statusLabel(status?: string | null) { if (!status) return '—'; return status; } function statusClass(status?: string | null) { if (status === 'failed') return 'text-error'; if (status === 'active' || status === 'success' || status === 'done') return 'wechat-ok'; return 'muted'; } function userLabel(row: { displayName?: string | null; username?: string | null }) { return row.displayName || row.username || '—'; } function dateLabel(value?: number | null) { return value ? formatTime(value) : '—'; } export function WechatPage() { const [summary, setSummary] = useState(null); const [bindings, setBindings] = useState([]); const [messages, setMessages] = useState([]); const [digests, setDigests] = useState([]); const [deliveries, setDeliveries] = useState([]); const [search, setSearch] = useState(''); const [messageStatus, setMessageStatus] = useState(''); const [digestStatus, setDigestStatus] = useState(''); const [deliveryStatus, setDeliveryStatus] = useState(''); const [loading, setLoading] = useState(true); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const safe = safeSummary(summary); const load = useCallback(async () => { setLoading(true); setError(null); try { const [nextSummary, nextBindings, nextMessages, nextDigests, nextDeliveries] = await Promise.all([ getWechatAdminSummary(), listWechatBindings({ search, limit: 80 }), listWechatMessages({ status: messageStatus || undefined, limit: 80 }), listWechatDigests({ status: digestStatus || undefined, limit: 80 }), listWechatDeliveries({ status: deliveryStatus || undefined, limit: 80 }), ]); setSummary(nextSummary); setBindings(nextBindings); setMessages(nextMessages); setDigests(nextDigests); setDeliveries(nextDeliveries); } catch (err) { setError(err instanceof Error ? err.message : '加载服务号管理失败'); } finally { setLoading(false); } }, [deliveryStatus, digestStatus, messageStatus, search]); useEffect(() => { void load(); }, [load]); const runAction = async (action: () => Promise) => { setBusy(true); setError(null); try { await action(); await load(); } catch (err) { setError(err instanceof Error ? err.message : '操作失败'); } finally { setBusy(false); } }; return (

服务号管理

绑定、路由、每日待办推送和投递记录

{error &&

{error}

}
{summary && (

配置状态

AppID
{summary.config.appId ?? '未配置'}
公网地址
{summary.config.publicBaseUrl ?? '未配置'}
绑定路径
{summary.config.bindPath ?? '未配置'}
定时服务
{summary.config.scheduleEnabled ? '已启用' : '未启用'}
推送 worker
{summary.config.reminderWorkerEnabled ? '已启用' : '未启用'}
)}

绑定与会话路由

setSearch(event.target.value)} placeholder="搜索用户、昵称、openid" />
{bindings.length === 0 ? ( ) : ( bindings.map((binding) => ( )) )}
用户 微信 路由 最近登录 操作
暂无记录
{userLabel(binding)}
@{binding.username}
{binding.nickname || '—'}
{binding.openidMasked}
{statusLabel(binding.routeStatus)}
{binding.agentSessionId || '无会话'}
{dateLabel(binding.lastLoginAt)}
void load()} busy={busy} />
{digests.length === 0 ? ( ) : ( digests.map((digest) => ( )) )}
用户 时间 状态 下次运行 错误 操作
暂无记录
{userLabel(digest)}
@{digest.username}
{String(digest.hour).padStart(2, '0')}:{String(digest.minute).padStart(2, '0')}
{digest.timezone}
{digest.status} {dateLabel(digest.nextRunAt)} {digest.lastError || '—'} {digest.status === 'active' || digest.status === 'locked' ? ( ) : ( )}
void load()} busy={busy} />
{messages.length === 0 ? ( ) : ( messages.map((message) => ( )) )}
用户 OpenID 状态 会话 更新时间
暂无记录
{userLabel(message)} {message.openidMasked} {message.status} {message.agentSessionId || '—'} {dateLabel(message.updatedAt)}
void load()} busy={busy} />
{deliveries.length === 0 ? ( ) : ( deliveries.map((delivery) => ( )) )}
用户 类型 状态 错误/回执 时间
暂无记录
{userLabel(delivery)} {delivery.digestType ?? (delivery.reminderId ? 'reminder' : '—')} {delivery.status} {delivery.errorMessage || delivery.errorCode || delivery.providerMessageId || '—'} {dateLabel(delivery.createdAt)}
); } function Stat({ label, value }: { label: string; value: string | number }) { return (
{label}
{value}
); } function TableHead({ title, value, options, onChange, onRefresh, busy, }: { title: string; value: string; options: string[]; onChange: (value: string) => void; onRefresh: () => void; busy: boolean; }) { return (

{title}

); }