diff --git a/src/lib/hugoParser.ts b/src/lib/hugoParser.ts index 0ccaba0..eda08d0 100644 --- a/src/lib/hugoParser.ts +++ b/src/lib/hugoParser.ts @@ -10,6 +10,8 @@ export interface HugoPostData { updated_at: string; categories: string[]; tags: string[]; + series: string[]; + series_weight: number | null; } /** @@ -41,6 +43,12 @@ function parseArrayField(val: unknown): string[] { return []; } +function parseIntegerField(val: unknown): number | null { + if (val === null || val === undefined || val === '') return null; + const parsed = typeof val === 'number' ? val : Number(String(val).trim()); + return Number.isInteger(parsed) ? parsed : null; +} + /** * 去掉文件名结尾的格式拓展名,提供纯文件名基础 */ @@ -120,6 +128,8 @@ export function parseHugoMarkdown(rawContent: string, fileName = ''): HugoPostDa published_at, updated_at, categories: parseArrayField(meta.categories || meta.category), - tags: parseArrayField(meta.tags || meta.tag) + tags: parseArrayField(meta.tags || meta.tag), + series: parseArrayField(meta.series), + series_weight: parseIntegerField(meta.series_weight) }; } diff --git a/src/lib/server/series.ts b/src/lib/server/series.ts new file mode 100644 index 0000000..62a87fe --- /dev/null +++ b/src/lib/server/series.ts @@ -0,0 +1,48 @@ +export class SeriesValidationError extends Error {} + +export function parseSeriesId(value: FormDataEntryValue | null): number | null { + if (value === null || value.toString().trim() === '') return null; + + const id = Number(value); + if (!Number.isInteger(id) || id <= 0) { + throw new SeriesValidationError('系列选择无效'); + } + + return id; +} + +export function parseSeriesWeight(value: FormDataEntryValue | null): number | null { + if (value === null || value.toString().trim() === '') return null; + + const weight = Number(value); + if (!Number.isInteger(weight)) { + throw new SeriesValidationError('系列顺序必须是整数'); + } + + return weight; +} + +export async function resolveSeriesWeight( + db: D1Database, + seriesId: number | null, + requestedWeight: number | null, + excludePostId?: string +): Promise { + if (seriesId === null) return null; + + const series = await db.prepare('SELECT id FROM series WHERE id = ?').bind(seriesId).first(); + if (!series) { + throw new SeriesValidationError('所选系列不存在'); + } + + if (requestedWeight !== null) return requestedWeight; + + const query = excludePostId + ? 'SELECT COALESCE(MAX(series_weight), 0) + 1 AS next_weight FROM posts WHERE series_id = ? AND id != ?' + : 'SELECT COALESCE(MAX(series_weight), 0) + 1 AS next_weight FROM posts WHERE series_id = ?'; + const result = excludePostId + ? await db.prepare(query).bind(seriesId, excludePostId).first<{ next_weight: number }>() + : await db.prepare(query).bind(seriesId).first<{ next_weight: number }>(); + + return result?.next_weight ?? 1; +}