Add market watch and match hub workflows

这个提交包含在:
cryptocommuniums-afk
2026-04-07 11:00:03 +08:00
父节点 495da60212
当前提交 32ffad1545
修改 39 个文件,包含 6974 行新增330 行删除

查看文件

@@ -25,6 +25,7 @@ type NormalizedPlan = z.infer<typeof normalizedPlanSchema>;
type NormalizedAdjustedPlan = z.infer<typeof normalizedAdjustedPlanSchema>;
const dayKeyPattern = /^day[_\s-]?(\d+)$/i;
const EMPTY_PLAN_ERROR_MESSAGE = "训练计划结果为空,请重试或缩小训练重点后再生成。";
function extractTextContent(content: unknown) {
if (typeof content === "string") {
@@ -66,6 +67,15 @@ function toPositiveInteger(value: unknown, fallback: number) {
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function firstMeaningfulText(...values: Array<unknown>) {
for (const value of values) {
if (typeof value === "string" && value.trim().length > 0) {
return value.trim();
}
}
return null;
}
function inferCategory(...values: Array<unknown>) {
const text = values
.filter((value): value is string => typeof value === "string")
@@ -118,6 +128,52 @@ function normalizeExercise(
};
}
function createFallbackExercise(day: number, section: Record<string, unknown>) {
const focus = firstMeaningfulText(section.focus, section.summary, section.title, section.theme);
if (!focus) return null;
return normalizeExercise(day, {
day,
name: focus,
description: firstMeaningfulText(section.summary, section.description, `${focus}训练`) ?? `${focus}训练`,
duration: section.duration ?? section.duration_minutes ?? 12,
tips: firstMeaningfulText(section.tips, section.notes, `重点关注:${focus}`) ?? `重点关注:${focus}`,
sets: 3,
reps: 10,
}, section);
}
function normalizeCanonicalPlan(
raw: Record<string, unknown>,
fallbackTitle: string,
): NormalizedPlan {
const rawExercises = Array.isArray(raw.exercises)
? raw.exercises.filter(
(item): item is Record<string, unknown> =>
Boolean(item) && typeof item === "object" && !Array.isArray(item),
)
: [];
const exercises = rawExercises.map((exercise, index) =>
normalizeExercise(
toPositiveInteger(exercise.day, index + 1),
exercise,
),
);
if (exercises.length === 0) {
throw new Error(EMPTY_PLAN_ERROR_MESSAGE);
}
return normalizedPlanSchema.parse({
title:
typeof raw.title === "string" && raw.title.trim().length > 0
? raw.title.trim()
: fallbackTitle,
exercises,
});
}
function normalizeDayMapPlan(
raw: Record<string, unknown>,
fallbackTitle: string
@@ -143,9 +199,21 @@ function normalizeDayMapPlan(
)
: [];
for (const exercise of sectionExercises) {
exercises.push(normalizeExercise(day, exercise, section));
if (sectionExercises.length > 0) {
for (const exercise of sectionExercises) {
exercises.push(normalizeExercise(day, exercise, section));
}
continue;
}
const fallbackExercise = createFallbackExercise(day, section);
if (fallbackExercise) {
exercises.push(fallbackExercise);
}
}
if (exercises.length === 0) {
throw new Error(EMPTY_PLAN_ERROR_MESSAGE);
}
return normalizedPlanSchema.parse({
@@ -164,7 +232,7 @@ export function normalizeTrainingPlanResponse(params: {
const raw = parseJsonContent(params.content);
if (Array.isArray(raw.exercises)) {
return normalizedPlanSchema.parse(raw);
return normalizeCanonicalPlan(raw, params.fallbackTitle);
}
return normalizeDayMapPlan(raw, params.fallbackTitle);
@@ -177,8 +245,9 @@ export function normalizeAdjustedPlanResponse(params: {
const raw = parseJsonContent(params.content);
if (Array.isArray(raw.exercises)) {
const normalized = normalizeCanonicalPlan(raw, params.fallbackTitle);
return normalizedAdjustedPlanSchema.parse({
...raw,
...normalized,
adjustmentNotes:
typeof raw.adjustmentNotes === "string" && raw.adjustmentNotes.trim().length > 0
? raw.adjustmentNotes.trim()