feat(desktop): ✨ 实现一些功能
1. 实现任务暂停功能 2. 实现页面的国际化功能 3.优化项目的结构以及BUG 4. 优化系统架构 5. 实现一大堆的功能
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
import { ChatOpenAI } from '@langchain/openai'
|
||||
import Store from 'electron-store'
|
||||
import { CONFIG_STORE_KEY } from '@rpc/constants/store_key'
|
||||
|
||||
const StoreClass = (Store as any).default || Store
|
||||
const store = new StoreClass({ encryptionKey: CONFIG_STORE_KEY })
|
||||
import { configService } from '@main/services/configService'
|
||||
|
||||
export const createChatModel = (type: 'reading' | 'summary', schema: any) => {
|
||||
const config = store.get(`chatModels.${type}`) as any
|
||||
const chatModelsConfig = configService.getChatModelsConfig()
|
||||
const config = chatModelsConfig?.[type] as any
|
||||
console.log('chatModels', config)
|
||||
|
||||
if (!config || !config.apiKey) {
|
||||
@@ -4,7 +4,7 @@ import { REFLECTION_CONTENT_PROMPT } from '@main/services/ai/prompts/readingRefl
|
||||
import { z } from 'zod'
|
||||
import { AppDataSource } from '@main/db/data-source'
|
||||
import { ReadingReflectionTaskItem } from '@main/db/entities/ReadingReflectionTaskItem'
|
||||
import { createChatModel } from '@main/services/ai/llmService'
|
||||
import { createChatModel } from '@main/services/ai/core/llmService'
|
||||
|
||||
export const generateReadingReflectionContentNode = async (
|
||||
state: typeof ReadingReflectionState.State,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PromptTemplate } from '@langchain/core/prompts'
|
||||
import { ReadingReflectionState } from '../state/readingReflectionState'
|
||||
import { REFLECTION_SUMMARY_PROMPT } from '@main/services/ai/prompts/readingReflactionPrompts'
|
||||
import { z } from 'zod'
|
||||
import { createChatModel } from '@main/services/ai/llmService'
|
||||
import { createChatModel } from '@main/services/ai/core/llmService'
|
||||
|
||||
/**
|
||||
* 步骤 3:生成摘要和关键词
|
||||
|
||||
173
src/main/services/configService.ts
Normal file
173
src/main/services/configService.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import Store from 'electron-store'
|
||||
import { app } from 'electron'
|
||||
import path from 'path'
|
||||
import crypto from 'crypto'
|
||||
|
||||
// 定义配置键名
|
||||
const NOTIFICATION_KEY = 'notification'
|
||||
const CHAT_MODELS_KEY = 'chatModels'
|
||||
const LANGUAGE_KEY = 'language'
|
||||
|
||||
/**
|
||||
* 配置服务,负责统一管理应用配置
|
||||
*/
|
||||
export class ConfigService {
|
||||
private store: Store
|
||||
private encryptionKey: string
|
||||
|
||||
constructor() {
|
||||
// 生成或获取加密密钥
|
||||
this.encryptionKey = this.getOrGenerateEncryptionKey()
|
||||
|
||||
// 兼容性处理获取 Store 构造函数
|
||||
const StoreClass = (Store as any).default || Store
|
||||
|
||||
// 创建 Store 实例
|
||||
this.store = new StoreClass({
|
||||
encryptionKey: this.encryptionKey,
|
||||
// 使用更安全的加密算法
|
||||
schema: {
|
||||
[NOTIFICATION_KEY]: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
masterSwitch: { type: 'boolean' },
|
||||
taskCompleted: { type: 'boolean' },
|
||||
taskFailed: { type: 'boolean' },
|
||||
silentMode: { type: 'boolean' }
|
||||
}
|
||||
},
|
||||
[CHAT_MODELS_KEY]: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reading: { type: 'object' },
|
||||
summary: { type: 'object' }
|
||||
}
|
||||
},
|
||||
[LANGUAGE_KEY]: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
language: { type: 'string', enum: ['zh', 'en', 'ja', 'ko', 'es'] }
|
||||
}
|
||||
}
|
||||
},
|
||||
// 提高加密强度
|
||||
encryptionAlgorithm: 'aes-256-cbc' as any
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成或获取加密密钥
|
||||
*/
|
||||
private getOrGenerateEncryptionKey(): string {
|
||||
// 从安全位置获取或生成密钥
|
||||
// 这里实现一个简单的密钥生成和存储机制
|
||||
const keyPath = path.join(app.getPath('userData'), 'encryption.key')
|
||||
|
||||
try {
|
||||
// 尝试从文件读取密钥
|
||||
const fs = require('fs')
|
||||
if (fs.existsSync(keyPath)) {
|
||||
return fs.readFileSync(keyPath, 'utf8')
|
||||
}
|
||||
|
||||
// 如果密钥不存在,生成一个新的随机密钥
|
||||
const newKey = crypto.randomBytes(32).toString('hex')
|
||||
fs.writeFileSync(keyPath, newKey, { mode: 0o600 }) // 设置文件权限,只有所有者可以读写
|
||||
return newKey
|
||||
} catch (error) {
|
||||
// 如果出现错误,使用一个基于应用信息的密钥作为后备
|
||||
console.error('Failed to generate or read encryption key:', error)
|
||||
return this.generateFallbackKey()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成后备密钥
|
||||
*/
|
||||
private generateFallbackKey(): string {
|
||||
// 基于应用信息生成一个后备密钥
|
||||
const appInfo = `${app.name}-${app.getVersion()}-${app.getPath('userData')}`
|
||||
return crypto.createHash('sha256').update(appInfo).digest('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通知配置
|
||||
*/
|
||||
getNotificationConfig(): any {
|
||||
return this.store.get(NOTIFICATION_KEY) || {
|
||||
masterSwitch: true,
|
||||
taskCompleted: true,
|
||||
taskFailed: true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存通知配置
|
||||
*/
|
||||
saveNotificationConfig(config: any): void {
|
||||
this.store.set(NOTIFICATION_KEY, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天模型配置
|
||||
*/
|
||||
getChatModelsConfig(): any {
|
||||
const data = this.store.get(CHAT_MODELS_KEY) as { reading?: any; summary?: any } | null
|
||||
|
||||
// 检查是否包含必要的嵌套 Key,如果没有,说明是旧版本数据
|
||||
if (data && typeof data === 'object' && !data.reading && !data.summary) {
|
||||
console.log('检测到旧版本配置,正在重置...')
|
||||
this.store.delete(CHAT_MODELS_KEY) // 删除旧的根键
|
||||
return null
|
||||
}
|
||||
|
||||
return data || null
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存聊天模型配置
|
||||
*/
|
||||
saveChatModelConfig(type: string, config: any): void {
|
||||
this.store.set(`${CHAT_MODELS_KEY}.${type}`, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定键的配置
|
||||
*/
|
||||
get(key: string): any {
|
||||
return this.store.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
*/
|
||||
set(key: string, value: any): void {
|
||||
this.store.set(key, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置
|
||||
*/
|
||||
delete(key: string): void {
|
||||
this.store.delete(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言配置
|
||||
*/
|
||||
getLanguageConfig(): any {
|
||||
return this.store.get(LANGUAGE_KEY) || {
|
||||
language: 'zh'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存语言配置
|
||||
*/
|
||||
saveLanguageConfig(config: any): void {
|
||||
this.store.set(LANGUAGE_KEY, config)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例实例
|
||||
export const configService = new ConfigService()
|
||||
56
src/main/services/notificationService.ts
Normal file
56
src/main/services/notificationService.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Notification } from 'electron'
|
||||
import { configService } from '@main/services/configService'
|
||||
import { notificationEventEmitter } from '@rpc/router/notice.router'
|
||||
|
||||
export class NotificationService {
|
||||
/**
|
||||
* 处理通知逻辑
|
||||
*/
|
||||
handleNotification(status: string, progress: number, total: number, index: number, batchId: string): void {
|
||||
// 从配置服务获取用户偏好
|
||||
const config = configService.getNotificationConfig()
|
||||
|
||||
// 如果总开关关闭,直接拦截
|
||||
if (!config.masterSwitch) return
|
||||
|
||||
// 场景 A: 任务全部完成 (100%)
|
||||
if (progress === 100 && config.taskCompleted) {
|
||||
// 只有当所有子任务都完成,或者当前是单任务时才弹出
|
||||
// 如果是批量任务,你可以选择在最后一个子任务完成时通知
|
||||
if (index + 1 === total) {
|
||||
const notification = new Notification({
|
||||
title: '🎉 读书心得已生成',
|
||||
body: total > 1 ? `共 ${total} 篇心得已全部处理完成。` : '您的书籍心得已准备就绪。',
|
||||
silent: config.silentMode
|
||||
})
|
||||
|
||||
notification.show()
|
||||
|
||||
// 监听点击事件
|
||||
notification.on('click', () => {
|
||||
notificationEventEmitter.emit('notification-click', {
|
||||
batchId
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 场景 B: 任务失败 (假设你传入的 status 是 'FAILED')
|
||||
if (status === 'FAILED' && config.taskFailed) {
|
||||
const notification = new Notification({
|
||||
title: '❌ 任务生成失败',
|
||||
body: `第 ${index + 1} 项任务执行异常,请检查网络或 API 余额。`,
|
||||
silent: config.silentMode
|
||||
})
|
||||
|
||||
notification.show()
|
||||
|
||||
// 监听点击事件
|
||||
notification.on('click', () => {
|
||||
notificationEventEmitter.emit('notification-click', {
|
||||
batchId
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user