feat(web): 健康模块 API 服务层 6 文件 47 端点
- patients.ts: 患者CRUD/标签/家庭/医护关联 14端点 - healthData.ts: 体征/化验/健康档案CRUD + 趋势 18端点 - appointments.ts: 预约CRUD + 排班管理 + 日历 8端点 - followUp.ts: 随访任务/记录CRUD 7端点 - consultations.ts: 咨询会话/消息CRUD + 导出 6端点 - doctors.ts: 医护CRUD 5端点
This commit is contained in:
160
apps/web/src/api/health/appointments.ts
Normal file
160
apps/web/src/api/health/appointments.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface Appointment {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
appointment_type: string;
|
||||||
|
appointment_date: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
status: string;
|
||||||
|
cancel_reason?: string;
|
||||||
|
notes?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateAppointmentReq {
|
||||||
|
patient_id: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
appointment_type?: string;
|
||||||
|
appointment_date: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateAppointmentStatusReq {
|
||||||
|
status: string;
|
||||||
|
cancel_reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Schedule {
|
||||||
|
id: string;
|
||||||
|
doctor_id: string;
|
||||||
|
schedule_date: string;
|
||||||
|
period_type: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
max_appointments: number;
|
||||||
|
current_appointments: number;
|
||||||
|
status: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateScheduleReq {
|
||||||
|
doctor_id: string;
|
||||||
|
schedule_date: string;
|
||||||
|
period_type?: string;
|
||||||
|
start_time: string;
|
||||||
|
end_time: string;
|
||||||
|
max_appointments: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateScheduleReq {
|
||||||
|
start_time?: string;
|
||||||
|
end_time?: string;
|
||||||
|
max_appointments?: number;
|
||||||
|
status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CalendarDay {
|
||||||
|
date: string;
|
||||||
|
schedules: Schedule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const appointmentApi = {
|
||||||
|
list: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
status?: string;
|
||||||
|
patient_id?: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
date?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<Appointment>;
|
||||||
|
}>('/health/appointments', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
get: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: Appointment;
|
||||||
|
}>(`/health/appointments/${id}`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
create: async (req: CreateAppointmentReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: Appointment;
|
||||||
|
}>('/health/appointments', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateStatus: async (
|
||||||
|
id: string,
|
||||||
|
req: UpdateAppointmentStatusReq & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: Appointment;
|
||||||
|
}>(`/health/appointments/${id}/status`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Schedules
|
||||||
|
listSchedules: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
doctor_id?: string;
|
||||||
|
date?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<Schedule>;
|
||||||
|
}>('/health/doctor-schedules', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createSchedule: async (req: CreateScheduleReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: Schedule;
|
||||||
|
}>('/health/doctor-schedules', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSchedule: async (
|
||||||
|
id: string,
|
||||||
|
req: UpdateScheduleReq & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: Schedule;
|
||||||
|
}>(`/health/doctor-schedules/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
calendar: async (params: {
|
||||||
|
start_date: string;
|
||||||
|
end_date: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: CalendarDay[];
|
||||||
|
}>('/health/doctor-schedules/calendar', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
107
apps/web/src/api/health/consultations.ts
Normal file
107
apps/web/src/api/health/consultations.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface Session {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
consultation_type: string;
|
||||||
|
status: string;
|
||||||
|
last_message_at?: string;
|
||||||
|
unread_count_patient: number;
|
||||||
|
unread_count_doctor: number;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateSessionReq {
|
||||||
|
patient_id: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
consultation_type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Message {
|
||||||
|
id: string;
|
||||||
|
session_id: string;
|
||||||
|
sender_id: string;
|
||||||
|
sender_role: string;
|
||||||
|
content_type: string;
|
||||||
|
content: string;
|
||||||
|
is_read: boolean;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateMessageReq {
|
||||||
|
session_id: string;
|
||||||
|
sender_id: string;
|
||||||
|
sender_role: string;
|
||||||
|
content_type?: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const consultationApi = {
|
||||||
|
listSessions: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
status?: string;
|
||||||
|
patient_id?: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<Session>;
|
||||||
|
}>('/health/consultation-sessions', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createSession: async (req: CreateSessionReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: Session;
|
||||||
|
}>('/health/consultation-sessions', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
closeSession: async (
|
||||||
|
id: string,
|
||||||
|
req: { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: Session;
|
||||||
|
}>(`/health/consultation-sessions/${id}/close`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
listMessages: async (
|
||||||
|
sessionId: string,
|
||||||
|
params: { page?: number; page_size?: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<Message>;
|
||||||
|
}>(`/health/consultation-sessions/${sessionId}/messages`, { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createMessage: async (req: CreateMessageReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: Message;
|
||||||
|
}>('/health/consultation-messages', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
exportSessions: async (params: {
|
||||||
|
status?: string;
|
||||||
|
patient_id?: string;
|
||||||
|
doctor_id?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: Session[];
|
||||||
|
}>('/health/consultation-sessions/export', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
83
apps/web/src/api/health/doctors.ts
Normal file
83
apps/web/src/api/health/doctors.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface Doctor {
|
||||||
|
id: string;
|
||||||
|
user_id?: string;
|
||||||
|
name: string;
|
||||||
|
department?: string;
|
||||||
|
title?: string;
|
||||||
|
specialty?: string;
|
||||||
|
license_number?: string;
|
||||||
|
bio?: string;
|
||||||
|
online_status: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateDoctorReq {
|
||||||
|
user_id?: string;
|
||||||
|
name: string;
|
||||||
|
department?: string;
|
||||||
|
title?: string;
|
||||||
|
specialty?: string;
|
||||||
|
license_number?: string;
|
||||||
|
bio?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateDoctorReq {
|
||||||
|
name?: string;
|
||||||
|
department?: string;
|
||||||
|
title?: string;
|
||||||
|
specialty?: string;
|
||||||
|
license_number?: string;
|
||||||
|
bio?: string;
|
||||||
|
online_status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const doctorApi = {
|
||||||
|
list: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
search?: string;
|
||||||
|
department?: string;
|
||||||
|
title?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<Doctor>;
|
||||||
|
}>('/health/doctors', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
get: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: Doctor;
|
||||||
|
}>(`/health/doctors/${id}`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
create: async (req: CreateDoctorReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: Doctor;
|
||||||
|
}>('/health/doctors', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
update: async (id: string, req: UpdateDoctorReq & { version: number }) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: Doctor;
|
||||||
|
}>(`/health/doctors/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: async (id: string) => {
|
||||||
|
await client.delete(`/health/doctors/${id}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
131
apps/web/src/api/health/followUp.ts
Normal file
131
apps/web/src/api/health/followUp.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface FollowUpTask {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
assigned_to?: string;
|
||||||
|
follow_up_type: string;
|
||||||
|
planned_date: string;
|
||||||
|
status: string;
|
||||||
|
content_template?: string;
|
||||||
|
related_appointment_id?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateFollowUpTaskReq {
|
||||||
|
patient_id: string;
|
||||||
|
assigned_to?: string;
|
||||||
|
follow_up_type: string;
|
||||||
|
planned_date: string;
|
||||||
|
content_template?: string;
|
||||||
|
related_appointment_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateFollowUpTaskReq {
|
||||||
|
assigned_to?: string;
|
||||||
|
follow_up_type?: string;
|
||||||
|
planned_date?: string;
|
||||||
|
content_template?: string;
|
||||||
|
status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FollowUpRecord {
|
||||||
|
id: string;
|
||||||
|
task_id: string;
|
||||||
|
executed_by?: string;
|
||||||
|
executed_date: string;
|
||||||
|
result?: string;
|
||||||
|
patient_condition?: string;
|
||||||
|
medical_advice?: string;
|
||||||
|
next_follow_up_date?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateFollowUpRecordReq {
|
||||||
|
task_id: string;
|
||||||
|
executed_by?: string;
|
||||||
|
executed_date: string;
|
||||||
|
result?: string;
|
||||||
|
patient_condition?: string;
|
||||||
|
medical_advice?: string;
|
||||||
|
next_follow_up_date?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const followUpApi = {
|
||||||
|
// Tasks
|
||||||
|
listTasks: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
patient_id?: string;
|
||||||
|
assigned_to?: string;
|
||||||
|
status?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<FollowUpTask>;
|
||||||
|
}>('/health/follow-up-tasks', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTask: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: FollowUpTask;
|
||||||
|
}>(`/health/follow-up-tasks/${id}`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createTask: async (req: CreateFollowUpTaskReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: FollowUpTask;
|
||||||
|
}>('/health/follow-up-tasks', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTask: async (
|
||||||
|
id: string,
|
||||||
|
req: UpdateFollowUpTaskReq & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: FollowUpTask;
|
||||||
|
}>(`/health/follow-up-tasks/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteTask: async (id: string, version: number) => {
|
||||||
|
await client.delete(`/health/follow-up-tasks/${id}`, {
|
||||||
|
data: { version },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Records
|
||||||
|
listRecords: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
task_id?: string;
|
||||||
|
patient_id?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<FollowUpRecord>;
|
||||||
|
}>('/health/follow-up-records', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createRecord: async (taskId: string, req: Omit<CreateFollowUpRecordReq, 'task_id'>) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: FollowUpRecord;
|
||||||
|
}>(`/health/follow-up-tasks/${taskId}/records`, { ...req, task_id: taskId });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
240
apps/web/src/api/health/healthData.ts
Normal file
240
apps/web/src/api/health/healthData.ts
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface VitalSigns {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
record_date: string;
|
||||||
|
systolic_bp_morning?: number;
|
||||||
|
diastolic_bp_morning?: number;
|
||||||
|
systolic_bp_evening?: number;
|
||||||
|
diastolic_bp_evening?: number;
|
||||||
|
heart_rate?: number;
|
||||||
|
weight?: number;
|
||||||
|
blood_sugar?: number;
|
||||||
|
water_intake_ml?: number;
|
||||||
|
urine_output_ml?: number;
|
||||||
|
notes?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVitalSignsReq {
|
||||||
|
record_date: string;
|
||||||
|
systolic_bp_morning?: number;
|
||||||
|
diastolic_bp_morning?: number;
|
||||||
|
systolic_bp_evening?: number;
|
||||||
|
diastolic_bp_evening?: number;
|
||||||
|
heart_rate?: number;
|
||||||
|
weight?: number;
|
||||||
|
blood_sugar?: number;
|
||||||
|
water_intake_ml?: number;
|
||||||
|
urine_output_ml?: number;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LabReport {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
report_date: string;
|
||||||
|
report_type: string;
|
||||||
|
indicators?: Record<string, unknown>;
|
||||||
|
image_urls?: string[];
|
||||||
|
doctor_interpretation?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateLabReportReq {
|
||||||
|
report_date: string;
|
||||||
|
report_type: string;
|
||||||
|
indicators?: Record<string, unknown>;
|
||||||
|
image_urls?: string[];
|
||||||
|
doctor_interpretation?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HealthRecord {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
record_type: string;
|
||||||
|
record_date: string;
|
||||||
|
content?: string;
|
||||||
|
attachment_urls?: string[];
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateHealthRecordReq {
|
||||||
|
record_type: string;
|
||||||
|
record_date: string;
|
||||||
|
content?: string;
|
||||||
|
attachment_urls?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrendData {
|
||||||
|
id: string;
|
||||||
|
patient_id: string;
|
||||||
|
indicator: string;
|
||||||
|
trend_data: { date: string; value: number }[];
|
||||||
|
generated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const healthDataApi = {
|
||||||
|
// Vital Signs
|
||||||
|
listVitalSigns: async (
|
||||||
|
patientId: string,
|
||||||
|
params: { page?: number; page_size?: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<VitalSigns>;
|
||||||
|
}>(`/health/patients/${patientId}/vital-signs`, { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createVitalSigns: async (patientId: string, req: CreateVitalSignsReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: VitalSigns;
|
||||||
|
}>(`/health/patients/${patientId}/vital-signs`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateVitalSigns: async (
|
||||||
|
patientId: string,
|
||||||
|
id: string,
|
||||||
|
req: Partial<CreateVitalSignsReq> & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: VitalSigns;
|
||||||
|
}>(`/health/patients/${patientId}/vital-signs/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteVitalSigns: async (patientId: string, id: string) => {
|
||||||
|
await client.delete(`/health/patients/${patientId}/vital-signs/${id}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Lab Reports
|
||||||
|
listLabReports: async (
|
||||||
|
patientId: string,
|
||||||
|
params: { page?: number; page_size?: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<LabReport>;
|
||||||
|
}>(`/health/patients/${patientId}/lab-reports`, { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createLabReport: async (patientId: string, req: CreateLabReportReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: LabReport;
|
||||||
|
}>(`/health/patients/${patientId}/lab-reports`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateLabReport: async (
|
||||||
|
patientId: string,
|
||||||
|
id: string,
|
||||||
|
req: Partial<CreateLabReportReq> & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: LabReport;
|
||||||
|
}>(`/health/patients/${patientId}/lab-reports/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteLabReport: async (patientId: string, id: string) => {
|
||||||
|
await client.delete(`/health/patients/${patientId}/lab-reports/${id}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Health Records
|
||||||
|
listHealthRecords: async (
|
||||||
|
patientId: string,
|
||||||
|
params: { page?: number; page_size?: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<HealthRecord>;
|
||||||
|
}>(`/health/patients/${patientId}/health-records`, { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createHealthRecord: async (
|
||||||
|
patientId: string,
|
||||||
|
req: CreateHealthRecordReq,
|
||||||
|
) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: HealthRecord;
|
||||||
|
}>(`/health/patients/${patientId}/health-records`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateHealthRecord: async (
|
||||||
|
patientId: string,
|
||||||
|
id: string,
|
||||||
|
req: Partial<CreateHealthRecordReq> & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: HealthRecord;
|
||||||
|
}>(`/health/patients/${patientId}/health-records/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteHealthRecord: async (patientId: string, id: string) => {
|
||||||
|
await client.delete(`/health/patients/${patientId}/health-records/${id}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Trends
|
||||||
|
listTrends: async (patientId: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: TrendData[];
|
||||||
|
}>(`/health/patients/${patientId}/trends`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateTrend: async (patientId: string) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: TrendData[];
|
||||||
|
}>(`/health/patients/${patientId}/trends/generate`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getIndicatorTimeseries: async (patientId: string, indicator: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: { date: string; value: number }[];
|
||||||
|
}>(`/health/patients/${patientId}/trends/${encodeURIComponent(indicator)}`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Mini program endpoints
|
||||||
|
getMiniTrend: async (params: { indicator?: string; days?: number }) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: { date: string; value: number }[];
|
||||||
|
}>('/health/vital-signs/trend', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
getMiniToday: async () => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: VitalSigns | null;
|
||||||
|
}>('/health/vital-signs/today');
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
};
|
||||||
182
apps/web/src/api/health/patients.ts
Normal file
182
apps/web/src/api/health/patients.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import client from '../client';
|
||||||
|
import type { PaginatedResponse } from '../types';
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
export interface PatientListItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
gender?: string;
|
||||||
|
birth_date?: string;
|
||||||
|
blood_type?: string;
|
||||||
|
status: string;
|
||||||
|
verification_status: string;
|
||||||
|
source?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PatientDetail {
|
||||||
|
id: string;
|
||||||
|
user_id?: string;
|
||||||
|
name: string;
|
||||||
|
gender?: string;
|
||||||
|
birth_date?: string;
|
||||||
|
blood_type?: string;
|
||||||
|
id_number?: string;
|
||||||
|
allergy_history?: string;
|
||||||
|
medical_history_summary?: string;
|
||||||
|
emergency_contact_name?: string;
|
||||||
|
emergency_contact_phone?: string;
|
||||||
|
status: string;
|
||||||
|
verification_status: string;
|
||||||
|
source?: string;
|
||||||
|
notes?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePatientReq {
|
||||||
|
name: string;
|
||||||
|
gender?: string;
|
||||||
|
birth_date?: string;
|
||||||
|
blood_type?: string;
|
||||||
|
id_number?: string;
|
||||||
|
allergy_history?: string;
|
||||||
|
medical_history_summary?: string;
|
||||||
|
emergency_contact_name?: string;
|
||||||
|
emergency_contact_phone?: string;
|
||||||
|
source?: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdatePatientReq extends Partial<CreatePatientReq> {
|
||||||
|
status?: string;
|
||||||
|
verification_status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FamilyMember {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
relationship: string;
|
||||||
|
phone?: string;
|
||||||
|
id_number?: string;
|
||||||
|
notes?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateFamilyMemberReq {
|
||||||
|
name: string;
|
||||||
|
relationship: string;
|
||||||
|
phone?: string;
|
||||||
|
id_number?: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- API ---
|
||||||
|
export const patientApi = {
|
||||||
|
list: async (params: {
|
||||||
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
search?: string;
|
||||||
|
status?: string;
|
||||||
|
tag_id?: string;
|
||||||
|
}) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PaginatedResponse<PatientListItem>;
|
||||||
|
}>('/health/patients', { params });
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
get: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: PatientDetail;
|
||||||
|
}>(`/health/patients/${id}`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
create: async (req: CreatePatientReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: PatientDetail;
|
||||||
|
}>('/health/patients', req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
update: async (id: string, req: UpdatePatientReq & { version: number }) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: PatientDetail;
|
||||||
|
}>(`/health/patients/${id}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: async (id: string, version: number) => {
|
||||||
|
await client.delete(`/health/patients/${id}`, { data: { version } });
|
||||||
|
},
|
||||||
|
|
||||||
|
manageTags: async (id: string, tagIds: string[]) => {
|
||||||
|
await client.post(`/health/patients/${id}/tags`, { tag_ids: tagIds });
|
||||||
|
},
|
||||||
|
|
||||||
|
getHealthSummary: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: Record<string, unknown>;
|
||||||
|
}>(`/health/patients/${id}/health-summary`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
listFamilyMembers: async (id: string) => {
|
||||||
|
const { data } = await client.get<{
|
||||||
|
success: boolean;
|
||||||
|
data: FamilyMember[];
|
||||||
|
}>(`/health/patients/${id}/family-members`);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
createFamilyMember: async (id: string, req: CreateFamilyMemberReq) => {
|
||||||
|
const { data } = await client.post<{
|
||||||
|
success: boolean;
|
||||||
|
data: FamilyMember;
|
||||||
|
}>(`/health/patients/${id}/family-members`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateFamilyMember: async (
|
||||||
|
patientId: string,
|
||||||
|
memberId: string,
|
||||||
|
req: Partial<CreateFamilyMemberReq> & { version: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await client.put<{
|
||||||
|
success: boolean;
|
||||||
|
data: FamilyMember;
|
||||||
|
}>(`/health/patients/${patientId}/family-members/${memberId}`, req);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteFamilyMember: async (patientId: string, memberId: string) => {
|
||||||
|
await client.delete(
|
||||||
|
`/health/patients/${patientId}/family-members/${memberId}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
assignDoctor: async (
|
||||||
|
id: string,
|
||||||
|
doctorId: string,
|
||||||
|
relationshipType: string,
|
||||||
|
) => {
|
||||||
|
await client.post(`/health/patients/${id}/doctors`, {
|
||||||
|
doctor_id: doctorId,
|
||||||
|
relationship_type: relationshipType,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDoctor: async (id: string, doctorId: string) => {
|
||||||
|
await client.delete(`/health/patients/${id}/doctors/${doctorId}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user