feat(miniprogram): 实现小程序透析模块 — 患者端查看 + 医护端录入/审阅
审计后续 H1: 补齐小程序端透析功能,对接后端 12 个 API 路由。 新增内容: - 患者端: 透析记录列表/详情 + 透析处方列表/详情(只读,4 页面) - 医护端: 透析记录列表/详情/创建 + 处方列表/详情/创建(6 页面) - Service 层: dialysis.ts(患者端只读)+ doctor/dialysis.ts(医护端 CRUD) - 集成入口: 医生工作台快捷操作 + 患者"我的"菜单 + 路由注册 - 基础设施: api.delete 扩展支持 data 参数(后端 delete 需要 version)
This commit is contained in:
92
apps/miniprogram/src/services/dialysis.ts
Normal file
92
apps/miniprogram/src/services/dialysis.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { api } from './request';
|
||||
|
||||
// ── Types ─────────────────────────────────────────────
|
||||
|
||||
export interface DialysisRecord {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
dialysis_date: string;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
dry_weight?: number;
|
||||
pre_weight?: number;
|
||||
post_weight?: number;
|
||||
pre_bp_systolic?: number;
|
||||
pre_bp_diastolic?: number;
|
||||
post_bp_systolic?: number;
|
||||
post_bp_diastolic?: number;
|
||||
pre_heart_rate?: number;
|
||||
post_heart_rate?: number;
|
||||
ultrafiltration_volume?: number;
|
||||
dialysis_duration?: number;
|
||||
blood_flow_rate?: number;
|
||||
dialysis_type: string;
|
||||
symptoms?: Record<string, unknown>;
|
||||
complication_notes?: string;
|
||||
status: string;
|
||||
reviewed_by?: string;
|
||||
reviewed_at?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface DialysisPrescription {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
dialyzer_model?: string;
|
||||
membrane_area?: number;
|
||||
dialysate_potassium?: number;
|
||||
dialysate_calcium?: number;
|
||||
dialysate_bicarbonate?: number;
|
||||
anticoagulation_type?: string;
|
||||
anticoagulation_dose?: string;
|
||||
target_ultrafiltration_ml?: number;
|
||||
target_dry_weight?: number;
|
||||
blood_flow_rate?: number;
|
||||
dialysate_flow_rate?: number;
|
||||
frequency_per_week?: number;
|
||||
duration_minutes?: number;
|
||||
vascular_access_type?: string;
|
||||
vascular_access_location?: string;
|
||||
effective_from?: string;
|
||||
effective_to?: string;
|
||||
status: string;
|
||||
prescribed_by?: string;
|
||||
notes?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
// ── Patient-facing API (read-only) ────────────────────
|
||||
|
||||
export async function listDialysisRecords(
|
||||
patientId: string,
|
||||
params?: { page?: number; page_size?: number },
|
||||
) {
|
||||
return api.get<{ data: DialysisRecord[]; total: number }>(
|
||||
`/health/patients/${patientId}/dialysis-records`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getDialysisRecord(id: string) {
|
||||
return api.get<DialysisRecord>(`/health/dialysis-records/${id}`);
|
||||
}
|
||||
|
||||
export async function listDialysisPrescriptions(params?: {
|
||||
patient_id?: string;
|
||||
status?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) {
|
||||
return api.get<{ data: DialysisPrescription[]; total: number }>(
|
||||
'/health/dialysis-prescriptions',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getDialysisPrescription(id: string) {
|
||||
return api.get<DialysisPrescription>(`/health/dialysis-prescriptions/${id}`);
|
||||
}
|
||||
@@ -1,343 +1,11 @@
|
||||
import { api } from './request';
|
||||
// doctor.ts — 统一 re-export 入口,保持 `import * as doctorApi` 兼容
|
||||
// 各领域实现已拆分到 ./doctor/ 子目录
|
||||
|
||||
// ── Dashboard ──────────────────────────────────────
|
||||
|
||||
export interface DoctorDashboard {
|
||||
total_patients: number;
|
||||
active_sessions: number;
|
||||
unread_messages: number;
|
||||
pending_follow_ups: number;
|
||||
today_consultations: number;
|
||||
pending_lab_review: number;
|
||||
today_appointments: number;
|
||||
}
|
||||
|
||||
export async function getDashboard() {
|
||||
return api.get<DoctorDashboard>('/health/doctor/dashboard');
|
||||
}
|
||||
|
||||
// ── Patient (doctor view) ──────────────────────────
|
||||
|
||||
export interface PatientItem {
|
||||
id: string;
|
||||
name: string;
|
||||
gender?: string;
|
||||
birth_date?: string;
|
||||
phone?: string;
|
||||
status?: string;
|
||||
tags?: { id: string; name: string; color?: string }[];
|
||||
last_visit_date?: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface PatientDetail extends PatientItem {
|
||||
blood_type?: string;
|
||||
allergy_history?: string;
|
||||
medical_history_summary?: string;
|
||||
emergency_contact_name?: string;
|
||||
emergency_contact_phone?: string;
|
||||
source?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface HealthSummary {
|
||||
patient_id: string;
|
||||
latest_vital_signs?: {
|
||||
record_date: string;
|
||||
systolic_bp?: number;
|
||||
diastolic_bp?: number;
|
||||
heart_rate?: number;
|
||||
weight?: number;
|
||||
blood_sugar?: number;
|
||||
} | null;
|
||||
latest_lab_report?: {
|
||||
id: string;
|
||||
report_date: string;
|
||||
report_type: string;
|
||||
abnormal_count?: number;
|
||||
} | null;
|
||||
pending_follow_ups?: number;
|
||||
upcoming_appointments?: number;
|
||||
}
|
||||
|
||||
export interface PatientTag {
|
||||
id: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
is_system?: boolean;
|
||||
}
|
||||
|
||||
export async function listPatients(params?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
search?: string;
|
||||
tag_id?: string;
|
||||
}) {
|
||||
return api.get<{ data: PatientItem[]; total: number }>('/health/patients', params);
|
||||
}
|
||||
|
||||
export async function getPatient(id: string) {
|
||||
return api.get<PatientDetail>(`/health/patients/${id}`);
|
||||
}
|
||||
|
||||
export async function getHealthSummary(patientId: string) {
|
||||
return api.get<HealthSummary>(`/health/patients/${patientId}/health-summary`);
|
||||
}
|
||||
|
||||
export async function listPatientTags() {
|
||||
return api.get<{ data: PatientTag[]; total: number }>('/health/patient-tags');
|
||||
}
|
||||
|
||||
// ── Consultation (doctor view) ─────────────────────
|
||||
|
||||
export interface ConsultationSession {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
patient_name?: string;
|
||||
doctor_id: string | null;
|
||||
consultation_type: string;
|
||||
status: string;
|
||||
subject: string | null;
|
||||
last_message: string | null;
|
||||
last_message_at: string | null;
|
||||
unread_count_doctor?: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ConsultationMessage {
|
||||
id: string;
|
||||
session_id: string;
|
||||
sender_id: string;
|
||||
sender_role: string;
|
||||
content_type: string;
|
||||
content: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export async function listSessions(params?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
status?: string;
|
||||
}) {
|
||||
return api.get<{ data: ConsultationSession[]; total: number }>(
|
||||
'/health/consultation-sessions',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getSession(id: string) {
|
||||
return api.get<ConsultationSession>(`/health/consultation-sessions/${id}`);
|
||||
}
|
||||
|
||||
export async function listMessages(sessionId: string, params?: { page?: number; page_size?: number; after_id?: string }) {
|
||||
return api.get<{ data: ConsultationMessage[]; total: number }>(
|
||||
`/health/consultation-sessions/${sessionId}/messages`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function sendMessage(sessionId: string, content: string, contentType = 'text') {
|
||||
return api.post<ConsultationMessage>('/health/consultation-messages', {
|
||||
session_id: sessionId,
|
||||
content_type: contentType,
|
||||
content,
|
||||
});
|
||||
}
|
||||
|
||||
export async function markSessionRead(sessionId: string) {
|
||||
return api.put<void>(`/health/consultation-sessions/${sessionId}/read`);
|
||||
}
|
||||
|
||||
export async function closeSession(sessionId: string) {
|
||||
return api.put<void>(`/health/consultation-sessions/${sessionId}/close`);
|
||||
}
|
||||
|
||||
// ── Follow-up (doctor view) ────────────────────────
|
||||
|
||||
export interface FollowUpTask {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
patient_name?: string;
|
||||
assigned_to?: string;
|
||||
follow_up_type: string;
|
||||
planned_date: string;
|
||||
content_template?: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export async function listFollowUpTasks(params?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
status?: string;
|
||||
patient_id?: string;
|
||||
}) {
|
||||
return api.get<{ data: FollowUpTask[]; total: number }>('/health/follow-up-tasks', params);
|
||||
}
|
||||
|
||||
export async function getFollowUpTask(id: string) {
|
||||
return api.get<FollowUpTask>(`/health/follow-up-tasks/${id}`);
|
||||
}
|
||||
|
||||
export async function updateFollowUpTask(id: string, data: Record<string, unknown>, version: number) {
|
||||
return api.put<FollowUpTask>(`/health/follow-up-tasks/${id}`, { ...data, version });
|
||||
}
|
||||
|
||||
export async function createFollowUpRecord(taskId: string, data: {
|
||||
result?: string;
|
||||
patient_condition?: string;
|
||||
medical_advice?: string;
|
||||
next_follow_up_date?: string;
|
||||
}) {
|
||||
return api.post<FollowUpRecord>(`/health/follow-up-tasks/${taskId}/records`, { task_id: taskId, ...data });
|
||||
}
|
||||
|
||||
export async function listFollowUpRecords(params?: { task_id?: string; page?: number }) {
|
||||
return api.get<{ data: FollowUpRecord[]; total: number }>('/health/follow-up-records', params);
|
||||
}
|
||||
|
||||
// ── Lab Report (doctor view) ───────────────────────
|
||||
|
||||
export interface LabReportItem {
|
||||
id: string;
|
||||
report_date: string;
|
||||
report_type: string;
|
||||
status: string;
|
||||
abnormal_count?: number;
|
||||
reviewed_by?: string;
|
||||
reviewed_at?: string;
|
||||
doctor_notes?: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export interface LabReportDetail extends LabReportItem {
|
||||
items?: {
|
||||
name: string;
|
||||
value: number;
|
||||
unit?: string;
|
||||
reference_min?: number;
|
||||
reference_max?: number;
|
||||
is_abnormal?: boolean;
|
||||
}[];
|
||||
image_urls?: string[];
|
||||
}
|
||||
|
||||
export async function listLabReports(patientId: string, params?: { page?: number; page_size?: number }) {
|
||||
return api.get<{ data: LabReportItem[]; total: number }>(
|
||||
`/health/patients/${patientId}/lab-reports`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getLabReport(patientId: string, reportId: string) {
|
||||
return api.get<LabReportDetail>(`/health/patients/${patientId}/lab-reports/${reportId}`);
|
||||
}
|
||||
|
||||
export async function reviewLabReport(
|
||||
patientId: string,
|
||||
reportId: string,
|
||||
data: { doctor_notes?: string; version: number },
|
||||
) {
|
||||
return api.put<LabReportDetail>(
|
||||
`/health/patients/${patientId}/lab-reports/${reportId}/review`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
// ── Appointments (doctor view) ─────────────────────
|
||||
|
||||
export async function listAppointments(params?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
status?: string;
|
||||
date?: string;
|
||||
}) {
|
||||
return api.get<{ data: any[]; total: number }>('/health/appointments', params);
|
||||
}
|
||||
|
||||
// ── Statistics ─────────────────────────────────────
|
||||
|
||||
export interface PatientStats {
|
||||
total_patients: number;
|
||||
new_this_month: number;
|
||||
new_this_week: number;
|
||||
active_this_month: number;
|
||||
}
|
||||
|
||||
export interface ConsultationStats {
|
||||
total_sessions: number;
|
||||
pending_reply: number;
|
||||
avg_response_time_minutes?: number | null;
|
||||
this_month: number;
|
||||
}
|
||||
|
||||
export interface FollowUpStats {
|
||||
total_tasks: number;
|
||||
completed: number;
|
||||
pending: number;
|
||||
overdue: number;
|
||||
completion_rate?: number;
|
||||
}
|
||||
|
||||
export async function getPatientStats() {
|
||||
return api.get<PatientStats>('/health/admin/statistics/patients');
|
||||
}
|
||||
|
||||
export async function getConsultationStats() {
|
||||
return api.get<ConsultationStats>('/health/admin/statistics/consultations');
|
||||
}
|
||||
|
||||
export async function getFollowUpStats() {
|
||||
return api.get<FollowUpStats>('/health/admin/statistics/follow-ups');
|
||||
}
|
||||
|
||||
// ── Alerts (doctor view) ────────────────────────────
|
||||
|
||||
export interface Alert {
|
||||
id: string;
|
||||
patient_id: string;
|
||||
rule_id: string;
|
||||
severity: string;
|
||||
title: string;
|
||||
detail?: Record<string, unknown>;
|
||||
status: string;
|
||||
acknowledged_by?: string;
|
||||
acknowledged_at?: string;
|
||||
resolved_at?: string;
|
||||
created_at: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
export async function listAlerts(params?: {
|
||||
patient_id?: string;
|
||||
status?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) {
|
||||
return api.get<{ data: Alert[]; total: number }>('/health/alerts', params);
|
||||
}
|
||||
|
||||
export async function acknowledgeAlert(id: string, version: number) {
|
||||
return api.put<Alert>(`/health/alerts/${id}/acknowledge`, { version });
|
||||
}
|
||||
|
||||
export async function dismissAlert(id: string, version: number) {
|
||||
return api.put<Alert>(`/health/alerts/${id}/dismiss`, { version });
|
||||
}
|
||||
|
||||
export async function resolveAlert(id: string, version: number) {
|
||||
return api.put<Alert>(`/health/alerts/${id}/resolve`, { version });
|
||||
}
|
||||
export * from './doctor/dashboard';
|
||||
export * from './doctor/patient';
|
||||
export * from './doctor/consultation';
|
||||
export * from './doctor/followup';
|
||||
export * from './doctor/labReport';
|
||||
export * from './doctor/alerts';
|
||||
export * from './doctor/appointment';
|
||||
export * from './doctor/dialysis';
|
||||
|
||||
141
apps/miniprogram/src/services/doctor/dialysis.ts
Normal file
141
apps/miniprogram/src/services/doctor/dialysis.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { api } from '../request';
|
||||
import type { DialysisRecord, DialysisPrescription } from '../dialysis';
|
||||
|
||||
// Re-export types for convenience
|
||||
export type { DialysisRecord, DialysisPrescription };
|
||||
|
||||
// ── Request types ─────────────────────────────────────
|
||||
|
||||
export interface CreateDialysisRecordReq {
|
||||
patient_id: string;
|
||||
dialysis_date: string;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
dry_weight?: number;
|
||||
pre_weight?: number;
|
||||
post_weight?: number;
|
||||
pre_bp_systolic?: number;
|
||||
pre_bp_diastolic?: number;
|
||||
post_bp_systolic?: number;
|
||||
post_bp_diastolic?: number;
|
||||
pre_heart_rate?: number;
|
||||
post_heart_rate?: number;
|
||||
ultrafiltration_volume?: number;
|
||||
dialysis_duration?: number;
|
||||
blood_flow_rate?: number;
|
||||
dialysis_type?: string;
|
||||
symptoms?: Record<string, unknown>;
|
||||
complication_notes?: string;
|
||||
}
|
||||
|
||||
export type UpdateDialysisRecordReq = Omit<CreateDialysisRecordReq, 'patient_id'>;
|
||||
|
||||
export interface CreateDialysisPrescriptionReq {
|
||||
patient_id: string;
|
||||
dialyzer_model?: string;
|
||||
membrane_area?: number;
|
||||
dialysate_potassium?: number;
|
||||
dialysate_calcium?: number;
|
||||
dialysate_bicarbonate?: number;
|
||||
anticoagulation_type?: string;
|
||||
anticoagulation_dose?: string;
|
||||
target_ultrafiltration_ml?: number;
|
||||
target_dry_weight?: number;
|
||||
blood_flow_rate?: number;
|
||||
dialysate_flow_rate?: number;
|
||||
frequency_per_week?: number;
|
||||
duration_minutes?: number;
|
||||
vascular_access_type?: string;
|
||||
vascular_access_location?: string;
|
||||
effective_from?: string;
|
||||
effective_to?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export type UpdateDialysisPrescriptionReq = Omit<CreateDialysisPrescriptionReq, 'patient_id'>;
|
||||
|
||||
export interface DialysisStatistics {
|
||||
total_records: number;
|
||||
this_month: number;
|
||||
type_distribution: Array<{ name: string; value: number }>;
|
||||
complication_rate: number;
|
||||
avg_ultrafiltration?: number;
|
||||
avg_duration?: number;
|
||||
pending_review: number;
|
||||
}
|
||||
|
||||
// ── Dialysis Records CRUD ─────────────────────────────
|
||||
|
||||
export async function listDialysisRecords(
|
||||
patientId: string,
|
||||
params?: { page?: number; page_size?: number },
|
||||
) {
|
||||
return api.get<{ data: DialysisRecord[]; total: number }>(
|
||||
`/health/patients/${patientId}/dialysis-records`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getDialysisRecord(id: string) {
|
||||
return api.get<DialysisRecord>(`/health/dialysis-records/${id}`);
|
||||
}
|
||||
|
||||
export async function createDialysisRecord(data: CreateDialysisRecordReq) {
|
||||
return api.post<DialysisRecord>('/health/dialysis-records', data);
|
||||
}
|
||||
|
||||
export async function updateDialysisRecord(
|
||||
id: string,
|
||||
data: UpdateDialysisRecordReq,
|
||||
version: number,
|
||||
) {
|
||||
return api.put<DialysisRecord>(`/health/dialysis-records/${id}`, { ...data, version });
|
||||
}
|
||||
|
||||
export async function deleteDialysisRecord(id: string, version: number) {
|
||||
return api.delete<void>(`/health/dialysis-records/${id}`, { version });
|
||||
}
|
||||
|
||||
export async function reviewDialysisRecord(id: string, version: number) {
|
||||
return api.put<DialysisRecord>(`/health/dialysis-records/${id}/review`, { version });
|
||||
}
|
||||
|
||||
// ── Dialysis Prescriptions CRUD ───────────────────────
|
||||
|
||||
export async function listDialysisPrescriptions(params?: {
|
||||
patient_id?: string;
|
||||
status?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}) {
|
||||
return api.get<{ data: DialysisPrescription[]; total: number }>(
|
||||
'/health/dialysis-prescriptions',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
export async function getDialysisPrescription(id: string) {
|
||||
return api.get<DialysisPrescription>(`/health/dialysis-prescriptions/${id}`);
|
||||
}
|
||||
|
||||
export async function createDialysisPrescription(data: CreateDialysisPrescriptionReq) {
|
||||
return api.post<DialysisPrescription>('/health/dialysis-prescriptions', data);
|
||||
}
|
||||
|
||||
export async function updateDialysisPrescription(
|
||||
id: string,
|
||||
data: UpdateDialysisPrescriptionReq,
|
||||
version: number,
|
||||
) {
|
||||
return api.put<DialysisPrescription>(`/health/dialysis-prescriptions/${id}`, { ...data, version });
|
||||
}
|
||||
|
||||
export async function deleteDialysisPrescription(id: string, version: number) {
|
||||
return api.delete<void>(`/health/dialysis-prescriptions/${id}`, { version });
|
||||
}
|
||||
|
||||
// ── Statistics ────────────────────────────────────────
|
||||
|
||||
export async function getDialysisStats() {
|
||||
return api.get<DialysisStatistics>('/health/admin/statistics/dialysis');
|
||||
}
|
||||
@@ -141,5 +141,5 @@ export const api = {
|
||||
|
||||
post: <T>(path: string, data?: unknown) => request<T>('POST', path, data),
|
||||
put: <T>(path: string, data?: unknown) => request<T>('PUT', path, data),
|
||||
delete: <T>(path: string) => request<T>('DELETE', path),
|
||||
delete: <T>(path: string, data?: unknown) => request<T>('DELETE', path, data),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user