## 如何设计离线阅读画像系统 ### 1. 第一阶段:画像模型设计 画像不是简单的计数,而是将原始数据(书名、字数、关键词)转化为认知模型。我们定义以下五个核心指标: 认知深度 (Cognition):通过统计关键词(Keywords)的重复频次和专业程度。 知识广度 (Breadth):统计不同书籍领域的分布(基于书名聚类或人工分类)。 实践应用 (Practicality):识别 occupation 字段,职业相关(Professional)的心得占比。 产出效率 (Output):计算总生成字数与任务完成率。 国际视野 (Global):统计英文(en)与中文(zh)任务的比例。 ### 2. 第二阶段:数据库实体定义 (Prisma/TypeORM) 假设你使用常用的本地 ORM。我们需要在数据库中增加一个画像缓存表,避免每次打开页面都进行全量计算。 ```typescript /** * 阅读画像缓存实体 * 存储计算后的分值,避免高频计算 */ export interface IReadingPersona { id: string; // 固定 ID 如 'current_user_persona' cognition: number; // 认知深度分 (0-100) breadth: number; // 知识广度分 (0-100) practicality: number; // 实践应用分 (0-100) output: number; // 产出效率分 (0-100) global: number; // 国际视野分 (0-100) topKeywords: string; // 存储 Top 10 关键词的 JSON 字符串 updatedAt: Date; // 最后计算时间 } ``` ### 3. 第三阶段:核心计算逻辑 (Main Process) 这是画像系统的“大脑”,负责执行 SQL 并转换数据。 ```typescript import { IReadingReflectionTaskItem, IReadingReflectionTaskBatch } from '../types' export class PersonaService { /** * 从数据库聚合数据并计算画像分值 */ async calculatePersona(items: IReadingReflectionTaskItem[], batches: IReadingReflectionTaskBatch[]) { // 1. 计算认知深度:根据关键词频次 const allKeywords = items.flatMap(i => i.keywords || []) const keywordMap = new Map() 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 } } } ``` ### 4. 第四阶段:前端可视化集成 (Statistics.vue) 使用 ECharts 渲染雷达图,将计算结果展现给用户。 ```vue ``` ## 🎨 设计心得:为什么这么做? 数据解耦:原始任务数据(Items/Batches)与画像数据分开存储。即使画像计算逻辑升级(比如你想改变评分算法),也不需要修改历史心得数据。 缓存策略:不要在用户每次切换页面时都去扫全表。建议在任务完成时触发一次增量计算,或者每天用户第一次打开应用时刷新一次。 可视化反馈:雷达图的面积代表了用户的“知识疆域”。当用户看着面积一点点变大时,这种离线的成就感是留存的关键。