diff --git a/README.md b/README.md index 8d83214..5a53b62 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ WeFlow 帮我跨过了微信 4.x 数据访问这道门槛,我才有机会继 | 找一句记得原文或关键词的聊天 | [档案](./docs/user-guide/chat-archive.md) | 连接微信数据,不需要 AI | | 找一件记得大意、但不知道在哪聊过的事 | [问问微信](./docs/user-guide/ai-search.md) | 配置 AI 服务,并选择会话和时间范围 | | 让长期、跨群聊查找更稳定 | [问问微信 → 本地知识库](./docs/user-guide/knowledge.md) | 主动建立本地索引;不会自动创建 | -| 快速了解一个群今天、昨天或近 7 天聊了什么 | [日报](./docs/user-guide/report.md) | 选择群聊并配置 AI 服务 | +| 快速了解一个群今日、昨日或近 7 天聊了什么 | [日报](./docs/user-guide/report.md) | 选择群聊并配置 AI 服务 | | 把群聊日报生成微信分享卡片(实验性) | [微信分享卡片](./docs/deployment/experimental-wechat-share-card.md) | 自备 Cloudflare、域名和微信测试号 | | 把微信语音变成可搜索的文字 | [设置 → 语音转文字](./docs/user-guide/voice.md) | 准备本地语音模型 | | 把聊天保存成 HTML、Markdown、CSV 或 JSON | [导出](./docs/user-guide/export.md) | 选择聊天、时间和格式,不需要 AI | diff --git a/docs/user-guide/report.md b/docs/user-guide/report.md index e2a3917..d780764 100644 --- a/docs/user-guide/report.md +++ b/docs/user-guide/report.md @@ -6,8 +6,8 @@ 典型场景包括: -- 整理今天工作群的讨论重点; -- 回顾昨天错过的决定和资源; +- 整理今日工作群的讨论重点; +- 回顾昨日错过的决定和资源; - 汇总近 7 天的项目进展、待办和未解决问题; - 把群里的图片、语音统计和重要消息放进一张长图或 HTML 页面。 @@ -16,7 +16,7 @@ 你可以从两个入口开始:打开一级导航“日报”后新建报告,或者在“档案”中选中一个群聊并点击“生成 AI 日报”。 1. 选择一个群聊。当前日报入口只支持群聊,不支持单聊。 -2. 选择时间范围:今天、昨天或近 7 天。 +2. 选择时间范围:今日、昨日或近 7 天。 3. 按需要选择参与总结的消息类型,先从文字开始最容易核对。 4. 选择报告模板/内容模式并开始生成。 5. 等待“整理输入 → AI 生成 → HTML/PNG 导出”完成。 @@ -37,6 +37,6 @@ - 先选正确的群和时间范围; - 不确定时先只选择文字消息; -- 群太活跃时分成“今天”和“近 7 天”两次生成; +- 群太活跃时分成“今日”和“近 7 天”两次生成; - 看到待办和结论后回到原消息核对上下文; - AI Provider 不可用时先检查模型配置和网络/本地服务状态。 diff --git a/scripts/generate-report-template-preview.cjs b/scripts/generate-report-template-preview.cjs index 5d49906..51f375d 100644 --- a/scripts/generate-report-template-preview.cjs +++ b/scripts/generate-report-template-preview.cjs @@ -98,7 +98,7 @@ const activityLine = Array.from(document.querySelectorAll('.analytics > .card')) const values = { REPORT_TITLE: title, REPORT_DATE: reportDate, - DATE_RANGE: '今天', + DATE_RANGE: '今日', TIME_SPAN: statValues[2] || dateTimeRange, HERO_SUMMARY: overview, HERO_TAKEAWAY: '', diff --git a/src/main/services/scheduled-report-service.ts b/src/main/services/scheduled-report-service.ts index 045d9f4..c730778 100644 --- a/src/main/services/scheduled-report-service.ts +++ b/src/main/services/scheduled-report-service.ts @@ -86,7 +86,7 @@ const reportRangeLabel = (range: ScheduledReportRange): string => : range === '7days' ? '近 7 天' : range === 'today' - ? '今天' + ? '今日' : '昨日' interface ScheduledReportNotificationPayload { diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index cad707e..c4c39a1 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1614,7 +1614,7 @@ function App(): React.ReactElement { ? '昨日' : summaryDateRange === '7days' ? '近 7 天' - : '今天', + : '今日', reportDate: reportMetadata.reportDate, messageCount: reportGeneration.reportMessages.length, generatedAt: new Date().toISOString(), diff --git a/src/renderer/src/components/reports/AiReportWorkspace.tsx b/src/renderer/src/components/reports/AiReportWorkspace.tsx index bfadcf2..e0d010a 100644 --- a/src/renderer/src/components/reports/AiReportWorkspace.tsx +++ b/src/renderer/src/components/reports/AiReportWorkspace.tsx @@ -58,7 +58,7 @@ interface AiReportWorkspaceProps { const rangeLabel = (range: SummaryDateRange): string => { if (range === 'yesterday') return '昨日' if (range === '7days') return '近 7 天' - return '今天' + return '今日' } const modelLabel = (model: string): string => { diff --git a/src/renderer/src/components/reports/ReportHistorySidebar.tsx b/src/renderer/src/components/reports/ReportHistorySidebar.tsx index fb3e941..83fcf95 100644 --- a/src/renderer/src/components/reports/ReportHistorySidebar.tsx +++ b/src/renderer/src/components/reports/ReportHistorySidebar.tsx @@ -69,8 +69,8 @@ const groupLabelFor = (value: string): string => { const time = new Date(date) time.setHours(0, 0, 0, 0) - if (time.getTime() >= startOfToday.getTime()) return '今天' - if (time.getTime() >= startOfYesterday.getTime()) return '昨天' + if (time.getTime() >= startOfToday.getTime()) return '今日' + if (time.getTime() >= startOfYesterday.getTime()) return '昨日' if (date.getFullYear() === now.getFullYear()) return `${date.getMonth() + 1}月${date.getDate()}日` return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日` } diff --git a/src/renderer/src/components/reports/ScheduledReportsWorkspace.tsx b/src/renderer/src/components/reports/ScheduledReportsWorkspace.tsx index e52df21..c82ae25 100644 --- a/src/renderer/src/components/reports/ScheduledReportsWorkspace.tsx +++ b/src/renderer/src/components/reports/ScheduledReportsWorkspace.tsx @@ -66,8 +66,8 @@ const rangeLabel = (range: ScheduledReportRange): string => : range === '7days' ? '近 7 天' : range === 'today' - ? '今天' - : '昨天' + ? '今日' + : '昨日' const isScheduledReportRange = (value: unknown): value is ScheduledReportRange => value === 'today' || value === 'yesterday' || value === '7days' || value === 'recent24h' @@ -99,7 +99,7 @@ const formatDateTime = (value?: string): string => { if (Number.isNaN(date.getTime())) return '时间未知' const now = new Date() const dayLabel = - date.toDateString() === now.toDateString() ? '今天' : date.toLocaleDateString('zh-CN') + date.toDateString() === now.toDateString() ? '今日' : date.toLocaleDateString('zh-CN') return `${dayLabel} ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}` } @@ -386,7 +386,7 @@ function ScheduledReportDialog({ setReportRange(value as ScheduledReportRange)} @@ -395,20 +395,24 @@ function ScheduledReportDialog({ {option.label} ))} - + 最近24小时 - 自定义 即将支持 + 自定义 + 即将支持 diff --git a/src/renderer/src/features/api-center/model/apiEndpoints.ts b/src/renderer/src/features/api-center/model/apiEndpoints.ts index b088495..f5dc8b9 100644 --- a/src/renderer/src/features/api-center/model/apiEndpoints.ts +++ b/src/renderer/src/features/api-center/model/apiEndpoints.ts @@ -69,7 +69,7 @@ export const API_ENDPOINTS: ApiEndpoint[] = [ }), endpoint('agent-group-report', { name: '生成群聊总结图片', - description: '读取指定群聊并生成今天、昨天或近 7 天的总结长图。', + description: '读取指定群聊并生成今日、昨日或近 7 天的总结长图。', body: true }), endpoint('agent-send', { diff --git a/src/renderer/src/utils/group-report-facts.ts b/src/renderer/src/utils/group-report-facts.ts index bdfabf6..466bfce 100644 --- a/src/renderer/src/utils/group-report-facts.ts +++ b/src/renderer/src/utils/group-report-facts.ts @@ -586,7 +586,7 @@ const buildMediaSection = async ( funBadges.push({ title: '高能输出王', owner: topSpeaker[0], - note: `今天一共发了 ${topSpeaker[1]} 条消息。` + note: `今日一共发了 ${topSpeaker[1]} 条消息。` }) } if (visionGallery[0]) { diff --git a/src/renderer/src/utils/group-report.ts b/src/renderer/src/utils/group-report.ts index c0e7b8b..e0d9f30 100644 --- a/src/renderer/src/utils/group-report.ts +++ b/src/renderer/src/utils/group-report.ts @@ -43,7 +43,7 @@ JSON 结构必须为: "overview": "1至2句整体讨论风格与氛围", "hero": { "headline": "一句抓重点的日报标题", - "summary": "一句总结今天发生了什么,最多3行", + "summary": "一句总结今日发生了什么,最多3行", "keyTakeaway": "最重要结论,没有可空", "pendingNote": "最值得跟进的一项,没有可空", "statusLine": "例如:今日形成 3 个结论 · 2 个待办 · 1 个问题尚未解决" @@ -645,7 +645,7 @@ const postProcessReport = ( topics[0]?.title || `${metadata.groupName}${mode === 'compact' ? '速览' : '日报'}`, summary: truncate( - report.hero?.summary || report.overview || '今天群里有新的讨论进展。', + report.hero?.summary || report.overview || '今日群里有新的讨论进展。', mode === 'compact' ? 84 : 120 ), keyTakeaway: report.hero?.keyTakeaway @@ -814,7 +814,7 @@ export const parseGroupDailyReport = ( ? { headline: asString(heroRoot.headline) || topics[0]?.title || '今日群聊速览', summary: - asString(heroRoot.summary) || asString(root.overview) || '今天群里有新的讨论进展。', + asString(heroRoot.summary) || asString(root.overview) || '今日群里有新的讨论进展。', keyTakeaway: asString(heroRoot.keyTakeaway), pendingNote: asString(heroRoot.pendingNote), statusLine: asString(heroRoot.statusLine) @@ -864,7 +864,7 @@ export type SummaryMessageType = | 'system' export const SUMMARY_DATE_OPTIONS: { value: SummaryDateRange; label: string }[] = [ - { value: 'today', label: '今天' }, + { value: 'today', label: '今日' }, { value: 'yesterday', label: '昨日' }, { value: '7days', label: '近 7 天' } ] @@ -923,6 +923,8 @@ export const getSummaryDateRangeAt = ( range: SummaryDateRange, now = new Date() ): { startTime: number; endTime: number } => { + // These ranges are local calendar dates. Only the separate recent24h mode + // uses a rolling 24-hour window in the scheduled-report service. const startOfToday = new Date(now) startOfToday.setHours(0, 0, 0, 0) const startOfYesterday = new Date(startOfToday) diff --git a/src/shared/scheduled-report-error.ts b/src/shared/scheduled-report-error.ts index 7e2b422..d32aaee 100644 --- a/src/shared/scheduled-report-error.ts +++ b/src/shared/scheduled-report-error.ts @@ -95,7 +95,7 @@ const messagesFor = ( return { severity: 'error', userTitle: '日报生成失败', - userMessage: '今天的聊天内容较多,超过当前 AI 模型一次可以处理的内容长度。', + userMessage: '今日的聊天内容较多,超过当前 AI 模型一次可以处理的内容长度。', suggestedAction: '可以缩短日报范围,或更换支持更长上下文的模型。', retryable: true } diff --git a/tests/component/report-features.test.tsx b/tests/component/report-features.test.tsx index 440e462..76a7020 100644 --- a/tests/component/report-features.test.tsx +++ b/tests/component/report-features.test.tsx @@ -133,7 +133,7 @@ describe('daily report controls', () => { ) - expect(screen.getByRole('radio', { name: '今天' })).toBeChecked() + expect(screen.getByRole('radio', { name: '今日' })).toBeChecked() await user.click(screen.getByRole('radio', { name: '近 7 天' })) expect(onRangeChange).toHaveBeenCalledWith('7days') expect(screen.getAllByRole('checkbox')).toHaveLength(SUMMARY_TYPE_OPTIONS.length) @@ -382,7 +382,7 @@ describe('daily report controls', () => { id: 'report-1', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-12T10:00:00.000Z', reportDate: '2026-08-12', @@ -443,7 +443,7 @@ describe('daily report controls', () => { id: 'temporary-result', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-13T10:00:00.000Z', reportDate: '2026-08-13', @@ -514,7 +514,7 @@ describe('daily report controls', () => { id: 'model-info', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-13T10:00:00.000Z', reportDate: '2026-08-13', @@ -582,7 +582,7 @@ describe('daily report controls', () => { id: 'report-send', contactId: groupContact.md5, contactName: groupContact.m_nsNickName, - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-17T10:00:00.000Z', reportDate: '2026-08-17', @@ -621,7 +621,7 @@ describe('daily report controls', () => { id: 'report-send-disabled', contactId: groupContact.md5, contactName: groupContact.m_nsNickName, - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-17T10:00:00.000Z', reportDate: '2026-08-17', @@ -657,7 +657,7 @@ describe('daily report controls', () => { id: 'report-switch', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-12T10:00:00.000Z', reportDate: '2026-08-12', @@ -753,7 +753,7 @@ describe('daily report controls', () => { id: 'report-delete-cancel', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-17T10:00:00.000Z', reportDate: '2026-08-17', @@ -823,7 +823,7 @@ describe('daily report controls', () => { id: 'report-delete-result', contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-17T10:00:00.000Z', reportDate: '2026-08-17', diff --git a/tests/component/scheduled-reports-workspace.test.tsx b/tests/component/scheduled-reports-workspace.test.tsx index 020a3cf..c808860 100644 --- a/tests/component/scheduled-reports-workspace.test.tsx +++ b/tests/component/scheduled-reports-workspace.test.tsx @@ -128,6 +128,26 @@ describe('ScheduledReportsWorkspace', () => { ) }) + it('keeps scheduled report range labels readable in compact layouts', async () => { + vi.mocked(window.api.listScheduledReports).mockResolvedValue([]) + render( + + ) + + await waitFor(() => expect(screen.getByText('还没有定时日报')).toBeVisible()) + fireEvent.click(screen.getAllByRole('button', { name: /新建定时日报/ })[0]) + + const range = screen.getByRole('radiogroup', { name: '总结范围' }) + expect(range).toHaveClass('grid-cols-2', 'sm:grid-cols-5') + expect(screen.getByText('自定义', { exact: true })).toHaveClass('whitespace-nowrap') + expect(screen.getByText('即将支持', { exact: true })).toHaveClass('whitespace-nowrap') + }) + it('keeps the notification switch off by default and loads the persisted setting', async () => { render( { endTime: now.getTime() / 1000 }) }) + + it('keeps a scheduled 20:00 today report on the current local calendar day', () => { + const now = new Date(2026, 7, 27, 20, 0, 0) + const startOfToday = new Date(2026, 7, 27, 0, 0, 0) + + expect(getSummaryDateRangeAt('today', now)).toEqual({ + startTime: startOfToday.getTime() / 1000, + endTime: now.getTime() / 1000 + }) + }) }) diff --git a/tests/unit/report-history-service.test.ts b/tests/unit/report-history-service.test.ts index 3b236c5..bf6230f 100644 --- a/tests/unit/report-history-service.test.ts +++ b/tests/unit/report-history-service.test.ts @@ -41,7 +41,7 @@ const reportSnapshot = { const reportMetadata = { groupName: '测试群', reportDate: '2026-08-12', - dateRange: '今天', + dateRange: '今日', messageCount: 10, activeUsers: 3, timeSpan: '09:00–18:00', @@ -68,7 +68,7 @@ describe('generated report template history', () => { const saved = await saveGeneratedReport({ contactId: 'group-md5', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-12T10:00:00.000Z', reportDate: '2026-08-11', @@ -114,7 +114,7 @@ describe('generated report template history', () => { const saved = await saveGeneratedReport({ contactId: 'legacy-group', contactName: '旧报告', - dateRange: '今天', + dateRange: '今日', messageCount: 5, generatedAt: '2026-08-12T11:00:00.000Z', generatedImage: `data:image/png;base64,${Buffer.from('legacy').toString('base64')}`, @@ -142,7 +142,7 @@ describe('generated report template history', () => { const saved = await saveGeneratedReport({ contactId: 'legacy-snapshot-group', contactName: '旧报告快照', - dateRange: '今天', + dateRange: '今日', messageCount: 8, generatedAt: '2026-08-12T12:00:00.000Z', generatedImage: `data:image/png;base64,${Buffer.from('legacy-snapshot').toString('base64')}`, @@ -172,7 +172,7 @@ describe('generated report template history', () => { const saved = await saveGeneratedReport({ contactId: 'classic-group', contactName: '经典日报群', - dateRange: '今天', + dateRange: '今日', messageCount: 6, generatedAt: '2026-08-12T13:00:00.000Z', generatedImage: `data:image/png;base64,${Buffer.from('classic').toString('base64')}`, diff --git a/tests/unit/report-template-switch.test.ts b/tests/unit/report-template-switch.test.ts index 42ba686..c420328 100644 --- a/tests/unit/report-template-switch.test.ts +++ b/tests/unit/report-template-switch.test.ts @@ -6,7 +6,7 @@ const structuredReport = { id: 'report-1', contactId: 'group-1', contactName: '测试群', - dateRange: '今天', + dateRange: '今日', messageCount: 10, generatedAt: '2026-08-12T10:00:00.000Z', reportDate: '2026-08-12', diff --git a/tests/unit/scheduled-report-service.test.ts b/tests/unit/scheduled-report-service.test.ts index cc25db2..c23de37 100644 --- a/tests/unit/scheduled-report-service.test.ts +++ b/tests/unit/scheduled-report-service.test.ts @@ -254,6 +254,42 @@ describe('scheduled report scheduling', () => { ).toContain('微信发送成功') }) + it('passes a selected today range through a scheduled 20:00 execution', async () => { + const storageDir = await mkdtemp(join(tmpdir(), 'tracememo-scheduled-report-today-')) + let currentTime = new Date(2026, 7, 27, 19, 59, 0) + const generateReport = vi.fn(async () => ({ + success: true, + groupName: '研发群', + pngPath: '/tmp/generated.png', + messageCount: 1 + })) + const service = new ScheduledReportService({ + storageDir, + now: () => currentTime, + getCapability: async () => capability, + generateReport, + saveGeneratedReport: async () => ({ success: true, record: reportRecord('/tmp/saved.png') }), + send: async () => ({ success: true, status: capability.senderStatus }), + isDatabaseReady: () => true + }) + const created = await service.createTask({ + name: '今日日报', + group: '研发群', + target: '研发群@chatroom', + scheduleTime: '20:00', + reportRange: 'today' + }) + expect(created.success).toBe(true) + currentTime = new Date(2026, 7, 27, 20, 0, 0) + + const execution = await runScheduled(service, created.data!.id) + + expect(execution.status).toBe('success') + expect(generateReport).toHaveBeenCalledWith( + expect.objectContaining({ group: '研发群', range: 'today' }) + ) + }) + it('does not send a generated report when history persistence fails', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'tracememo-scheduled-report-history-')) const generatedPath = join(storageDir, 'report.png')