forked from Gitlink/gitlink-cli
fix: 评分系统修复(镜像检测、年龄默认分、社区健康保底)
1. 检测镜像仓库(mirror=true),自动调整评分权重 2. 创建时间不可用时给默认 5 分(原 0 分) 3. 镜像仓库贡献者/Issue/PR 数据标记为『—』而非低分 4. 镜像仓库的综合评分权重改为成熟度40%+文档30%+社区15%+贡献15%
This commit is contained in:
parent
c7842c849b
commit
3965579e48
|
|
@ -17,26 +17,28 @@ const prs = readJSON('prs-all.json').data || {};
|
|||
const langs = readJSON('repo-languages.json').data || {};
|
||||
const contribs = readJSON('repo-contributors.json').data || {};
|
||||
|
||||
// ==== 数据提取(兼容实际字段名)====
|
||||
// ==== 数据提取 ====
|
||||
const name = repo.name || 'unknown';
|
||||
const fullName = repo.full_name || 'unknown';
|
||||
const isMirror = !!repo.mirror; // 镜像仓库检测
|
||||
|
||||
// description: 从 issue 的 project 信息中获取,或从 repo 顶层获取
|
||||
// description
|
||||
let description = '';
|
||||
try { description = issues.issues[0].project.description || ''; } catch(e) {}
|
||||
if (!description) description = repo.description || repo.about || '';
|
||||
// 从 repo 的 fork_info 获取描述
|
||||
if (!description && repo.fork_info) {
|
||||
try { description = issues.issues[0].project.description || ''; } catch(e) {}
|
||||
}
|
||||
|
||||
// 创建时间:从 issue 的第一条创建时间推断,或从项目信息获取
|
||||
// 创建时间
|
||||
let created = repo.created_at || repo.created_on || '';
|
||||
if (!created && issues.issues && issues.issues.length > 0) {
|
||||
created = issues.issues[issues.issues.length - 1].created_at || '';
|
||||
}
|
||||
// 支持 "2026-06-04 15:35" 格式的日期
|
||||
if (created) created = created.replace(' ', 'T');
|
||||
|
||||
const updated = repo.updated_at || repo.last_updated || '';
|
||||
|
||||
// Star/Like — GitLink 使用 praises_count 和 watchers_count
|
||||
const stars = repo.praises_count || repo.watchers_count || 0;
|
||||
const forks = repo.forked_count || 0;
|
||||
const defaultBranch = repo.default_branch || 'master';
|
||||
|
|
@ -45,7 +47,6 @@ const contributorCount = repo.contributor_users_count || 0;
|
|||
|
||||
const langEntries = Object.entries(langs).sort((a,b) => parseFloat(b[1]) - parseFloat(a[1]));
|
||||
const topLangs = langEntries.slice(0, 3).map(e => e[0] + ' ' + e[1]).join(', ');
|
||||
|
||||
const topics = (repo.topics || []).join(', ') || '无标签';
|
||||
|
||||
// 项目年龄(月)
|
||||
|
|
@ -64,108 +65,138 @@ const closedCount = issues.closed_count || 0;
|
|||
const openedCount = issues.opened_count || 0;
|
||||
const totalIssues = closedCount + openedCount || issueList.length || issuesCount;
|
||||
|
||||
// PR 数据 — status 是字符串 "open"/"merged"/"closed"
|
||||
// PR 数据
|
||||
const prList = prs.pulls || [];
|
||||
let prOpen = 0, prMerged = 0;
|
||||
let prOpen = 0, prMerged = 0, prClosed = 0;
|
||||
prList.forEach(p => {
|
||||
const s = (p.status || '').toLowerCase();
|
||||
if (s === 'open' || s === 'opened') prOpen++;
|
||||
else if (s === 'merged' || s === 'merge') prMerged++;
|
||||
else if (s === 'closed') prClosed++;
|
||||
});
|
||||
const prTotal = repo.pull_requests_count || prList.length || 1;
|
||||
const prTotal = repo.pull_requests_count || prList.length || 0;
|
||||
|
||||
// 贡献者
|
||||
const contribList = contribs.list || [];
|
||||
const uniqueContributors = contribList.length || contributorCount;
|
||||
|
||||
// ==== 是否有足够的社区活动数据 ====
|
||||
const hasActivity = totalIssues > 0 || prTotal > 0 || uniqueContributors > 1;
|
||||
|
||||
// ==== 维度评分 ====
|
||||
|
||||
// 1. 成熟度评分 (0-10)
|
||||
const ageScore = Math.min(10, Math.max(0, ageMonths / 2));
|
||||
// 修复:创建时间不可用时给中等分
|
||||
const ageScore = ageMonths > 0 ? Math.min(10, ageMonths / 2) : 5;
|
||||
const starScore = stars > 100 ? 10 : stars > 50 ? 7 : stars > 10 ? 5 : Math.max(1, stars);
|
||||
const forkScore = forks > 50 ? 10 : forks > 20 ? 7 : forks > 5 ? 5 : Math.max(1, forks);
|
||||
const updateScore = 7;
|
||||
const updateScore = ageMonths > 0 ? 7 : 5;
|
||||
const maturityScore = Math.round((ageScore * 0.25 + starScore * 0.25 + forkScore * 0.20 + updateScore * 0.30) * 10) / 10;
|
||||
|
||||
// 2. 文档质量评分 (0-10)
|
||||
let docScore = 3;
|
||||
if (description && description.length > 10) docScore += 2;
|
||||
if (description && description.length > 5) docScore += 2;
|
||||
if (repo.license_short_name || repo.license_name) docScore += 2;
|
||||
if (repo.open_devops) docScore += 1;
|
||||
if (repo.has_tests) docScore += 1;
|
||||
// 镜像仓库且无检测到的文档,给保底分表示"数据不可用"
|
||||
if (isMirror && docScore < 4) docScore = 4;
|
||||
docScore = Math.min(10, docScore);
|
||||
|
||||
// 3. 贡献模式评分 (0-10)
|
||||
const contribScore = Math.min(10, Math.max(1, uniqueContributors * 2));
|
||||
const freqScore = 6;
|
||||
const contribModeScore = Math.round((contribScore * 0.30 + freqScore * 0.30 + 5 * 0.20 + 5 * 0.20) * 10) / 10;
|
||||
const freqScore = hasActivity ? 6 : 3;
|
||||
let contribModeScore;
|
||||
if (isMirror && uniqueContributors <= 1) {
|
||||
// 镜像仓库:贡献数据不可用,给中等分
|
||||
contribModeScore = 5;
|
||||
} else {
|
||||
contribModeScore = Math.round((contribScore * 0.30 + freqScore * 0.30 + 5 * 0.20 + 5 * 0.20) * 10) / 10;
|
||||
}
|
||||
|
||||
// 4. 社区健康度评分 (0-10)
|
||||
const closeRate = totalIssues > 0 ? Math.round(closedCount / totalIssues * 100) : 0;
|
||||
const closeRateScore = closeRate > 80 ? 10 : closeRate > 50 ? 7 : closeRate > 30 ? 5 : 3;
|
||||
const mergeRate = prTotal > 0 ? Math.round(prMerged / prTotal * 100) : 0;
|
||||
const mergeRateScore = mergeRate > 70 ? 10 : mergeRate > 40 ? 7 : 3;
|
||||
const communityScore = Math.round((closeRateScore * 0.30 + 6 * 0.25 + mergeRateScore * 0.25 + 6 * 0.20) * 10) / 10;
|
||||
let closeRate = 0, mergeRate = 0;
|
||||
let closeRateScore, mergeRateScore, communityScore;
|
||||
if (isMirror && totalIssues === 0 && prTotal === 0) {
|
||||
closeRateScore = 5; mergeRateScore = 5; communityScore = 5;
|
||||
} else {
|
||||
closeRate = totalIssues > 0 ? Math.round(closedCount / totalIssues * 100) : 0;
|
||||
closeRateScore = closeRate > 80 ? 10 : closeRate > 50 ? 7 : closeRate > 30 ? 5 : totalIssues > 0 ? 3 : 5;
|
||||
mergeRate = prTotal > 0 ? Math.round(prMerged / prTotal * 100) : 0;
|
||||
mergeRateScore = mergeRate > 70 ? 10 : mergeRate > 40 ? 7 : prTotal > 0 ? 3 : 5;
|
||||
communityScore = Math.round((closeRateScore * 0.30 + 6 * 0.25 + mergeRateScore * 0.25 + 6 * 0.20) * 10) / 10;
|
||||
}
|
||||
|
||||
// 5. 综合评分
|
||||
const totalScore = Math.round((maturityScore * 0.25 + docScore * 0.20 + contribModeScore * 0.30 + communityScore * 0.25) * 10) / 10;
|
||||
// 镜像仓库:调整权重
|
||||
let totalScore;
|
||||
if (isMirror && !hasActivity) {
|
||||
// 镜像仓库:成熟度和文档权重提高
|
||||
totalScore = Math.round((maturityScore * 0.40 + docScore * 0.30 + contribModeScore * 0.15 + communityScore * 0.15) * 10) / 10;
|
||||
} else {
|
||||
totalScore = Math.round((maturityScore * 0.25 + docScore * 0.20 + contribModeScore * 0.30 + communityScore * 0.25) * 10) / 10;
|
||||
}
|
||||
|
||||
const level = totalScore >= 9 ? '🟢 优秀' : totalScore >= 7 ? '🟢 良好' : totalScore >= 5 ? '🟡 一般' : totalScore >= 3 ? '🔴 堪忧' : '🔴 危险';
|
||||
|
||||
// 可复现性
|
||||
const hasLicense = repo.license_short_name ? '✅' : '❌';
|
||||
const hasReadme = (description && description.length > 0) ? '✅' : '❌';
|
||||
const hasDevops = repo.open_devops ? '✅' : '❌';
|
||||
// 可读的状态显示
|
||||
const totalIssuesStr = totalIssues > 0 ? `${totalIssues}(关闭 ${closedCount} / 打开 ${totalIssues - closedCount})` : `${totalIssues || '—'}`;
|
||||
const prTotalStr = prTotal > 0 ? `${prTotal}(合并 ${prMerged} / 打开 ${prOpen})` : `${prTotal || '—'}`;
|
||||
const contributorsStr = uniqueContributors > 0 ? `${uniqueContributors} 人` : '—';
|
||||
|
||||
// 改进建议
|
||||
const suggestions = [];
|
||||
if (stars < 20) suggestions.push('🟡 中优先级 — 项目影响力有限,建议在社区中推广');
|
||||
if (!repo.license_short_name) suggestions.push('🔴 高优先级 — 缺少开源许可证,建议添加 LICENSE 文件');
|
||||
if (uniqueContributors < 3) suggestions.push('🔴 高优先级 — 贡献者较少,建议标记 good-first-issue 吸引新贡献者');
|
||||
if (!repo.license_short_name && !isMirror) suggestions.push('🔴 高优先级 — 缺少开源许可证,建议添加 LICENSE 文件');
|
||||
if (uniqueContributors < 3 && !isMirror) suggestions.push('🔴 高优先级 — 贡献者较少,建议标记 good-first-issue 吸引新贡献者');
|
||||
if (closeRate < 50 && totalIssues > 0) suggestions.push('🟡 中优先级 — Issue 关闭率偏低,建议定期清理积压 Issue');
|
||||
if (mergeRate < 50 && prTotal > 1) suggestions.push('🟡 中优先级 — PR 合并率偏低,建议加快 PR 处理速度');
|
||||
if (isMirror && !hasActivity) suggestions.push('ℹ️ 该项目为镜像仓库,部分社区活动数据不可用,评分仅供参考');
|
||||
if (suggestions.length === 0) suggestions.push('🟢 低优先级 — 项目状态良好,继续保持');
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = now.toISOString().slice(0, 10);
|
||||
const timeStr = now.toISOString().slice(0, 19).replace('T', ' ');
|
||||
|
||||
const ageDisplay = ageMonths > 0 ? `约 ${Math.round(ageMonths)} 个月` : '未知';
|
||||
const mirrorNote = isMirror ? '\n> ℹ️ 该项目为镜像仓库,部分数据(贡献者/Issue/PR)可能不完整\n' : '';
|
||||
|
||||
// ==== 生成报告 ====
|
||||
const report = `# 🔬 科研项目洞察报告 — ${fullName}
|
||||
|
||||
**分析日期:** ${dateStr}
|
||||
**综合评分:** ${totalScore}/10 ${level}
|
||||
|
||||
**综合评分:** ${totalScore}/10 ${level}${isMirror ? ' 📦 镜像仓库' : ''}
|
||||
${mirrorNote}
|
||||
## 一、项目画像
|
||||
| 维度 | 数据 |
|
||||
|------|------|
|
||||
| 项目名称 | ${name} |
|
||||
| 描述 | ${description || '无描述'} |
|
||||
| 创建时间 | ${created || '未知'}(约 ${Math.round(ageMonths)} 个月) |
|
||||
| 创建时间 | ${created || '未知'}(${ageDisplay}) |
|
||||
| Star / Fork | ${stars} / ${forks} |
|
||||
| 默认分支 | ${defaultBranch} |
|
||||
| 主要语言 | ${topLangs || '未知'} |
|
||||
| 研究主题 | ${topics} |
|
||||
| 贡献者数 | ${uniqueContributors} 人 |
|
||||
| 贡献者数 | ${contributorsStr} |
|
||||
| Issue 总数 | ${totalIssues} |
|
||||
|
||||
## 二、项目成熟度(${maturityScore}/10)
|
||||
|
||||
**评分因子:**
|
||||
- 项目年龄:约 ${Math.round(ageMonths)} 个月 → ${ageScore.toFixed(1)} 分(权重 25%)
|
||||
- 项目年龄:${ageDisplay} → ${ageScore.toFixed(1)} 分(权重 25%)
|
||||
- 社区影响力:${stars} Star / ${forks} Fork → ${Math.round(starScore)} 分(权重 25%)
|
||||
- Fork 活跃度:${forks} Fork → ${Math.round(forkScore)} 分(权重 20%)
|
||||
- 维护活跃度:最近更新 ${updated || '未知'} → ${updateScore} 分(权重 30%)
|
||||
|
||||
**分析:** 项目创建约 ${Math.round(ageMonths)} 个月,获得 ${stars} 个 Star 和 ${forks} 个 Fork。${stars > 50 ? '项目已有一定社区影响力,处于成长期。' : '项目尚在早期阶段,需加强社区推广。'}
|
||||
**分析:** ${stars > 50 ? '项目已有一定社区影响力,处于成长期。' : '项目尚在早期阶段,需加强社区推广。'}
|
||||
|
||||
## 三、文档质量(${docScore}/10)
|
||||
|
||||
| 检查项 | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| README | ${hasReadme} | ${description ? '项目描述已填写' : '建议完善 README'} |
|
||||
| 开源许可证 | ${hasLicense} | ${repo.license_short_name || '未检测到,建议添加'} |
|
||||
| DevOps CI | ${hasDevops} | ${repo.open_devops ? '已配置' : '建议配置 CI 流水线'} |
|
||||
| README | ${description && description.length > 5 ? '✅' : '❌'} | ${description ? '项目描述已填写' : '建议完善 README'} |
|
||||
| 开源许可证 | ${repo.license_short_name ? '✅' : '❌'} | ${repo.license_short_name || '未检测到,建议添加'} |
|
||||
| DevOps CI | ${repo.open_devops ? '✅' : '❌'} | ${repo.open_devops ? '已配置' : '建议配置 CI 流水线'} |
|
||||
|
||||
**改进建议:** ${docScore < 6 ? '建议补充 README、LICENSE 和 CI 配置以提升项目规范性' : '文档基本完善'}
|
||||
|
||||
|
|
@ -173,29 +204,29 @@ const report = `# 🔬 科研项目洞察报告 — ${fullName}
|
|||
|
||||
| 指标 | 数据 | 评分 |
|
||||
|------|------|------|
|
||||
| 贡献者数量 | ${uniqueContributors} 人 | ${contribScore}/10 |
|
||||
| 提交活跃度 | 中等 | ${freqScore}/10 |
|
||||
| 贡献者数量 | ${contributorsStr} | ${isMirror && uniqueContributors <= 1 ? '—(镜像仓库)' : contribScore + '/10'} |
|
||||
| 提交活跃度 | ${hasActivity ? '中等' : '—(镜像仓库)'} | ${isMirror && !hasActivity ? '—' : freqScore + '/10'} |
|
||||
|
||||
**分析:** 项目有 ${uniqueContributors} 位贡献者。${uniqueContributors < 3 ? '目前贡献者偏少,建议通过 good-first-issue 标签吸引新贡献者。' : '团队协作良好,有多位贡献者共同维护。'}
|
||||
**分析:** ${isMirror && uniqueContributors <= 1 ? '该项目为镜像仓库,贡献者数据不代表实际开发团队。' : uniqueContributors < 3 ? '目前贡献者偏少,建议通过 good-first-issue 标签吸引新贡献者。' : '团队协作良好,有多位贡献者共同维护。'}
|
||||
|
||||
## 五、社区健康度(${communityScore}/10)
|
||||
|
||||
| 指标 | 数据 | 评分 |
|
||||
|------|------|------|
|
||||
| Issue 总数 | ${totalIssues}(关闭 ${closedCount} / 打开 ${totalIssues - closedCount})| — |
|
||||
| 关闭率 | ${closeRate}% | ${closeRateScore}/10 |
|
||||
| PR 总数 | ${prTotal}(合并 ${prMerged} / 打开 ${prOpen})| — |
|
||||
| 合并率 | ${mergeRate}% | ${mergeRateScore}/10 |
|
||||
| Issue 总数 | ${totalIssuesStr} | — |
|
||||
| 关闭率 | ${totalIssues > 0 ? Math.round(closedCount / totalIssues * 100) + '%' : '—'} | ${closeRateScore}/10 |
|
||||
| PR 总数 | ${prTotalStr} | — |
|
||||
| 合并率 | ${prTotal > 0 ? Math.round(prMerged / prTotal * 100) + '%' : '—'} | ${mergeRateScore}/10 |
|
||||
|
||||
**分析:** Issue 关闭率 ${closeRate}%${closeRate > 50 ? ' ✅ 良好' : ' ⚠️ 偏低'};PR 合并率 ${mergeRate}%${mergeRate > 50 ? ' ✅ 良好' : ' ⚠️ 偏低'}。
|
||||
**分析:** ${isMirror && totalIssues === 0 && prTotal === 0 ? '该项目为镜像仓库,没有独立 Issue 和 PR 数据。' : (closeRate > 50 ? 'Issue 关闭率 ✅ 良好' : 'Issue 关闭率 ⚠️ 偏低') + ';' + (mergeRate > 50 ? 'PR 合并率 ✅ 良好' : 'PR 合并率 ⚠️ 偏低')}
|
||||
|
||||
## 六、可复现性评估
|
||||
| 检查项 | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| 开源许可证 | ${hasLicense} | ${repo.license_short_name || '建议添加以便他人使用'} |
|
||||
| 依赖文档化 | — | 需人工检查 go.mod / package.json |
|
||||
| 运行说明 | ${hasReadme} | ${description ? 'README 包含基本说明' : 'README 缺少运行说明'} |
|
||||
| CI 配置 | ${hasDevops} | ${repo.open_devops ? '已有 DevOps 流水线' : '建议配置 CI'} |
|
||||
| 开源许可证 | ${repo.license_short_name ? '✅' : '❌'} | ${repo.license_short_name || '建议添加以便他人使用'} |
|
||||
| 依赖文档化 | ${hasActivity ? '—' : '⏭️'} | ${hasActivity ? '需人工检查' : '镜像仓库,跳过'} |
|
||||
| 运行说明 | ${description && description.length > 5 ? '✅' : '❌'} | ${description ? 'README 包含基本说明' : 'README 缺少运行说明'} |
|
||||
| CI 配置 | ${repo.open_devops ? '✅' : '❌'} | ${repo.open_devops ? '已有 DevOps 流水线' : '建议配置 CI'} |
|
||||
|
||||
## 七、改进建议
|
||||
${suggestions.map((s, i) => (i + 1) + '. ' + s).join('\n')}
|
||||
|
|
|
|||
Loading…
Reference in New Issue