feat(desktop): ✨ 实现MD文件模板导出方式
This commit is contained in:
@@ -9,6 +9,9 @@ import { IUserReadingPersona } from '@shared/types/IUserReadingPersona'
|
||||
export class PersonaService {
|
||||
constructor(private personaRepo: Repository<ReadingPersona>) {}
|
||||
|
||||
/**
|
||||
* 刷新画像并保存到数据库
|
||||
*/
|
||||
/**
|
||||
* 刷新画像并保存到数据库
|
||||
*/
|
||||
@@ -16,22 +19,30 @@ export class PersonaService {
|
||||
items: IReadingReflectionTaskItem[],
|
||||
batches: IReadingReflectionTaskBatch[]
|
||||
) {
|
||||
const rawResult = await this.calculatePersona(items, batches) // 调用你原来的计算逻辑
|
||||
// 1. 获取计算结果
|
||||
const rawResult = await this.calculatePersona(items, batches)
|
||||
|
||||
// 2. 创建或更新实体
|
||||
const persona = new ReadingPersona()
|
||||
persona.id = 'current_user_persona'
|
||||
|
||||
// 核心分值映射
|
||||
persona.cognition = rawResult.cognition
|
||||
persona.breadth = rawResult.breadth
|
||||
persona.practicality = rawResult.practicality
|
||||
persona.output = rawResult.output
|
||||
persona.global = rawResult.global
|
||||
persona.topKeywords = JSON.stringify(rawResult.topKeywords)
|
||||
|
||||
// 存储完整的 stats 结构以便前端适配
|
||||
// ✨ 修复关键点:从 rawResult.stats 中获取 topKeywords
|
||||
// 因为 calculatePersona 返回的是 { stats: { topKeywords: [...] } }
|
||||
persona.topKeywords = JSON.stringify(rawResult.stats.topKeywords)
|
||||
|
||||
// 3. 存储完整的 rawStats 结构,确保与前端接口定义对齐
|
||||
persona.rawStats = {
|
||||
totalWords: items.reduce((sum, i) => sum + (i.content?.length || 0), 0),
|
||||
totalBooks: batches.length,
|
||||
topKeywords: rawResult.topKeywords
|
||||
totalWords: rawResult.stats.totalWords,
|
||||
totalBooks: rawResult.stats.totalBooks,
|
||||
totalHours: rawResult.stats.totalHours, // 别忘了我们在 calculatePersona 补充的专注时长
|
||||
topKeywords: rawResult.stats.topKeywords
|
||||
}
|
||||
|
||||
return await this.personaRepo.save(persona)
|
||||
@@ -43,22 +54,44 @@ export class PersonaService {
|
||||
items: IReadingReflectionTaskItem[],
|
||||
batches: IReadingReflectionTaskBatch[]
|
||||
) {
|
||||
// 1. 计算认知深度:根据关键词频次
|
||||
const totalBooks = batches.length
|
||||
const totalWords = items.reduce((sum, i) => sum + (i.content?.length || 0), 0)
|
||||
|
||||
// --- 1. 认知深度 (Cognition) ---
|
||||
const allKeywords = items.flatMap((i) => i.keywords || [])
|
||||
const keywordMap = new Map<string, number>()
|
||||
allKeywords.forEach((k) => keywordMap.set(k, (keywordMap.get(k) || 0) + 1))
|
||||
const cognitionScore = Math.min(100, keywordMap.size * 1.5 + allKeywords.length / 8)
|
||||
|
||||
// 逻辑:去重后的关键词越多且重复越高,分值越高 (示例算法)
|
||||
const cognitionScore = Math.min(100, keywordMap.size * 2 + allKeywords.length / 5)
|
||||
// --- 2. 知识广度 (Breadth) - 修复 TS2339 ---
|
||||
// 逻辑:如果 batch 没分类,就看有多少个独立的高频关键词,这代表了涉及的主题广度
|
||||
const uniqueThemes = new Set(batches.map((b) => (b as any).category).filter(Boolean))
|
||||
|
||||
// 2. 计算知识广度:根据书籍数量
|
||||
const breadthScore = Math.min(100, batches.length * 10)
|
||||
let breadthScore = 0
|
||||
if (uniqueThemes.size > 0) {
|
||||
// 如果有分类数据,按分类算
|
||||
breadthScore = Math.min(100, uniqueThemes.size * 20 + totalBooks * 2)
|
||||
} else {
|
||||
// 如果没分类数据,按关键词覆盖面算(每 5 个独立关键词视为一个知识领域)
|
||||
breadthScore = Math.min(100, (keywordMap.size / 5) * 15 + totalBooks * 2)
|
||||
}
|
||||
|
||||
// 3. 计算产出效率:根据总字数
|
||||
const totalWords = items.reduce((sum, i) => sum + (i.content?.length || 0), 0)
|
||||
const outputScore = Math.min(100, totalWords / 500) // 每 5万字满分
|
||||
// --- 3. 语言能力与全球化 (Global) ---
|
||||
const langDist = items.reduce(
|
||||
(acc, curr) => {
|
||||
// 兼容处理:如果 curr.language 不存在则默认为 'zh'
|
||||
const lang = (curr as any).language || 'zh'
|
||||
acc[lang] = (acc[lang] || 0) + 1
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, number>
|
||||
)
|
||||
// 计算英文占比分:有英文记录就从 60 分起跳,最高 100
|
||||
const globalScore = langDist['en'] ? Math.min(100, 60 + langDist['en'] * 5) : 50
|
||||
|
||||
// --- 4. 专注时长 (Total Hours) ---
|
||||
const totalHours = Math.round((totalWords / 1000) * 1.5 + totalBooks)
|
||||
|
||||
// 4. 计算 Top 10 关键词
|
||||
const sortedKeywords = [...keywordMap.entries()]
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10)
|
||||
@@ -67,10 +100,15 @@ export class PersonaService {
|
||||
return {
|
||||
cognition: Math.round(cognitionScore),
|
||||
breadth: Math.round(breadthScore),
|
||||
output: Math.round(outputScore),
|
||||
practicality: 75, // 可根据 occupation 比例动态计算
|
||||
global: 60, // 可根据 language 比例动态计算
|
||||
topKeywords: sortedKeywords
|
||||
output: Math.min(100, Math.round(totalWords / 500)),
|
||||
practicality: 75,
|
||||
global: Math.round(globalScore),
|
||||
stats: {
|
||||
totalWords,
|
||||
totalBooks,
|
||||
totalHours,
|
||||
topKeywords: sortedKeywords
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user