mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-06 05:27:48 +08:00
fix: 增加thinking disabled
修改退群监控模板 定时日报保存日报历史
This commit is contained in:
@@ -72,6 +72,7 @@ describe('AI model settings', () => {
|
||||
expect(screen.getByLabelText('供应商名称')).toHaveFocus()
|
||||
expect(screen.getByLabelText('模型名称')).toHaveAttribute('placeholder', '例如:DeepSeek Chat')
|
||||
expect(screen.getByLabelText('模型 ID')).toHaveAttribute('placeholder', '例如:deepseek-chat')
|
||||
expect(screen.getByRole('combobox', { name: '思考模式' })).toHaveTextContent('跟随模型默认')
|
||||
})
|
||||
|
||||
it('reveals and focuses the provider editor when editing an existing provider', async () => {
|
||||
|
||||
@@ -260,7 +260,7 @@ let groupExitMonitorState = {
|
||||
monitoredRoomIds: ['group_regular@chatroom', 'group_folded@chatroom'],
|
||||
notificationRoomIds: [],
|
||||
notificationTemplate:
|
||||
'[退群监测]\n\n用户: {user}\n\n群备注: {groupRemark}\n\n微信号: {wxid}\n\n退群时间: {time}',
|
||||
'[退群监测]\n\n用户: {user}\n\n群备注: {groupRemark}\n\n微信号: {wxid}\n\n人数: {previousCount} -> {currentCount}\n\n退群时间: {time}',
|
||||
lastCheckedAt: fixtureNowMs - 30 * 1000,
|
||||
lastReadAt: 0,
|
||||
unreadCount: 1
|
||||
@@ -329,6 +329,7 @@ handle('group-exit-monitor:markRead', (readAt) => {
|
||||
|
||||
const scheduledReportTasks = []
|
||||
const scheduledReportExecutions = []
|
||||
const generatedReports = []
|
||||
const scheduledReportNextRun = (scheduleTime) => {
|
||||
const [hours, minutes] = String(scheduleTime || '09:00')
|
||||
.split(':')
|
||||
@@ -839,11 +840,19 @@ handle('report:prepareTemplateSwitch', () => ({
|
||||
values: { REPORT_TITLE: '固定脱敏群日报' }
|
||||
}
|
||||
}))
|
||||
handle('report:listGenerated', () => ({ success: true, reports: [] }))
|
||||
handle('report:saveGenerated', (request) => ({
|
||||
success: true,
|
||||
record: { id: 'fixture-report-record', ...request }
|
||||
}))
|
||||
handle('report:listGenerated', () => ({ success: true, reports: [...generatedReports] }))
|
||||
handle('report:saveGenerated', (request) => {
|
||||
const record = {
|
||||
id: 'fixture-report-record',
|
||||
...request,
|
||||
htmlStatus: request.htmlPath ? 'ready' : 'missing',
|
||||
pngStatus: request.pngPath ? 'ready' : 'missing'
|
||||
}
|
||||
const index = generatedReports.findIndex((report) => report.id === record.id)
|
||||
if (index >= 0) generatedReports[index] = record
|
||||
else generatedReports.unshift(record)
|
||||
return { success: true, record }
|
||||
})
|
||||
handle('report:updateGeneratedTemplate', (request) => ({
|
||||
success: true,
|
||||
record: { id: request.reportId, templateId: request.templateId }
|
||||
|
||||
@@ -37,6 +37,44 @@ const provider = (id: string, modelId: string, vision: boolean): AIProviderConfi
|
||||
describe('AI provider vision routing', () => {
|
||||
afterAll(() => rmSync(root, { recursive: true, force: true }))
|
||||
|
||||
it('sends the disabled thinking control only when it is selected', async () => {
|
||||
const service = new AIProviderService()
|
||||
const configured = provider('thinking-provider', 'deepseek-v4-flash-vision-exp', false)
|
||||
configured.advanced.thinking = 'disabled'
|
||||
expect(service.save(configured).success).toBe(true)
|
||||
expect(service.setDefault(configured.id).success).toBe(true)
|
||||
|
||||
const fetchMock = vi
|
||||
.fn()
|
||||
.mockResolvedValue(
|
||||
new Response(
|
||||
JSON.stringify({ choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }] }),
|
||||
{ headers: { 'content-type': 'application/json' } }
|
||||
)
|
||||
)
|
||||
vi.stubGlobal('fetch', fetchMock)
|
||||
try {
|
||||
const result = await service.chat([{ role: 'user', content: 'hello' }])
|
||||
expect(result).toMatchObject({ success: true, data: 'ok', finishReason: 'stop' })
|
||||
const body = JSON.parse(fetchMock.mock.calls[0][1].body as string) as Record<string, unknown>
|
||||
expect(body).toMatchObject({
|
||||
model: 'deepseek-v4-flash-vision-exp',
|
||||
thinking: { type: 'disabled' }
|
||||
})
|
||||
|
||||
configured.advanced.thinking = 'default'
|
||||
expect(service.save(configured).success).toBe(true)
|
||||
await service.chat([{ role: 'user', content: 'hello' }])
|
||||
const defaultBody = JSON.parse(fetchMock.mock.calls[1][1].body as string) as Record<
|
||||
string,
|
||||
unknown
|
||||
>
|
||||
expect(defaultBody).not.toHaveProperty('thinking')
|
||||
} finally {
|
||||
vi.unstubAllGlobals()
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps DeepSeek as the text model while routing images to a verified vision model', () => {
|
||||
const service = new AIProviderService()
|
||||
expect(service.save(provider('deepseek', 'deepseek-chat', false)).success).toBe(true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtempSync, readJsonSync, rmSync } from 'fs-extra'
|
||||
import { mkdtempSync, readJsonSync, rmSync, writeJsonSync } from 'fs-extra'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -32,6 +32,7 @@ vi.mock('../../src/main/services/personal-wechat-send-service', () => ({
|
||||
}))
|
||||
|
||||
import { GroupExitMonitorService } from '../../src/main/services/group-exit-monitor-service'
|
||||
import { GROUP_EXIT_NOTIFICATION_TEMPLATE } from '../../src/shared/group-exit-monitor'
|
||||
|
||||
const member = { wxid: 'wxid_member', wechatNickname: '微信名', groupNickname: '群内名' }
|
||||
|
||||
@@ -77,6 +78,17 @@ describe('GroupExitMonitorService', () => {
|
||||
expect(() => service.setNotificationTemplate('用户: {unknown}')).toThrow('不支持的占位符')
|
||||
})
|
||||
|
||||
it('migrates the previous default notification template', () => {
|
||||
writeJsonSync(join(mocks.userData, 'group-exit-monitor.json'), {
|
||||
notificationTemplate:
|
||||
'[退群监测]\n\n用户: {user}\n\n群备注: {groupRemark}\n\n微信号: {wxid}\n\n退群时间: {time}'
|
||||
})
|
||||
|
||||
const service = new GroupExitMonitorService()
|
||||
|
||||
expect(service.getState().notificationTemplate).toBe(GROUP_EXIT_NOTIFICATION_TEMPLATE)
|
||||
})
|
||||
|
||||
it('keeps the previous baseline when a native snapshot contains null members', async () => {
|
||||
installGroupDb()
|
||||
mocks.chat.getGroupSnapshotAsync.mockResolvedValueOnce({
|
||||
|
||||
@@ -65,6 +65,7 @@ describe('group exit monitor member diff', () => {
|
||||
expect(message).toContain('用户: 微信名')
|
||||
expect(message).toContain('群备注: 未设置')
|
||||
expect(message).toContain('微信号: wxid_fixture')
|
||||
expect(message).toContain('人数: 240 -> 239')
|
||||
expect(message).toContain('退群时间: 2026-08-31 00:19:07')
|
||||
})
|
||||
|
||||
@@ -97,8 +98,8 @@ describe('group exit monitor member diff', () => {
|
||||
message: '群昵称退出了测试群',
|
||||
detectedAt: new Date(2026, 7, 31, 0, 19, 7).getTime()
|
||||
},
|
||||
'{user}|{groupRemark}|{wxid}|{time}'
|
||||
'{user}|{groupRemark}|{wxid}|{previousCount}|{currentCount}|{time}'
|
||||
)
|
||||
expect(message).toBe('微信名|群内昵称|wxid_fixture|2026-08-31 00:19:07')
|
||||
expect(message).toBe('微信名|群内昵称|wxid_fixture|2|1|2026-08-31 00:19:07')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
buildGroupReportInput,
|
||||
getSummaryDateRangeAt,
|
||||
parseGroupDailyReport
|
||||
} from '../../src/renderer/src/utils/group-report'
|
||||
import { summaryContent } from '../../src/renderer/src/utils/group-report-facts'
|
||||
@@ -324,3 +325,24 @@ describe('group report parsing', () => {
|
||||
expect(input.media.gallery).toEqual([])
|
||||
})
|
||||
})
|
||||
describe('summary date ranges', () => {
|
||||
it('uses local calendar days for today, yesterday, and seven-day ranges', () => {
|
||||
const now = new Date(2026, 0, 1, 12, 34, 56)
|
||||
const today = new Date(2026, 0, 1)
|
||||
const yesterday = new Date(2025, 11, 31)
|
||||
const sevenDaysAgo = new Date(2025, 11, 26)
|
||||
|
||||
expect(getSummaryDateRangeAt('today', now)).toEqual({
|
||||
startTime: today.getTime() / 1000,
|
||||
endTime: now.getTime() / 1000
|
||||
})
|
||||
expect(getSummaryDateRangeAt('yesterday', now)).toEqual({
|
||||
startTime: yesterday.getTime() / 1000,
|
||||
endTime: today.getTime() / 1000 - 1
|
||||
})
|
||||
expect(getSummaryDateRangeAt('7days', now)).toEqual({
|
||||
startTime: sevenDaysAgo.getTime() / 1000,
|
||||
endTime: now.getTime() / 1000
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,6 +71,7 @@ describe('generated report template history', () => {
|
||||
dateRange: '今天',
|
||||
messageCount: 10,
|
||||
generatedAt: '2026-08-12T10:00:00.000Z',
|
||||
reportDate: '2026-08-11',
|
||||
generatedImage: `data:image/png;base64,${Buffer.from('mobile-01').toString('base64')}`,
|
||||
htmlPath: originalHtml,
|
||||
reportSnapshot,
|
||||
@@ -79,6 +80,7 @@ describe('generated report template history', () => {
|
||||
})
|
||||
expect(saved.success).toBe(true)
|
||||
expect(saved.record).toBeDefined()
|
||||
expect(saved.record?.reportDate).toBe('2026-08-11')
|
||||
|
||||
const updated = await updateGeneratedReportTemplate({
|
||||
reportId: saved.record!.id,
|
||||
|
||||
@@ -34,6 +34,7 @@ describe('scheduled report scheduling', () => {
|
||||
const generatedPath = join(storageDir, 'report.png')
|
||||
let generated = 0
|
||||
let sent = 0
|
||||
const saveHistory = vi.fn().mockResolvedValue({ success: true })
|
||||
const service = new ScheduledReportService({
|
||||
storageDir,
|
||||
now: () => now,
|
||||
@@ -46,6 +47,7 @@ describe('scheduled report scheduling', () => {
|
||||
sent += 1
|
||||
return { success: true, status: capability.senderStatus }
|
||||
},
|
||||
saveGeneratedReport: saveHistory,
|
||||
isDatabaseReady: () => true
|
||||
})
|
||||
const created = await service.createTask({
|
||||
@@ -61,6 +63,14 @@ describe('scheduled report scheduling', () => {
|
||||
expect(run.data?.status).toBe('success')
|
||||
expect(generated).toBe(1)
|
||||
expect(sent).toBe(1)
|
||||
expect(saveHistory).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
contactName: '研发群',
|
||||
reportDate: undefined,
|
||||
pngPath: generatedPath,
|
||||
messageCount: 0
|
||||
})
|
||||
)
|
||||
expect(await service.listExecutions(taskId)).toHaveLength(1)
|
||||
const restored = new ScheduledReportService({
|
||||
storageDir,
|
||||
@@ -75,4 +85,30 @@ describe('scheduled report scheduling', () => {
|
||||
JSON.parse(await readFile(join(storageDir, 'executions.json'), 'utf8'))[0].message
|
||||
).toContain('微信发送成功')
|
||||
})
|
||||
|
||||
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')
|
||||
const send = vi.fn().mockResolvedValue({ success: true, status: capability.senderStatus })
|
||||
const service = new ScheduledReportService({
|
||||
storageDir,
|
||||
getCapability: async () => capability,
|
||||
generateReport: async () => ({ success: true, pngPath: generatedPath }),
|
||||
saveGeneratedReport: vi.fn().mockResolvedValue({ success: false, error: '磁盘不可写' }),
|
||||
send,
|
||||
isDatabaseReady: () => true
|
||||
})
|
||||
const created = await service.createTask({
|
||||
name: '历史失败日报',
|
||||
group: '研发群',
|
||||
target: '研发群@chatroom',
|
||||
scheduleTime: '09:00'
|
||||
})
|
||||
|
||||
const result = await service.runScheduledReportNow(created.data!.id)
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.data?.error).toContain('report_history_save_failed:磁盘不可写')
|
||||
expect(send).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user