feat(workspace): 增加工作活动日报引擎
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { TaskWorklog } from './task-worklog';
|
||||
import type { WorkActivity, WorkActivityCategory } from './work-activity';
|
||||
import type { WorkItem } from './workspace-engine';
|
||||
|
||||
export interface WorkspaceDailyReportItem {
|
||||
@@ -13,14 +14,40 @@ export interface WorkspaceDailyReportItem {
|
||||
versionName?: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceDailyReportActivityItem {
|
||||
id: string;
|
||||
sourceId: string;
|
||||
sourceType: WorkActivity['sourceType'];
|
||||
action: WorkActivity['action'];
|
||||
category: WorkActivityCategory;
|
||||
title: string;
|
||||
summary: string;
|
||||
occurredAt: string;
|
||||
context?: string;
|
||||
metadata?: WorkActivity['metadata'];
|
||||
}
|
||||
|
||||
export interface WorkspaceDailyReportNeedsProgressItem {
|
||||
id: string;
|
||||
title: string;
|
||||
status: string;
|
||||
type: WorkItem['type'];
|
||||
context?: string;
|
||||
}
|
||||
|
||||
export type WorkspaceDailyReportGroups = Record<WorkActivityCategory, WorkspaceDailyReportActivityItem[]>;
|
||||
|
||||
export interface WorkspaceDailyReport {
|
||||
date: string;
|
||||
totalHours: number;
|
||||
totalCount: number;
|
||||
items: WorkspaceDailyReportItem[];
|
||||
groups: WorkspaceDailyReportGroups;
|
||||
needsProgressItems: WorkspaceDailyReportNeedsProgressItem[];
|
||||
}
|
||||
|
||||
interface GetWorkspaceDailyReportInput {
|
||||
activities?: WorkActivity[];
|
||||
worklogs: TaskWorklog[];
|
||||
workItems: WorkItem[];
|
||||
userId: string;
|
||||
@@ -28,6 +55,7 @@ interface GetWorkspaceDailyReportInput {
|
||||
}
|
||||
|
||||
export function getWorkspaceDailyReport({
|
||||
activities = [],
|
||||
worklogs,
|
||||
workItems,
|
||||
userId,
|
||||
@@ -54,10 +82,85 @@ export function getWorkspaceDailyReport({
|
||||
};
|
||||
});
|
||||
|
||||
const dailyActivities = activities
|
||||
.filter((activity) => activity.actorId === userId && activity.date === date)
|
||||
.sort((a, b) => b.occurredAt.localeCompare(a.occurredAt));
|
||||
|
||||
const groups = makeEmptyGroups();
|
||||
for (const activity of dailyActivities) {
|
||||
const item = workItemMap.get(activity.sourceId);
|
||||
groups[activity.category].push({
|
||||
id: activity.id,
|
||||
sourceId: activity.sourceId,
|
||||
sourceType: activity.sourceType,
|
||||
action: activity.action,
|
||||
category: activity.category,
|
||||
title: activity.title,
|
||||
summary: activity.summary,
|
||||
occurredAt: activity.occurredAt,
|
||||
context: getContext(item),
|
||||
metadata: activity.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
const touchedSourceIds = new Set([
|
||||
...dailyActivities.map((activity) => activity.sourceId),
|
||||
...items.map((item) => item.taskId),
|
||||
]);
|
||||
|
||||
const needsProgressItems = workItems
|
||||
.filter((item) => shouldRequireProgress(item, date, touchedSourceIds))
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
status: item.status,
|
||||
type: item.type,
|
||||
context: getContext(item),
|
||||
}));
|
||||
|
||||
return {
|
||||
date,
|
||||
totalHours: items.reduce((sum, item) => sum + item.hours, 0),
|
||||
totalCount: items.length,
|
||||
totalCount: items.length + dailyActivities.length,
|
||||
items,
|
||||
groups,
|
||||
needsProgressItems,
|
||||
};
|
||||
}
|
||||
|
||||
function makeEmptyGroups(): WorkspaceDailyReportGroups {
|
||||
return {
|
||||
delivery: [],
|
||||
progress: [],
|
||||
creation: [],
|
||||
risk: [],
|
||||
note: [],
|
||||
};
|
||||
}
|
||||
|
||||
function getContext(item?: WorkItem): string | undefined {
|
||||
if (!item) return undefined;
|
||||
return [item.productName, item.projectName, item.versionName].filter(Boolean).join(' / ');
|
||||
}
|
||||
|
||||
function shouldRequireProgress(item: WorkItem, date: string, touchedSourceIds: Set<string>): boolean {
|
||||
if (item.completed) return false;
|
||||
if (touchedSourceIds.has(item.id)) return false;
|
||||
if (!isInProgressStatus(item.status)) return false;
|
||||
|
||||
const startedAt = getStartedAt(item);
|
||||
if (!startedAt) return false;
|
||||
return startedAt.slice(0, 10) < date;
|
||||
}
|
||||
|
||||
function isInProgressStatus(status: string): boolean {
|
||||
return ['in_progress', 'testing', 'running', 'fixing', 'verifying'].includes(status);
|
||||
}
|
||||
|
||||
function getStartedAt(item: WorkItem): string | undefined {
|
||||
const extra = item.extra ?? {};
|
||||
if (typeof extra.actualStartAt === 'string') return extra.actualStartAt;
|
||||
if (typeof extra.startedAt === 'string') return extra.startedAt;
|
||||
if (typeof extra.startTime === 'string') return extra.startTime;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user