后端: - 事件流模型(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>
140 lines
4.6 KiB
TypeScript
140 lines
4.6 KiB
TypeScript
import request from './request';
|
|
|
|
export type RoiCategory = 'cash_cow' | 'efficiency_tool' | 'moat' | 'composite';
|
|
export type BizSystem = 'airhubs' | 'airflow' | 'aircore';
|
|
export type ProjectType = 'hardware' | 'software';
|
|
export type Confidence = 'high' | 'medium' | 'low';
|
|
|
|
export interface AggregateResult {
|
|
projectId: string;
|
|
from: string;
|
|
to: string;
|
|
totalCost: number;
|
|
totalRevenue: number;
|
|
netProfit: number;
|
|
roiValue: number | null;
|
|
confidence: Confidence;
|
|
bepDays: number | null;
|
|
costBreakdown: Record<string, number>;
|
|
revenueBreakdown: Record<string, number>;
|
|
costEventCount: number;
|
|
revenueEventCount: number;
|
|
}
|
|
|
|
export interface TimeseriesBucket {
|
|
bucket: string;
|
|
cost: number;
|
|
revenue: number;
|
|
net: number;
|
|
cumulativeCost: number;
|
|
cumulativeRevenue: number;
|
|
cumulativeRoi: number | null;
|
|
}
|
|
|
|
export interface DashboardResult {
|
|
from: string;
|
|
to: string;
|
|
summary: {
|
|
totalCost: number;
|
|
totalRevenue: number;
|
|
netProfit: number;
|
|
roiValue: number | null;
|
|
projectCount: number;
|
|
};
|
|
byCategory: Record<string, { totalCost: number; totalRevenue: number; netProfit: number; projectCount: number }>;
|
|
projects: Array<{
|
|
projectId: string;
|
|
name: string;
|
|
identifier: string;
|
|
category: RoiCategory | null;
|
|
totalCost: number;
|
|
totalRevenue: number;
|
|
roiValue: number | null;
|
|
confidence: Confidence;
|
|
}>;
|
|
}
|
|
|
|
export interface TagSuggestion {
|
|
suggestedCategory: RoiCategory;
|
|
suggestedBizSystem: BizSystem;
|
|
suggestedProjectType: ProjectType;
|
|
confidence: number;
|
|
reasoning: string;
|
|
}
|
|
|
|
export interface Strategy {
|
|
id: string;
|
|
category: RoiCategory;
|
|
name: string;
|
|
formulaKey: string;
|
|
params: {
|
|
hourlyRate: number;
|
|
amortYears?: number;
|
|
commitHourCoef: number;
|
|
taskHourCoef: number;
|
|
};
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface DriverFactor {
|
|
type: '现金流驱动' | '降本增效驱动' | '技术资产驱动';
|
|
text: string;
|
|
}
|
|
|
|
// ── 核心聚合 ──
|
|
export const aggregateRoi = (projectId: string, from: string, to: string) =>
|
|
request.get<{ code: number; data: AggregateResult }>(`/api/roi/aggregate`, { params: { projectId, from, to } });
|
|
|
|
export const timeseriesRoi = (projectId: string, from: string, to: string, granularity: 'day' | 'week' | 'month' | 'year' = 'month') =>
|
|
request.get<{ code: number; data: TimeseriesBucket[] }>(`/api/roi/timeseries`, { params: { projectId, from, to, granularity } });
|
|
|
|
export const fetchDashboard = (from: string, to: string) =>
|
|
request.get<{ code: number; data: DashboardResult }>(`/api/roi/dashboard`, { params: { from, to } });
|
|
|
|
// ── 事件流 ──
|
|
export const createCostEvent = (projectId: string, payload: any) =>
|
|
request.post(`/api/projects/${projectId}/cost-events`, payload);
|
|
|
|
export const createRevenueEvent = (projectId: string, payload: any) =>
|
|
request.post(`/api/projects/${projectId}/revenue-events`, payload);
|
|
|
|
export const listEvents = (projectId: string, type: 'cost' | 'revenue', from?: string, to?: string, limit = 100) =>
|
|
request.get(`/api/projects/${projectId}/events`, { params: { type, from, to, limit } });
|
|
|
|
export const deleteEvent = (projectId: string, eventId: string, type: 'cost' | 'revenue') =>
|
|
request.delete(`/api/projects/${projectId}/events/${eventId}`, { params: { type } });
|
|
|
|
// ── 策略 ──
|
|
export const fetchStrategies = () =>
|
|
request.get<{ code: number; data: Strategy[] }>(`/api/roi/strategies`);
|
|
|
|
export const updateStrategy = (id: string, params: any) =>
|
|
request.patch(`/api/roi/strategies/${id}`, { params });
|
|
|
|
// ── 打标 ──
|
|
export interface TagPayload {
|
|
category: RoiCategory;
|
|
compositeStrategies?: ('cash_cow' | 'efficiency_tool' | 'moat')[] | null;
|
|
bizSystem?: BizSystem | null;
|
|
projectType?: ProjectType | null;
|
|
ownerId?: string | null;
|
|
launchedAt?: string | null;
|
|
vAsset?: number | null;
|
|
tags?: string[] | null;
|
|
}
|
|
export const tagProject = (projectId: string, payload: TagPayload) =>
|
|
request.post(`/api/projects/${projectId}/tag`, payload);
|
|
|
|
export const suggestTag = (projectId: string) =>
|
|
request.post<{ code: number; data: TagSuggestion }>(`/api/projects/${projectId}/suggest-tag`, undefined, { timeout: 60000 });
|
|
|
|
// ── 项目映射 ──
|
|
export const listMapping = () => request.get(`/api/roi/mapping`);
|
|
export const createMapping = (payload: any) => request.post(`/api/roi/mapping`, payload);
|
|
export const deleteMapping = (id: string) => request.delete(`/api/roi/mapping/${id}`);
|
|
export const listUnmapped = () => request.get(`/api/roi/unmapped`);
|
|
|
|
// ── 驱动因子 ──
|
|
export const fetchDriverFactors = (projectId: string, periodKey?: string) =>
|
|
request.get(`/api/projects/${projectId}/driver-factors`, { params: { periodKey } });
|