From c2c99126beb39cf87b430c1f7942ed810f1aee41 Mon Sep 17 00:00:00 2001 From: selfrelease Date: Sun, 16 Aug 2026 09:12:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=83=A8=E9=97=A8=E5=88=9B=E5=BB=BA/?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=B6=20parentId=20=E7=A9=BA=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E5=AF=BC=E8=87=B4=E5=A4=96=E9=94=AE=E7=BA=A6?= =?UTF-8?q?=E6=9D=9F=E8=BF=9D=E5=8F=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前端选择"无(根部门)"时传 parentId="",空字符串是 falsy 跳过了 父部门存在性检查,但 ...data 展开后 Prisma 尝试创建 parentId="" 的部门,数据库外键约束违反(无 id="" 的部门)。 修复:创建/更新时将空字符串统一转为 null,并显式传字段而非展开。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- backend/src/routes/department.routes.ts | 29 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/backend/src/routes/department.routes.ts b/backend/src/routes/department.routes.ts index 4336396..c5aeb44 100644 --- a/backend/src/routes/department.routes.ts +++ b/backend/src/routes/department.routes.ts @@ -36,16 +36,21 @@ router.get('/', authMiddleware, async (req: AuthRequest, res, next) => { router.post('/', authMiddleware, async (req: AuthRequest, res, next) => { try { const data = createDeptSchema.parse(req.body) + // 空字符串视为无父部门,统一转为 null + const parentId = data.parentId && data.parentId.trim() !== '' ? data.parentId : null let level = 0 - if (data.parentId) { - const parent = await prisma.department.findFirst({ where: { id: data.parentId, orgId: req.user!.orgId! } }) + if (parentId) { + const parent = await prisma.department.findFirst({ where: { id: parentId, orgId: req.user!.orgId! } }) if (!parent) throw { code: 'NOT_FOUND', message: '父部门不存在' } level = parent.level + 1 } const dept = await prisma.department.create({ data: { - ...data, + name: data.name, + parentId, level, + sortOrder: data.sortOrder, + description: data.description, orgId: req.user!.orgId!, createdBy: req.user!.id, }, @@ -61,19 +66,27 @@ router.put('/:id', authMiddleware, async (req: AuthRequest, res, next) => { try { const { id } = req.params const data = updateDeptSchema.parse(req.body) + // 空字符串视为无父部门,统一转为 null + const parentId = data.parentId !== undefined && data.parentId !== null && data.parentId.trim() !== '' ? data.parentId : data.parentId === undefined ? undefined : null // 防止循环引用 - if (data.parentId === id) throw { code: 'VALIDATION_ERROR', message: '不能将自身设为父部门' } + if (parentId === id) throw { code: 'VALIDATION_ERROR', message: '不能将自身设为父部门' } let level: number | undefined - if (data.parentId) { - const parent = await prisma.department.findFirst({ where: { id: data.parentId, orgId: req.user!.orgId! } }) + if (parentId) { + const parent = await prisma.department.findFirst({ where: { id: parentId, orgId: req.user!.orgId! } }) if (!parent) throw { code: 'NOT_FOUND', message: '父部门不存在' } level = parent.level + 1 - } else if (data.parentId === null) { + } else if (parentId === null) { level = 0 } const dept = await prisma.department.update({ where: { id }, - data: { ...data, level }, + data: { + ...(data.name !== undefined ? { name: data.name } : {}), + ...(parentId !== undefined ? { parentId } : {}), + ...(data.sortOrder !== undefined ? { sortOrder: data.sortOrder } : {}), + ...(data.description !== undefined ? { description: data.description } : {}), + ...(level !== undefined ? { level } : {}), + }, }) res.json({ success: true, data: dept }) } catch (err) {