From 7f3e30ba38c60eb756b129cabd813b1f1f44b2e0 Mon Sep 17 00:00:00 2001 From: wangziqi Date: Thu, 6 Aug 2026 11:59:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(archive):=20=E6=A1=A3=E6=A1=88=E8=81=9A?= =?UTF-8?q?=E5=90=88=E6=8E=A5=E5=8F=A3=20null=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E5=BD=92=E4=B8=80=E5=8C=96=E4=B8=BA=E7=A9=BA=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 避免可选列缺省返回 null 导致前端响应校验报「字段格式异常」 --- .../src/archive/archive.service.spec.ts | 50 +++++++++++++++++++ apps/server/src/archive/archive.service.ts | 15 +++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/apps/server/src/archive/archive.service.spec.ts b/apps/server/src/archive/archive.service.spec.ts index 09c3451..643a458 100644 --- a/apps/server/src/archive/archive.service.spec.ts +++ b/apps/server/src/archive/archive.service.spec.ts @@ -36,4 +36,54 @@ describe('ArchiveService.getProfile', () => { expect(response).toMatchObject({ student, result, attendances: [] }); expect(response).not.toHaveProperty('resultArchive'); }); + + it('将学生可空字符串字段归一化为空串,避免前端校验报格式异常', async () => { + const student = { + id: 7, + name: '测试学生', + phone: null, + idNumber: null, + studentNo: null, + gender: null, + ethnicity: null, + emergencyContact: null, + emergencyPhone: null, + supervisor: null, + }; + const emptyRepos = { + profile: { findOne: jest.fn().mockResolvedValue(null) }, + enrollment: { find: jest.fn().mockResolvedValue([]) }, + exam: { find: jest.fn().mockResolvedValue([]) }, + learning: { find: jest.fn().mockResolvedValue([]) }, + result: { findOne: jest.fn().mockResolvedValue(null) }, + attachment: { find: jest.fn().mockResolvedValue([]) }, + attendance: { find: jest.fn().mockResolvedValue([]) }, + }; + const service = new ArchiveService( + { findOne: jest.fn().mockResolvedValue(student) } as never, + emptyRepos.profile as never, + emptyRepos.enrollment as never, + emptyRepos.exam as never, + emptyRepos.learning as never, + emptyRepos.result as never, + emptyRepos.attachment as never, + emptyRepos.attendance as never, + {} as never, + ); + + const response = await service.getProfile(7); + + expect(response.student).toMatchObject({ + id: 7, + name: '测试学生', + phone: '', + idNumber: '', + studentNo: '', + gender: '', + ethnicity: '', + emergencyContact: '', + emergencyPhone: '', + supervisor: '', + }); + }); }); diff --git a/apps/server/src/archive/archive.service.ts b/apps/server/src/archive/archive.service.ts index ac210b1..4cf083f 100644 --- a/apps/server/src/archive/archive.service.ts +++ b/apps/server/src/archive/archive.service.ts @@ -63,6 +63,19 @@ export class ArchiveService { relations: ['organization'], }); if (!student) throw new NotFoundException('学生不存在'); + // 前端档案契约要求这些字段为字符串;可空列缺省时归一化为空串, + // 避免接口返回 null 导致响应校验报「student.idNumber 格式异常」。 + const normalizedStudent = { + ...student, + phone: student.phone ?? '', + idNumber: student.idNumber ?? '', + studentNo: student.studentNo ?? '', + gender: student.gender ?? '', + ethnicity: student.ethnicity ?? '', + emergencyContact: student.emergencyContact ?? '', + emergencyPhone: student.emergencyPhone ?? '', + supervisor: student.supervisor ?? '', + }; const [ profileRaw, @@ -91,7 +104,7 @@ export class ArchiveService { ]); return { - student, + student: normalizedStudent, profile: profileRaw, enrollments, examScores,