011bd6d4ca
交付写作核心(TipTap、自动保存、分卷分章)、Onboarding、7 主题与双语 i18n、快捷键设置;主进程使用 node:sqlite;补全 Vitest 与 Playwright E2E;统一 design/ 目录并移除 desigin 拼写错误路径。 Co-authored-by: Cursor <cursoragent@cursor.com>
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
import { randomUUID } from 'crypto'
|
|
import type { Volume } from '../../../shared/types'
|
|
import type { SqliteDb } from '../types'
|
|
function mapRow(row: Record<string, unknown>): Volume {
|
|
return {
|
|
id: row.id as string,
|
|
name: row.name as string,
|
|
description: row.description as string,
|
|
sortOrder: row.sort_order as number
|
|
}
|
|
}
|
|
|
|
export class VolumeRepository {
|
|
constructor(private db: SqliteDb) {}
|
|
create(name: string, sortOrder: number): Volume {
|
|
const id = randomUUID()
|
|
this.db
|
|
.prepare(
|
|
`INSERT INTO volumes (id, name, sort_order) VALUES (?, ?, ?)`
|
|
)
|
|
.run(id, name, sortOrder)
|
|
return this.get(id)!
|
|
}
|
|
|
|
get(id: string): Volume | null {
|
|
const row = this.db.prepare('SELECT * FROM volumes WHERE id = ?').get(id) as
|
|
| Record<string, unknown>
|
|
| undefined
|
|
return row ? mapRow(row) : null
|
|
}
|
|
|
|
list(): Volume[] {
|
|
return (
|
|
this.db.prepare('SELECT * FROM volumes ORDER BY sort_order').all() as Record<string, unknown>[]
|
|
).map(mapRow)
|
|
}
|
|
|
|
update(id: string, patch: { name?: string; description?: string }): Volume {
|
|
const existing = this.get(id)
|
|
if (!existing) throw new Error('Volume not found')
|
|
this.db
|
|
.prepare(
|
|
`UPDATE volumes SET name = ?, description = ?, updated_at = datetime('now') WHERE id = ?`
|
|
)
|
|
.run(patch.name ?? existing.name, patch.description ?? existing.description, id)
|
|
return this.get(id)!
|
|
}
|
|
|
|
delete(id: string): void {
|
|
this.db.prepare('DELETE FROM volumes WHERE id = ?').run(id)
|
|
}
|
|
}
|