101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { Repository } from 'typeorm'
|
|
import { ReadingPersona } from '@main/db/entities/ReadingPersona'
|
|
import {
|
|
IReadingReflectionTaskBatch,
|
|
IReadingReflectionTaskItem
|
|
} from '@shared/types/IReadingReflectionTask'
|
|
import { IUserReadingPersona } from '@shared/types/IUserReadingPersona'
|
|
|
|
export class PersonaService {
|
|
constructor(private personaRepo: Repository<ReadingPersona>) {}
|
|
|
|
/**
|
|
* 刷新画像并保存到数据库
|
|
*/
|
|
async refreshPersona(
|
|
items: IReadingReflectionTaskItem[],
|
|
batches: IReadingReflectionTaskBatch[]
|
|
) {
|
|
const rawResult = await this.calculatePersona(items, batches) // 调用你原来的计算逻辑
|
|
|
|
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 结构以便前端适配
|
|
persona.rawStats = {
|
|
totalWords: items.reduce((sum, i) => sum + (i.content?.length || 0), 0),
|
|
totalBooks: batches.length,
|
|
topKeywords: rawResult.topKeywords
|
|
}
|
|
|
|
return await this.personaRepo.save(persona)
|
|
}
|
|
/**
|
|
* 从数据库聚合数据并计算画像分值
|
|
*/
|
|
async calculatePersona(
|
|
items: IReadingReflectionTaskItem[],
|
|
batches: IReadingReflectionTaskBatch[]
|
|
) {
|
|
// 1. 计算认知深度:根据关键词频次
|
|
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 * 2 + allKeywords.length / 5)
|
|
|
|
// 2. 计算知识广度:根据书籍数量
|
|
const breadthScore = Math.min(100, batches.length * 10)
|
|
|
|
// 3. 计算产出效率:根据总字数
|
|
const totalWords = items.reduce((sum, i) => sum + (i.content?.length || 0), 0)
|
|
const outputScore = Math.min(100, totalWords / 500) // 每 5万字满分
|
|
|
|
// 4. 计算 Top 10 关键词
|
|
const sortedKeywords = [...keywordMap.entries()]
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 10)
|
|
.map((entry) => entry[0])
|
|
|
|
return {
|
|
cognition: Math.round(cognitionScore),
|
|
breadth: Math.round(breadthScore),
|
|
output: Math.round(outputScore),
|
|
practicality: 75, // 可根据 occupation 比例动态计算
|
|
global: 60, // 可根据 language 比例动态计算
|
|
topKeywords: sortedKeywords
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 实体转化为用户阅读画像
|
|
* @param entity 实体
|
|
* */
|
|
export function entityToUserReadingPersona(entity: ReadingPersona): IUserReadingPersona {
|
|
return {
|
|
domainDepth: JSON.parse(entity.topKeywords || '[]').map((name: string) => ({
|
|
name,
|
|
score: entity.cognition, // 简易算法:共用认知深度分
|
|
bookCount: 1 // 可根据数据库详细统计进一步细化
|
|
})),
|
|
breadthScore: entity.breadth,
|
|
efficiencyScore: entity.output,
|
|
maturityScore: entity.practicality,
|
|
languageScore: entity.global,
|
|
stats: entity.rawStats || {
|
|
totalWords: 0,
|
|
totalBooks: 0,
|
|
topKeywords: [],
|
|
mostUsedOccupation: 'other'
|
|
}
|
|
}
|
|
}
|