feat: 批量导入支持记录三方 id,实现增量更新,存量覆盖
This commit is contained in:
@@ -56,6 +56,38 @@ function parsePosition(raw: unknown): number | undefined {
|
||||
return n
|
||||
}
|
||||
|
||||
function parseRiddleId(raw: unknown): string | null {
|
||||
if (raw === undefined || raw === null || raw === '') return null
|
||||
if (typeof raw !== 'string') {
|
||||
throw new Error('riddleId 必须是字符串')
|
||||
}
|
||||
|
||||
const riddleId = raw.trim()
|
||||
if (!riddleId) return null
|
||||
if (riddleId.length > 128) {
|
||||
throw new Error('riddleId 长度不能超过 128')
|
||||
}
|
||||
return riddleId
|
||||
}
|
||||
|
||||
function optionalString(raw: unknown): string | null {
|
||||
return raw ? String(raw) : null
|
||||
}
|
||||
|
||||
function buildLevelData(body: Record<string, unknown>) {
|
||||
return {
|
||||
image1Url: body.image1Url as string,
|
||||
image1Description: optionalString(body.image1Description),
|
||||
image2Url: body.image2Url as string,
|
||||
image2Description: optionalString(body.image2Description),
|
||||
answer: body.answer as string,
|
||||
punchline: optionalString(body.punchline),
|
||||
hint1: optionalString(body.hint1),
|
||||
hint2: optionalString(body.hint2),
|
||||
hint3: optionalString(body.hint3),
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/levels - Create a new level
|
||||
// body.position 可选:1..N+1;不传则追加末尾
|
||||
export async function POST(request: NextRequest) {
|
||||
@@ -69,17 +101,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const {
|
||||
image1Url,
|
||||
image1Description,
|
||||
image2Url,
|
||||
image2Description,
|
||||
answer,
|
||||
punchline,
|
||||
hint1,
|
||||
hint2,
|
||||
hint3,
|
||||
} = body
|
||||
const { image1Url, image2Url, answer } = body
|
||||
|
||||
if (!image1Url || !image2Url || !answer) {
|
||||
return NextResponse.json(
|
||||
@@ -89,15 +111,49 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
let position: number | undefined
|
||||
let riddleId: string | null
|
||||
try {
|
||||
position = parsePosition(body.position)
|
||||
riddleId = parseRiddleId(body.riddleId)
|
||||
} catch (e) {
|
||||
return NextResponse.json(
|
||||
{ error: e instanceof Error ? e.message : 'invalid position' },
|
||||
{ error: e instanceof Error ? e.message : 'invalid level payload' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const data = buildLevelData(body)
|
||||
|
||||
if (riddleId) {
|
||||
const existing = await prisma.level.findUnique({
|
||||
where: { riddleId },
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
let newSortKey: string | undefined
|
||||
if (position !== undefined) {
|
||||
const total = await prisma.level.count()
|
||||
if (position > total) {
|
||||
return NextResponse.json(
|
||||
{ error: `position 超出范围,合法区间 [1, ${total}]` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
newSortKey = await keyForPosition(position, existing.id)
|
||||
}
|
||||
|
||||
const level = await prisma.level.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
...data,
|
||||
...(newSortKey ? { sortKey: newSortKey } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json(level)
|
||||
}
|
||||
}
|
||||
|
||||
let sortKey: string
|
||||
if (position === undefined) {
|
||||
sortKey = await keyAfterLast()
|
||||
@@ -115,15 +171,8 @@ export async function POST(request: NextRequest) {
|
||||
const level = await prisma.level.create({
|
||||
data: {
|
||||
id: uuidv4(),
|
||||
image1Url,
|
||||
image1Description: image1Description || null,
|
||||
image2Url,
|
||||
image2Description: image2Description || null,
|
||||
answer,
|
||||
punchline: punchline || null,
|
||||
hint1: hint1 || null,
|
||||
hint2: hint2 || null,
|
||||
hint3: hint3 || null,
|
||||
riddleId,
|
||||
...data,
|
||||
sortKey,
|
||||
},
|
||||
})
|
||||
@@ -151,18 +200,7 @@ export async function PUT(request: NextRequest) {
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const {
|
||||
id,
|
||||
image1Url,
|
||||
image1Description,
|
||||
image2Url,
|
||||
image2Description,
|
||||
answer,
|
||||
punchline,
|
||||
hint1,
|
||||
hint2,
|
||||
hint3,
|
||||
} = body
|
||||
const { id } = body
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json({ error: 'id is required' }, { status: 400 })
|
||||
@@ -191,18 +229,12 @@ export async function PUT(request: NextRequest) {
|
||||
newSortKey = await keyForPosition(position, id)
|
||||
}
|
||||
|
||||
const data = buildLevelData(body)
|
||||
|
||||
const level = await prisma.level.update({
|
||||
where: { id },
|
||||
data: {
|
||||
image1Url,
|
||||
image1Description: image1Description || null,
|
||||
image2Url,
|
||||
image2Description: image2Description || null,
|
||||
answer,
|
||||
punchline: punchline || null,
|
||||
hint1: hint1 || null,
|
||||
hint2: hint2 || null,
|
||||
hint3: hint3 || null,
|
||||
...data,
|
||||
...(newSortKey ? { sortKey: newSortKey } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user