后端: - 事件流模型(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>
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
/**
|
|
* 一次性脚本:从 mock API 回填过去 N 天的营收事件。
|
|
* 同时给所有项目自动 seed project_revenue_mapping(identifier 作 businessProjectKey)。
|
|
* 用法: bun run scripts/backfill-revenue.ts [days=60]
|
|
*/
|
|
import { v4 as uuid } from 'uuid';
|
|
import dayjs from 'dayjs';
|
|
import { eq, inArray } from 'drizzle-orm';
|
|
import { db } from '../src/db/index';
|
|
import { projects, projectRevenueMapping } from '../src/db/schema';
|
|
import { runRevenueIngest } from '../src/services/roi/revenue-ingest';
|
|
|
|
const days = Number(process.argv[2] || 60);
|
|
|
|
// 1. seed mapping: identifier → projectId(没有就建)
|
|
const all = await db.select().from(projects);
|
|
const existing = await db.select().from(projectRevenueMapping);
|
|
const existingKeys = new Set(existing.map(m => m.businessProjectKey));
|
|
|
|
const toInsert = all
|
|
.filter(p => p.identifier && !existingKeys.has(p.identifier))
|
|
.map(p => ({
|
|
id: uuid(),
|
|
projectId: p.id,
|
|
businessProjectKey: p.identifier!,
|
|
enabled: 1,
|
|
notes: 'auto-seeded',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
}));
|
|
|
|
if (toInsert.length > 0) {
|
|
await db.insert(projectRevenueMapping).values(toInsert);
|
|
console.log(`Seeded ${toInsert.length} project mappings`);
|
|
}
|
|
|
|
// 2. 逐日拉 mock 数据
|
|
for (let i = days; i >= 0; i--) {
|
|
const date = dayjs().subtract(i, 'day').format('YYYY-MM-DD');
|
|
await runRevenueIngest(date);
|
|
if (i % 10 === 0) console.log(` → ${date}`);
|
|
}
|
|
console.log('Done.');
|
|
process.exit(0);
|