guanghulab/dingtalk-bot/pca/pca-calculator.js
2026-05-10 13:12:44 +08:00

269 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs');
const path = require('path');
const portraitEngine = require('../portrait/portrait-engine');
// 加载规则配置
function loadRules() {
const rulesPath = path.join(__dirname, 'pca-rules.json');
return JSON.parse(fs.readFileSync(rulesPath));
}
// 维度1EXE执行力计算
function calculateEXE(portrait, rules) {
const factors = rules.dimensions.EXE.factors;
const raw = portrait.raw_stats || {};
const recent = portrait.recent_snapshots?.[0] || {};
// 连胜率 (30%)
const streakRate = Math.min(100, (raw.streak || 0) * 10);
// 完成率 (30%)
const completionRate = raw.total_items ? (raw.completed_items / raw.total_items) * 100 : 100;
// 执行节奏得分 (20%)
const rhythmScore = {
'快': 100,
'稳': 80,
'慢': 50
}[recent.rhythm] || 60;
// 摩擦恢复得分 (20%)
const frictionScore = {
'无': 100,
'低': 85,
'中': 60,
'高': 30
}[recent.friction] || 50;
const total = (
streakRate * (factors.streak_rate / 100) +
completionRate * (factors.completion_rate / 100) +
rhythmScore * (factors.rhythm_score / 100) +
frictionScore * (factors.friction_recovery / 100)
);
return {
score: Math.round(total),
details: {
streakRate,
completionRate,
rhythmScore,
frictionScore
}
};
}
// 维度2TEC技术纵深计算
function calculateTEC(portrait, rules) {
const factors = rules.dimensions.TEC.factors;
const raw = portrait.raw_stats || {};
// EL等级得分 (40%)
const elScore = raw.el_level ? parseInt(raw.el_level) * 10 : 80;
// 代码量得分 (30%)
const codeLines = raw.code_lines || 0;
const codeScore = Math.min(100, codeLines / 100);
// 技术栈广度 (30%) - 从最近快照中推断
const recentSnapshots = portrait.recent_snapshots || [];
const uniqueModules = new Set();
recentSnapshots.forEach(s => {
if (s.raw_stats?.module) uniqueModules.add(s.raw_stats.module);
});
const breadthScore = Math.min(100, uniqueModules.size * 25);
const total = (
elScore * (factors.el_level / 100) +
codeScore * (factors.code_lines / 100) +
breadthScore * (factors.tech_breadth / 100)
);
return {
score: Math.round(total),
details: {
elScore,
codeScore,
breadthScore
}
};
}
// 维度3SYS系统理解力计算
function calculateSYS(portrait, rules) {
const factors = rules.dimensions.SYS.factors;
const recent = portrait.recent_snapshots?.[0] || {};
// 跨模块能力 (35%)
const crossModuleScore = portrait.total_sessions > 3 ? 100 : 60;
// 系统级任务 (35%)
const systemTasksScore = recent.raw_stats?.completed_items > 3 ? 100 : 70;
// 工程思维 (30%)
const engineeringScore = recent.growth?.signals?.includes('自主修复报错') ? 100 : 80;
const total = (
crossModuleScore * (factors.cross_module / 100) +
systemTasksScore * (factors.system_tasks / 100) +
engineeringScore * (factors.engineering_mind / 100)
);
return {
score: Math.round(total),
details: {
crossModuleScore,
systemTasksScore,
engineeringScore
}
};
}
// 维度4COL协作力计算
function calculateCOL(portrait, rules) {
const factors = rules.dimensions.COL.factors;
const recent = portrait.recent_snapshots?.[0] || {};
// SYSLOG质量 (40%)
const syslogScore = recent.will === '强烈' ? 100 : 80;
// Git协作 (30%) - 从连胜推断
const gitScore = Math.min(100, (portrait.current_streak || 0) * 20);
// 人格体协同 (30%)
const personaScore = recent.mood === '正向' ? 100 : 70;
const total = (
syslogScore * (factors.syslog_quality / 100) +
gitScore * (factors.git_collab / 100) +
personaScore * (factors.persona_sync / 100)
);
return {
score: Math.round(total),
details: {
syslogScore,
gitScore,
personaScore
}
};
}
// 维度5INI主动性计算
function calculateINI(portrait, rules) {
const factors = rules.dimensions.INI.factors;
const recent = portrait.recent_snapshots?.[0] || {};
// 意愿信号 (40%)
const willScore = {
'强烈': 100,
'正常': 80,
'低落': 40,
'待观察': 50
}[recent.will] || 60;
// 自发行为 (30%)
const selfScore = recent.growth?.signals?.includes('自主解决问题') ? 100 : 70;
// 72h响应 (30%)
const responseScore = portrait.current_streak > 0 ? 100 : 50;
const total = (
willScore * (factors.will_signal / 100) +
selfScore * (factors.self_initiative / 100) +
responseScore * (factors.response_72h / 100)
);
return {
score: Math.round(total),
details: {
willScore,
selfScore,
responseScore
}
};
}
// 主计算函数
function calculate(devId) {
// 获取画像数据
const portrait = portraitEngine.getPortrait(devId);
if (!portrait) {
throw new Error(`开发者 ${devId} 不存在`);
}
const rules = loadRules();
// 计算各维度
const exe = calculateEXE(portrait, rules);
const tec = calculateTEC(portrait, rules);
const sys = calculateSYS(portrait, rules);
const col = calculateCOL(portrait, rules);
const ini = calculateINI(portrait, rules);
// 加权汇总
const totalScore = Math.round(
exe.score * (rules.dimensions.EXE.weight / 100) +
tec.score * (rules.dimensions.TEC.weight / 100) +
sys.score * (rules.dimensions.SYS.weight / 100) +
col.score * (rules.dimensions.COL.weight / 100) +
ini.score * (rules.dimensions.INI.weight / 100)
);
// 等级判定
let grade = 'D';
for (const [g, threshold] of Object.entries(rules.grade_thresholds)) {
if (totalScore >= threshold) {
grade = g;
break;
}
}
// 维度摘要
const summary = `EXE:${exe.score} TEC:${tec.score} SYS:${sys.score} COL:${col.score} INI:${ini.score}`;
return {
dev_id: devId,
timestamp: new Date().toISOString(),
totalScore,
grade,
dimensions: {
EXE: exe.score,
TEC: tec.score,
SYS: sys.score,
COL: col.score,
INI: ini.score
},
details: {
EXE: exe.details,
TEC: tec.details,
SYS: sys.details,
COL: col.details,
INI: ini.details
},
summary
};
}
// 便捷方法:直接计算最新画像
function calculateLatest() {
const portraits = portraitEngine.getAllPortraits();
const results = {};
for (const devId in portraits) {
results[devId] = calculate(devId);
}
return results;
}
module.exports = {
calculate,
calculateLatest,
calculateEXE,
calculateTEC,
calculateSYS,
calculateCOL,
calculateINI
};