fix(admin): 修正接口 schema 与路由不匹配

- classroomSchema 允许 building/status 为 null,教室查看页恢复数据
- 排期 matrix 单元格为对象而非数组,教室排期页恢复渲染
- 押金批量收取改用 /deposits/eligible-students 路由
- 学生下拉 schema 按 /deposits/student-lookups 实际结构校验
- 水电费录入改走 /expenses/student-utility,批量恢复改为 PUT
This commit is contained in:
2026-08-06 11:58:19 +08:00
parent 67e14e357c
commit 2e7bb81ebd
4 changed files with 18 additions and 9 deletions

View File

@@ -153,8 +153,8 @@ export const classroomSchema = z
.object({
id: z.number(),
name: z.string(),
building: z.string().optional(),
status: z.string().optional(),
building: z.string().nullable().optional(),
status: z.string().nullable().optional(),
})
.passthrough();
@@ -180,7 +180,11 @@ export const depositSchema = z
export const depositsSchema = z.array(depositSchema);
export const depositStudentLookupSchema = z
.object({ studentId: z.number(), name: z.string().nullable().optional() })
.object({
id: z.number(),
name: z.string().nullable().optional(),
studentNo: z.string().nullable().optional(),
})
.passthrough();
export const depositStudentLookupsSchema = z.array(depositStudentLookupSchema);

View File

@@ -4,7 +4,8 @@ export const classroomScheduleSchema = z
.object({
classrooms: z.array(z.record(z.string(), z.unknown())),
organizations: z.array(z.record(z.string(), z.unknown())),
matrix: z.record(z.string(), z.record(z.string(), z.array(z.record(z.string(), z.unknown())))),
// matrix: 教室 id → 日期 → 单条排课/租赁记录(对象,非数组)
matrix: z.record(z.string(), z.record(z.string(), z.record(z.string(), z.unknown()))),
summary: z.record(z.string(), z.record(z.string(), z.unknown())),
days: z.number().optional(),
})

View File

@@ -97,7 +97,11 @@ const DepositsPage: React.FC = () => {
{ invalidate: [['deposits']] },
);
const payInstallmentMutation = useApiMutation(
async (installmentId: number) => api.post(`/deposits/installments/${installmentId}/pay`),
async (installmentId: number) =>
api.put(`/deposits/installments/${installmentId}`, {
status: 'paid',
paidDate: dayjs().format('YYYY-MM-DD'),
}),
{ invalidate: [['deposits']] },
);
const saveInstallmentCellMutation = useApiMutation(
@@ -135,7 +139,7 @@ const DepositsPage: React.FC = () => {
if (eligibleRoomType) params.roomType = eligibleRoomType;
return validateResponse<EligibleStudent[]>(
eligibleStudentsSchema,
await api.get('/deposits/eligible', { params }),
await api.get('/deposits/eligible-students', { params }),
);
},
});

View File

@@ -132,7 +132,7 @@ const ExpensesPage: React.FC = () => {
{ invalidate: [['expenses']] },
),
utility: useApiMutation(
async (payload: Record<string, unknown>) => api.post('/expenses/utility', payload),
async (payload: Record<string, unknown>) => api.post('/expenses/student-utility', payload),
{ invalidate: [['expenses'], ['bills']] },
),
importUtility: useApiMutation(
@@ -166,11 +166,11 @@ const ExpensesPage: React.FC = () => {
{ invalidate: [['expenses']] },
),
batchRestoreRoom: useApiMutation(
async (ids: number[]) => api.post('/expenses/room/batch-restore', { ids }),
async (ids: number[]) => api.put('/expenses/room/batch-restore', { ids }),
{ invalidate: [['expenses']] },
),
batchRestorePersonal: useApiMutation(
async (ids: number[]) => api.post('/expenses/personal/batch-restore', { ids }),
async (ids: number[]) => api.put('/expenses/personal/batch-restore', { ids }),
{ invalidate: [['expenses']] },
),
purgeRoom: useApiMutation(