feat: #67 GET /activity/trend 新增 dailySeries 时间序列数据
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 43s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 43s
返回格式新增 dailySeries 字段 [{date, value, label}],支持 iOS 折线图/柱状图渲染。
数据来源:按日聚合 LearningActivity.durationSeconds。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9c14bda0c2
commit
4b8653080e
@ -8,6 +8,12 @@ export const TrendItemSchema = z.object({
|
||||
detail: z.string().max(500),
|
||||
});
|
||||
|
||||
export const DailySeriesItemSchema = z.object({
|
||||
date: z.string(),
|
||||
value: z.number(),
|
||||
label: z.string(),
|
||||
});
|
||||
|
||||
export const LearningTrendResultSchema = z.object({
|
||||
periodSummary: z.string().min(1).max(2000),
|
||||
overallScore: z.number().int().min(0).max(100),
|
||||
@ -17,9 +23,11 @@ export const LearningTrendResultSchema = z.object({
|
||||
weaknesses: z.array(z.string().max(500)).max(10).default([]),
|
||||
recommendations: z.array(z.string().max(500)).max(10).default([]),
|
||||
nextFocusAreas: z.array(z.string().max(200)).max(5).default([]),
|
||||
dailySeries: z.array(DailySeriesItemSchema).default([]),
|
||||
});
|
||||
|
||||
export type LearningTrendResult = z.infer<typeof LearningTrendResultSchema>;
|
||||
export type DailySeriesItem = z.infer<typeof DailySeriesItemSchema>;
|
||||
|
||||
export const LEARNING_TREND_OUTPUT_SCHEMA_DESC = `{
|
||||
"periodSummary": "过去7天,你的学习时长为320分钟,完成了45次主动回忆和120张复习卡片。整体掌握水平有所提升。",
|
||||
|
||||
@ -66,6 +66,9 @@ export class LearningActivityService {
|
||||
|
||||
const prevTotalMinutes = Math.round(sum(previous, 'durationSeconds') / 60);
|
||||
|
||||
// Build daily time-series for chart rendering
|
||||
const dailySeries = this.buildDailySeries(recent, periodDays);
|
||||
|
||||
const trendInput = {
|
||||
userId,
|
||||
periodDays,
|
||||
@ -86,6 +89,28 @@ export class LearningActivityService {
|
||||
} : undefined,
|
||||
};
|
||||
|
||||
return this.trendWorkflow.execute(trendInput);
|
||||
const aiResult = await this.trendWorkflow.execute(trendInput);
|
||||
return { ...aiResult, dailySeries };
|
||||
}
|
||||
|
||||
private buildDailySeries(activities: any[], days: number) {
|
||||
const series: { date: string; value: number; label: string }[] = [];
|
||||
const now = new Date();
|
||||
for (let i = days - 1; i >= 0; i--) {
|
||||
const d = new Date(now);
|
||||
d.setDate(d.getDate() - i);
|
||||
const dateStr = d.toISOString().split('T')[0];
|
||||
const dayActs = activities.filter((a) => {
|
||||
const ad = a.activityDate instanceof Date ? a.activityDate : new Date(a.activityDate);
|
||||
return ad.toISOString().split('T')[0] === dateStr;
|
||||
});
|
||||
const minutes = Math.round(dayActs.reduce((s: number, a: any) => s + a.durationSeconds, 0) / 60);
|
||||
series.push({
|
||||
date: dateStr,
|
||||
value: minutes,
|
||||
label: `${minutes}分钟`,
|
||||
});
|
||||
}
|
||||
return series;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user