后端: - 事件流模型(project_cost_events / project_revenue_events)+ launchedAt 截断 - 3 大业务体系归属(airhubs/airflow/aircore) + 项目类型(hw/sw) + identifier 自动生成 - AI 三件套推荐(category + bizSystem + projectType) - 营收 mock API + 外部对接规范 + 资产摊销 cron - 5 个 migration(0003 ROI 引擎 / 0004 driver factors / 0005 biz system) - 单测 11/11 过 前端: - 项目级 ROI 看板:4 KPI 卡片 + 折线图(周/月/年)+ 成本/产出事件流并排 - 全公司决策罗盘:3 大 ROI 指标 + 业务线堆叠 + 分类筛选 chip - 项目列表 + 侧边栏:按产品线分组(可折叠 + localStorage 持久化) - Admin: ROI 策略配置 + 项目映射 + 未映射收容 数据: - 23 项目全部 AI 自动分类 + 自动 identifier(airhubs-hw-001 这种) - launchedAt 按各项目首次 commit 时间设置 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
3.1 KiB
TypeScript
87 lines
3.1 KiB
TypeScript
/**
|
|
* ROI 引擎纯函数单元测试。
|
|
* 仅测不依赖 DB 的逻辑;聚合/查询走集成测试。
|
|
*/
|
|
import { describe, it, expect } from 'bun:test';
|
|
import { evaluateConfidence } from '../../src/services/roi/confidence-evaluator';
|
|
import { bepFromTotals } from '../../src/services/roi/bep-calculator';
|
|
|
|
describe('confidence-evaluator', () => {
|
|
it('returns low when no events at all', () => {
|
|
expect(evaluateConfidence([], [])).toBe('low');
|
|
});
|
|
|
|
it('returns low when only cost, no revenue', () => {
|
|
expect(evaluateConfidence([{ dataSource: 'manual' }], [])).toBe('low');
|
|
});
|
|
|
|
it('returns low when all cost is auto-estimated', () => {
|
|
const costs = [
|
|
{ dataSource: 'auto_commits' },
|
|
{ dataSource: 'auto_tasks' },
|
|
];
|
|
const revenues = [{ dataSource: 'manual' }];
|
|
expect(evaluateConfidence(costs, revenues)).toBe('low');
|
|
});
|
|
|
|
it('returns high when 80%+ cost is plane_actual/manual + revenue exists', () => {
|
|
const costs = [
|
|
{ dataSource: 'plane_actual' },
|
|
{ dataSource: 'plane_actual' },
|
|
{ dataSource: 'manual' },
|
|
{ dataSource: 'manual' },
|
|
{ dataSource: 'auto_commits' }, // 1/5 = 20% 自动,刚好满足 80%+ 高质量
|
|
];
|
|
const revenues = [{ dataSource: 'api_pulled' }];
|
|
expect(evaluateConfidence(costs, revenues)).toBe('high');
|
|
});
|
|
|
|
it('returns medium when high-quality cost ratio is between 1% and 80%', () => {
|
|
const costs = [
|
|
{ dataSource: 'plane_actual' },
|
|
{ dataSource: 'auto_commits' },
|
|
{ dataSource: 'auto_commits' },
|
|
];
|
|
const revenues = [{ dataSource: 'manual' }];
|
|
// 1/3 = 33% 高质量,落在 medium 区间
|
|
expect(evaluateConfidence(costs, revenues)).toBe('medium');
|
|
});
|
|
|
|
it('returns high when all cost manual', () => {
|
|
const costs = [{ dataSource: 'manual' }, { dataSource: 'manual' }];
|
|
const revenues = [{ dataSource: 'manual' }];
|
|
expect(evaluateConfidence(costs, revenues)).toBe('high');
|
|
});
|
|
});
|
|
|
|
describe('bepFromTotals', () => {
|
|
it('returns 0 when already broken even (revenue >= cost)', () => {
|
|
expect(bepFromTotals(100000, 100000, 0, 0)).toBe(0);
|
|
expect(bepFromTotals(100000, 150000, 0, 0)).toBe(0);
|
|
});
|
|
|
|
it('returns null when recent net income is non-positive', () => {
|
|
// 累计亏 5w,近 30 天净产出为 0
|
|
expect(bepFromTotals(100000, 50000, 30000, 30000)).toBe(null);
|
|
// 近 30 天还在亏
|
|
expect(bepFromTotals(100000, 50000, 30000, 20000)).toBe(null);
|
|
});
|
|
|
|
it('returns positive days when on track to break even', () => {
|
|
// 总投入 100w,总产出 80w => 缺口 20w
|
|
// 近 30 天:成本 3w,收入 6w => 日均净 1000 元
|
|
// 预计天数 = 200000 / 1000 = 200 天
|
|
expect(bepFromTotals(1_000_000, 800_000, 30_000, 60_000)).toBe(200);
|
|
});
|
|
|
|
it('rounds up partial days', () => {
|
|
// 缺口 1000,日均净 300 => 3.33 天向上取整 = 4
|
|
expect(bepFromTotals(2000, 1000, 0, 9000)).toBe(4); // daily = 9000/30 = 300
|
|
});
|
|
|
|
it('respects custom windowDays', () => {
|
|
// 缺口 1000,近 10 天净 1000 => 日均 100 => 10 天回本
|
|
expect(bepFromTotals(2000, 1000, 0, 1000, 10)).toBe(10);
|
|
});
|
|
});
|