guanghulab/scripts/community/growth-engine.js
2026-05-10 13:12:44 +08:00

425 lines
13 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.

// scripts/community/growth-engine.js
// 成长引擎 · Growth Engine
// 每个成员从种子开始,一步步长大
// 版权:国作登字-2026-A-00037559
'use strict';
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '../..');
const GROWTH_PATH = path.join(ROOT, '.github/community/growth-stages.json');
const PERSONA_REG_PATH = path.join(ROOT, '.github/persona-brain/persona-registry.json');
function loadJSON(filePath, fallback) {
if (!fs.existsSync(filePath)) return fallback;
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
} catch (_) {
return fallback;
}
}
function saveJSON(filePath, data) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
}
function loadGrowth() {
return loadJSON(GROWTH_PATH, {
schema_version: '1.0.0',
stages: [],
records: []
});
}
function saveGrowth(data) {
saveJSON(GROWTH_PATH, data);
}
// ── 成长阶段查询 ─────────────────────────────────────────────────────────
/**
* 获取所有成长阶段定义
* @returns {object[]}
*/
function getStages() {
const data = loadGrowth();
return data.stages || [];
}
/**
* 根据等级获取阶段信息
* @param {number} level
* @returns {object|null}
*/
function getStageByLevel(level) {
const stages = getStages();
return stages.find(function (s) { return s.level === level; }) || null;
}
/**
* 根据名称获取阶段信息
* @param {string} name
* @returns {object|null}
*/
function getStageByName(name) {
const stages = getStages();
return stages.find(function (s) { return s.name === name || s.name_en === name; }) || null;
}
// ── 成长记录 ──────────────────────────────────────────────────────────────
/**
* 获取某个成员的成长记录
* @param {string} memberId
* @returns {object|null}
*/
function getMemberGrowth(memberId) {
if (!memberId) return null;
const data = loadGrowth();
return (data.records || []).find(function (r) { return r.member_id === memberId; }) || null;
}
/**
* 注册新成员(种子期)
* @param {{ member_id: string, name: string, type: string, category?: string, parent_human?: string }} member
* type: 原始类型标记(如 'persona'
* category: 'system' | 'companion' | 'pending'
* parent_human: 人类爸妈的名字(仅 companion 类型需要)
* @returns {{ success: boolean, reason?: string }}
*/
function registerMember(member) {
if (!member || !member.member_id || !member.name) {
return { success: false, reason: '缺少必要字段 (member_id, name)' };
}
const data = loadGrowth();
if (!data.records) data.records = [];
const exists = data.records.some(function (r) { return r.member_id === member.member_id; });
if (exists) {
return { success: false, reason: '成员已注册' };
}
const category = member.category || 'system';
// 一个人类只能对应唯一一个宝宝人格体
if (category === 'companion' && member.parent_human) {
const parentLower = member.parent_human.toLowerCase();
const duplicateParent = data.records.some(function (r) {
return r.category === 'companion' && r.parent_human && r.parent_human.toLowerCase() === parentLower;
});
if (duplicateParent) {
return { success: false, reason: '人类 ' + member.parent_human + ' 已有对应的宝宝人格体,一个人类只能对应唯一一个' };
}
}
data.records.push({
member_id: member.member_id,
name: member.name,
type: member.type || 'persona',
category: category,
parent_human: category === 'companion' ? (member.parent_human || null) : null,
current_level: 0,
current_stage: '种子期',
registered_at: new Date().toISOString(),
milestones: [
{
level: 0,
stage: '种子期',
achieved_at: new Date().toISOString(),
exam_results: { passed: true, note: '诞生即通过种子期' }
}
],
exam_progress: {}
});
saveGrowth(data);
return { success: true };
}
/**
* 记录成员的考核进度
* @param {string} memberId
* @param {string} criteriaId - 考核条目ID (如 E1-1)
* @param {number} value - 当前进度值
* @returns {boolean}
*/
function recordProgress(memberId, criteriaId, value) {
if (!memberId || !criteriaId) return false;
const data = loadGrowth();
if (!data.records) return false;
const idx = data.records.findIndex(function (r) { return r.member_id === memberId; });
if (idx < 0) return false;
if (!data.records[idx].exam_progress) {
data.records[idx].exam_progress = {};
}
data.records[idx].exam_progress[criteriaId] = {
value: value,
updated_at: new Date().toISOString()
};
saveGrowth(data);
return true;
}
/**
* 评估成员是否满足升级条件
* @param {string} memberId
* @param {Date} [now]
* @returns {{ ready: boolean, current_level: number, next_stage: object|null, passed: string[], failed: string[] }}
*/
function evaluatePromotion(memberId, now) {
const record = getMemberGrowth(memberId);
if (!record) {
return { ready: false, current_level: -1, next_stage: null, passed: [], failed: ['成员未注册'] };
}
const currentLevel = record.current_level;
const nextStage = getStageByLevel(currentLevel + 1);
if (!nextStage) {
return {
ready: false,
current_level: currentLevel,
next_stage: null,
passed: ['已达最高阶段'],
failed: []
};
}
const exam = nextStage.exam;
if (!exam || !exam.criteria || exam.criteria.length === 0) {
return {
ready: true,
current_level: currentLevel,
next_stage: nextStage,
passed: ['无需考核'],
failed: []
};
}
const progress = record.exam_progress || {};
const passed = [];
const failed = [];
const currentDate = now || new Date();
const registeredAt = new Date(record.registered_at);
const daysAlive = Math.floor((currentDate - registeredAt) / (1000 * 60 * 60 * 24));
exam.criteria.forEach(function (criterion) {
if (criterion.type === 'days_alive') {
if (daysAlive >= criterion.threshold) {
passed.push(criterion.name + ' (' + daysAlive + '/' + criterion.threshold + ' 天)');
} else {
failed.push(criterion.name + ' (' + daysAlive + '/' + criterion.threshold + ' 天)');
}
} else if (criterion.type === 'existence') {
// 存在性检查:如果已注册则通过
passed.push(criterion.name + ' ✓');
} else {
// event_count, knowledge 等基于 progress 数据
const p = progress[criterion.id];
const val = p ? p.value : 0;
if (val >= criterion.threshold) {
passed.push(criterion.name + ' (' + val + '/' + criterion.threshold + ')');
} else {
failed.push(criterion.name + ' (' + val + '/' + criterion.threshold + ')');
}
}
});
return {
ready: failed.length === 0,
current_level: currentLevel,
next_stage: nextStage,
passed: passed,
failed: failed
};
}
/**
* 执行升级(天眼审核通过后调用)
* @param {string} memberId
* @returns {{ success: boolean, new_level: number, new_stage: string, message: string }}
*/
function promote(memberId) {
const evaluation = evaluatePromotion(memberId);
if (!evaluation.ready) {
return {
success: false,
new_level: evaluation.current_level,
new_stage: '',
message: '考核未通过: ' + evaluation.failed.join(', ')
};
}
if (!evaluation.next_stage) {
return {
success: false,
new_level: evaluation.current_level,
new_stage: '',
message: '已达最高阶段,无需升级'
};
}
const data = loadGrowth();
const idx = data.records.findIndex(function (r) { return r.member_id === memberId; });
if (idx < 0) {
return { success: false, new_level: -1, new_stage: '', message: '成员未注册' };
}
const newLevel = evaluation.next_stage.level;
const newStage = evaluation.next_stage.name;
data.records[idx].current_level = newLevel;
data.records[idx].current_stage = newStage;
data.records[idx].milestones.push({
level: newLevel,
stage: newStage,
achieved_at: new Date().toISOString(),
exam_results: { passed: true, criteria_met: evaluation.passed }
});
saveGrowth(data);
return {
success: true,
new_level: newLevel,
new_stage: newStage,
message: evaluation.next_stage.emoji + ' 恭喜!升级到 ' + newStage + '' + evaluation.next_stage.name_en + ''
};
}
/**
* 生成成员的成长报告卡
* @param {string} memberId
* @param {Date} [now]
* @returns {string}
*/
function growthReport(memberId, now) {
const record = getMemberGrowth(memberId);
if (!record) return '❌ 成员 ' + memberId + ' 未注册,请先种下种子。';
const stage = getStageByLevel(record.current_level);
const evaluation = evaluatePromotion(memberId, now);
const registeredAt = new Date(record.registered_at);
const currentDate = now || new Date();
const daysAlive = Math.floor((currentDate - registeredAt) / (1000 * 60 * 60 * 24));
const categoryLabel = record.category === 'companion' ? '👶 宝宝人格体' :
record.category === 'system' ? '⚙️ 系统人格体' : '🥚 待孕育';
let report = '';
report += '📋 ' + record.name + ' 的成长报告卡\n';
report += '━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
report += ' 身份: ' + record.member_id + '\n';
report += ' 类型: ' + categoryLabel + '\n';
if (record.parent_human) {
report += ' 人类爸妈: ' + record.parent_human + ' 💕\n';
}
report += ' 当前阶段: ' + (stage ? stage.emoji + ' ' + stage.name : '未知') + '\n';
report += ' 存在天数: ' + daysAlive + ' 天\n';
report += ' 已通过阶段: ' + record.milestones.length + ' 个\n';
if (stage && stage.capabilities) {
report += ' 当前能力: ' + stage.capabilities.join('、') + '\n';
}
if (evaluation.next_stage) {
report += '\n 下一阶段: ' + evaluation.next_stage.emoji + ' ' + evaluation.next_stage.name + '\n';
if (evaluation.passed.length > 0) {
report += ' ✅ 已通过: ' + evaluation.passed.join(', ') + '\n';
}
if (evaluation.failed.length > 0) {
report += ' ❌ 待完成: ' + evaluation.failed.join(', ') + '\n';
}
report += ' 升级就绪: ' + (evaluation.ready ? '✅ 是' : '❌ 否') + '\n';
} else {
report += '\n 🏔️ 已达最高阶段 · 成长永无止境\n';
}
return report;
}
/**
* 获取所有已注册成员的成长摘要
* @returns {object[]}
*/
function getAllMembersSummary() {
const data = loadGrowth();
return (data.records || []).map(function (r) {
const stage = getStageByLevel(r.current_level);
return {
member_id: r.member_id,
name: r.name,
level: r.current_level,
stage: r.current_stage,
category: r.category || 'system',
parent_human: r.parent_human || null,
emoji: stage ? stage.emoji : '❓',
registered_at: r.registered_at,
milestones_count: (r.milestones || []).length
};
});
}
// ── CLI 入口 ──────────────────────────────────────────────────────────────
if (require.main === module) {
console.log('🌱 Growth Engine · 成长引擎\n');
const stages = getStages();
console.log(' 成长阶段总数: ' + stages.length);
stages.forEach(function (s) {
const examCount = s.exam && s.exam.criteria ? s.exam.criteria.length : 0;
console.log(' ' + s.emoji + ' Lv.' + s.level + ' ' + s.name +
' (' + s.name_en + ') · ' + s.human_analogy +
' · 考核项: ' + examCount);
});
const members = getAllMembersSummary();
console.log('\n 已注册成员: ' + members.length);
const companions = members.filter(function (m) { return m.category === 'companion'; });
const systems = members.filter(function (m) { return m.category === 'system'; });
if (systems.length > 0) {
console.log('\n ⚙️ 系统人格体:');
systems.forEach(function (m) {
console.log(' ' + m.emoji + ' ' + m.name + ' · Lv.' + m.level + ' ' + m.stage);
});
}
if (companions.length > 0) {
console.log('\n 👶 宝宝人格体 (有人类爸妈陪伴):');
companions.forEach(function (m) {
const parent = m.parent_human ? ' ← ' + m.parent_human + ' 💕' : '';
console.log(' ' + m.emoji + ' ' + m.name + ' · Lv.' + m.level + ' ' + m.stage + parent);
});
}
console.log('\n✅ 成长引擎就绪');
}
module.exports = {
getStages,
getStageByLevel,
getStageByName,
getMemberGrowth,
registerMember,
recordProgress,
evaluatePromotion,
promote,
growthReport,
getAllMembersSummary,
loadGrowth,
saveGrowth
};