From 799afd1ea5a39bf96431bb39b0a8d1c996f482ae Mon Sep 17 00:00:00 2001 From: john Date: Fri, 11 Sep 2026 08:52:58 +0800 Subject: [PATCH] feat(admin): auto-load today news morning preview in WeChat page Co-authored-by: Cursor --- server/app.mjs | 20 ++++++ src/admin/pages/WechatPage.tsx | 113 ++++++++++++++++++++++++++++++++- src/api/client.ts | 9 +++ src/types.ts | 22 +++++++ 4 files changed, 161 insertions(+), 3 deletions(-) diff --git a/server/app.mjs b/server/app.mjs index 490f570..8c2c40d 100644 --- a/server/app.mjs +++ b/server/app.mjs @@ -455,6 +455,26 @@ export function createAdminApp(services) { } }); + adminApi.get('/wechat/news-morning-draft/today', requireAdmin, async (_req, res) => { + if (!wechatNewsMorningDraftService) { + return res.status(503).json({ message: '新闻早报草稿推送未启用' }); + } + return res.json(await wechatNewsMorningDraftService.getTodayStatus()); + }); + + adminApi.get('/wechat/news-morning-draft/preview/today', requireAdmin, async (_req, res) => { + if (!wechatNewsMorningDraftService) { + return res.status(503).json({ message: '新闻早报草稿推送未启用' }); + } + try { + return res.json(await wechatNewsMorningDraftService.previewToday()); + } catch (error) { + return res.status(400).json({ + message: error instanceof Error ? error.message : '预览失败', + }); + } + }); + adminApi.post('/wechat/news-morning-draft/push', requireAdmin, async (req, res) => { if (!wechatNewsMorningDraftService) { return res.status(503).json({ message: '新闻早报草稿推送未启用' }); diff --git a/src/admin/pages/WechatPage.tsx b/src/admin/pages/WechatPage.tsx index 5cf8e2d..62bb4b0 100644 --- a/src/admin/pages/WechatPage.tsx +++ b/src/admin/pages/WechatPage.tsx @@ -8,6 +8,7 @@ import { getWechatIntentRouterConfig, getWechatIntentRouterRuntime, getWechatNewsMorningDraftConfig, + getWechatNewsMorningDraftTodayStatus, listAdminUsers, listLlmProviderKeys, listWechatBindings, @@ -18,7 +19,7 @@ import { listWechatWebNotifications, patchWechatIntentRouterConfig, patchWechatNewsMorningDraftConfig, - previewWechatNewsMorningDraft, + previewWechatNewsMorningDraftToday, pushWechatNewsMorningDraft, resumeWechatDigest, updateWechatScheduleLlmConfig, @@ -36,6 +37,7 @@ import type { WechatNewsMorningDraftPreview, WechatNewsMorningDraftRun, WechatNewsMorningDraftTemplate, + WechatNewsMorningDraftTodayStatus, WechatWebNotification, } from '../../types'; import { formatTime } from '../utils/format'; @@ -188,6 +190,7 @@ export function WechatPage() { const [savedNewsDraftForm, setSavedNewsDraftForm] = useState(null); const [newsDraftTemplate, setNewsDraftTemplate] = useState(null); const [newsDraftPreview, setNewsDraftPreview] = useState(null); + const [newsDraftTodayStatus, setNewsDraftTodayStatus] = useState(null); const [newsDraftRuns, setNewsDraftRuns] = useState([]); const [newsDraftSaving, setNewsDraftSaving] = useState(false); const [newsDraftWorking, setNewsDraftWorking] = useState(false); @@ -274,10 +277,40 @@ export function WechatPage() { } }, [deliveryStatus, digestStatus, messageStatus, notificationStatus, search]); + const refreshTodayNewsMorningDraft = useCallback(async (sourceUserId?: string) => { + if (!sourceUserId?.trim()) { + setNewsDraftTodayStatus(null); + return; + } + try { + const status = await getWechatNewsMorningDraftTodayStatus(); + setNewsDraftTodayStatus(status); + if (status.todayPageReady) { + setNewsDraftPreview(await previewWechatNewsMorningDraftToday()); + } else { + setNewsDraftPreview(null); + } + } catch (err) { + setNewsDraftTodayStatus(null); + if (!(err instanceof Error && /404|503/u.test(err.message))) { + setError(err instanceof Error ? err.message : '加载今日早报失败'); + } + } + }, []); + useEffect(() => { void load(); }, [load]); + useEffect(() => { + if (!newsDraftForm?.sourceUserId.trim()) { + setNewsDraftTodayStatus(null); + setNewsDraftPreview(null); + return; + } + void refreshTodayNewsMorningDraft(newsDraftForm.sourceUserId); + }, [newsDraftForm?.sourceUserId, refreshTodayNewsMorningDraft]); + const runAction = async (action: () => Promise) => { setBusy(true); setError(null); @@ -405,7 +438,14 @@ export function WechatPage() { setNewsDraftWorking(true); setError(null); try { - setNewsDraftPreview(await previewWechatNewsMorningDraft()); + const status = await getWechatNewsMorningDraftTodayStatus(); + setNewsDraftTodayStatus(status); + if (status.todayPageReady) { + setNewsDraftPreview(await previewWechatNewsMorningDraftToday()); + } else { + setNewsDraftPreview(null); + setError(status.message ?? '今日新闻早报尚未生成'); + } } catch (err) { setError(err instanceof Error ? err.message : '预览失败'); } finally { @@ -442,6 +482,7 @@ export function WechatPage() { setNotice(`草稿已写入微信后台,media_id:${result.draftMediaId ?? '—'}`); setNewsDraftPreview(result.preview ?? null); setNewsDraftRuns(await listWechatNewsMorningDraftRuns(10)); + await refreshTodayNewsMorningDraft(newsDraftForm.sourceUserId); } catch (err) { setError(err instanceof Error ? err.message : '推送失败'); setNotice(null); @@ -579,6 +620,72 @@ export function WechatPage() { {newsDraftSaving ? '保存中…' : newsDraftDirty ? '保存配置' : '已保存'} + {newsDraftTodayStatus ? ( +
+
+
+

今日早报 · {newsDraftTodayStatus.dateKey}

+

+ 期望文件名:{newsDraftTodayStatus.expectedSlug}.html +

+
+ +
+
+
+
生成状态
+
+ {newsDraftTodayStatus.todayPageReady ? '今日页面已生成' : newsDraftTodayStatus.message ?? '待生成'} +
+
+ {newsDraftTodayStatus.page ? ( + <> + +
+
更新时间
+
{dateLabel(newsDraftTodayStatus.page.modifiedAt)}
+
+ + ) : newsDraftTodayStatus.latestPageSlug ? ( +
+
最近页面
+
{newsDraftTodayStatus.latestPageSlug}(非今日)
+
+ ) : null} + {newsDraftTodayStatus.autoPushEnabled ? ( +
+
自动推送
+
+ 计划 {String(newsDraftTodayStatus.pushHour ?? 6).padStart(2, '0')}: + {String(newsDraftTodayStatus.pushMinute ?? 0).padStart(2, '0')} + {newsDraftTodayStatus.runs.push?.status === 'success' + ? ' · 今日已推送草稿' + : newsDraftTodayStatus.runs.push?.status === 'failed' + ? ` · 推送失败:${newsDraftTodayStatus.runs.push.errorMessage ?? '—'}` + : ' · 待推送'} +
+
+ ) : null} +
+
+ ) : null}