修复项: - fix(db): 迁移 149 — 修复 Admin 角色权限绑定被迁移链破坏 (FE-C1) - fix(health): 4 个 handler 添加空名称验证 — Doctor/Article/AlertRule/Tag (API-C1~C4) - fix(health): Stats 仪表盘 new_this_week 查询修复 — SeaORM date_trunc bug (FE-C2) - fix(server): 添加安全响应头 — X-Frame-Options/CSP/XSS-Protection/Referrer-Policy (SEC-H1) - fix(mp): 预约创建契约修复 — notes/reason 字段映射 + 移除 schedule_id (MP-H1) - fix(mp): 咨询会话 subject/last_message 字段改为可选 (MP-H3) - fix(ai): AiConfig Default derive 替代手写 impl (clippy) 测试报告: - 8 维度端到端测试全部完成 (后端 87 用例 / 前端 30 页面 / 小程序 80+ API / 安全 20 项 / 性能 20 端点) - 多角色 7 角色 49 检查 100% 通过 - 综合测试报告 + 专家评估报告
107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import { api } from './request';
|
|
|
|
export interface Appointment {
|
|
id: string;
|
|
patient_name: string;
|
|
doctor_name: string;
|
|
department?: string;
|
|
appointment_date: string;
|
|
start_time: string;
|
|
end_time: string;
|
|
status: string;
|
|
version: number;
|
|
}
|
|
|
|
export interface Doctor {
|
|
id: string;
|
|
name: string;
|
|
department: string;
|
|
title?: string;
|
|
}
|
|
|
|
export interface DoctorSchedule {
|
|
id: string;
|
|
doctor_id: string;
|
|
date: string;
|
|
start_time: string;
|
|
end_time: string;
|
|
max_appointments: number;
|
|
current_appointments: number;
|
|
available_count: number;
|
|
}
|
|
|
|
export async function listAppointments(patientId?: string, page = 1) {
|
|
return api.get<{ data: Appointment[]; total: number }>('/health/appointments', {
|
|
page,
|
|
page_size: 20,
|
|
...(patientId && { patient_id: patientId }),
|
|
});
|
|
}
|
|
|
|
export async function getAppointment(id: string) {
|
|
return api.get<Appointment>(`/health/appointments/${id}`);
|
|
}
|
|
|
|
export async function createAppointment(data: {
|
|
patient_id: string;
|
|
doctor_id: string;
|
|
appointment_date: string;
|
|
start_time: string;
|
|
end_time: string;
|
|
notes?: string;
|
|
appointment_type?: string;
|
|
}) {
|
|
return api.post<Appointment>('/health/appointments', data);
|
|
}
|
|
|
|
export async function cancelAppointment(id: string, version: number) {
|
|
return api.put(`/health/appointments/${id}/status`, {
|
|
status: 'cancelled',
|
|
version,
|
|
});
|
|
}
|
|
|
|
export async function getDoctorSchedules(doctorId: string, startDate: string, endDate: string) {
|
|
const resp = await api.get<{ data: DoctorSchedule[]; total: number }>('/health/doctor-schedules', {
|
|
doctor_id: doctorId,
|
|
start_date: startDate,
|
|
end_date: endDate,
|
|
page_size: 50,
|
|
});
|
|
// 计算可用号源数
|
|
for (const s of resp.data) {
|
|
s.available_count = s.available_count ?? (s.max_appointments - s.current_appointments);
|
|
}
|
|
return resp;
|
|
}
|
|
|
|
export async function listDoctors(department?: string) {
|
|
return api.get<{ data: Doctor[]; total: number }>('/health/doctors', {
|
|
page_size: 100,
|
|
...(department && { department }),
|
|
});
|
|
}
|
|
|
|
export async function calendarView(startDate: string, endDate: string, doctorId?: string) {
|
|
const raw = await api.get<{ date: string; schedules: DoctorSchedule[] }[]>(
|
|
'/health/doctor-schedules/calendar',
|
|
{
|
|
start_date: startDate,
|
|
end_date: endDate,
|
|
...(doctorId && { doctor_id: doctorId }),
|
|
},
|
|
);
|
|
// 后台返回按日期分组的嵌套结构,展平为扁平数组并计算 available_count
|
|
const flat: DoctorSchedule[] = [];
|
|
for (const day of (raw as unknown as { date: string; schedules: DoctorSchedule[] }[])) {
|
|
for (const s of day.schedules) {
|
|
flat.push({
|
|
...s,
|
|
date: s.date || day.date,
|
|
available_count: s.available_count ?? (s.max_appointments - s.current_appointments),
|
|
});
|
|
}
|
|
}
|
|
return flat;
|
|
}
|