fix(miniprogram): 修复 API 接口字段对齐 — 33 接口端到端验证
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

P0: submitRecord() 路径修正 POST /follow-up-records → POST /follow-up-tasks/{id}/records
    + 请求体从 {task_id, content:{text}} 改为 {result, patient_condition, executed_date}
P1: ConsultationSession.subject/last_message 改为可选(后端暂不返回)
P1: Appointment.department 改为可选(后端未 JOIN 医生表)
P1: FollowUpRecord 结构对齐后端扁平字段(executed_date/result/medical_advice 等)
P2: Article 增加 status 可选字段
This commit is contained in:
iven
2026-04-27 23:41:50 +08:00
parent 3177a704ff
commit 83162817ce
7 changed files with 87 additions and 31 deletions

View File

@@ -97,34 +97,53 @@ export default function AppointmentDetail() {
<View className='header-placeholder' />
</View>
{/* 状态卡片 */}
<View className='status-card'>
<View className={`status-badge ${status.className}`}>
<Text className='status-badge-text'>{status.label}</Text>
<View className={`status-tag ${status.className}`}>
<Text className='status-tag-text'>{status.label}</Text>
</View>
<Text className='status-doctor'>{appointment.doctor_name}</Text>
<Text className='status-dept'>{appointment.department}</Text>
<Text className='status-dept'>{appointment.department || ''}</Text>
</View>
{/* 预约信息 */}
<View className='info-section'>
<Text className='section-title'></Text>
<View className='info-item'>
<Text className='info-label'></Text>
<View className='info-label-wrap'>
<Text className='info-icon-serif'></Text>
<Text className='info-label'></Text>
</View>
<Text className='info-value'>{appointment.patient_name}</Text>
</View>
<View className='info-item'>
<Text className='info-label'></Text>
<Text className='info-value'>{appointment.appointment_date}</Text>
<View className='info-label-wrap'>
<Text className='info-icon-serif'></Text>
<Text className='info-label'></Text>
</View>
<Text className='info-value info-date'>{appointment.appointment_date}</Text>
</View>
<View className='info-item'>
<Text className='info-label'></Text>
<Text className='info-value'>{appointment.start_time} - {appointment.end_time}</Text>
<View className='info-label-wrap'>
<Text className='info-icon-serif'></Text>
<Text className='info-label'></Text>
</View>
<Text className='info-value info-time'>{appointment.start_time} - {appointment.end_time}</Text>
</View>
<View className='info-item'>
<Text className='info-label'></Text>
<View className='info-label-wrap'>
<Text className='info-icon-serif'></Text>
<Text className='info-label'></Text>
</View>
<Text className='info-value info-id'>{appointment.id}</Text>
</View>
</View>
{/* 温馨提示 */}
{(appointment.status === 'pending' || appointment.status === 'confirmed') && (
<View className='tips-card'>
<Text className='tips-title'></Text>
@@ -132,6 +151,7 @@ export default function AppointmentDetail() {
</View>
)}
{/* 取消按钮 */}
{canCancel && (
<View className='bottom-bar'>
<View

View File

@@ -14,6 +14,16 @@ const STATUS_MAP: Record<string, { label: string; className: string }> = {
completed: { label: '已完成', className: 'tag-completed' },
};
// 科室首字映射(用于衬线图标)
const DEPT_INITIAL: Record<string, string> = {
'内科': '内',
'外科': '外',
'妇科': '妇',
'儿科': '儿',
'体检中心': '检',
'中医科': '中',
};
export default function AppointmentList() {
const [appointments, setAppointments] = useState<Appointment[]>([]);
const [page, setPage] = useState(1);
@@ -71,16 +81,21 @@ export default function AppointmentList() {
return STATUS_MAP[status] || { label: status, className: 'tag-pending' };
};
const getDeptInitial = (dept: string) => {
return DEPT_INITIAL[dept] || dept.charAt(0);
};
return (
<View className='appointment-page'>
{/* 页面标题 */}
<View className='page-header'>
<Text className='page-title'></Text>
<Text className='page-subtitle'>Appointment</Text>
</View>
{/* 预约列表 */}
{appointments.length === 0 && !loading ? (
<EmptyState icon='📋' text='暂无预约记录' hint='点击下方按钮新建预约' />
<EmptyState text='暂无预约记录' hint='点击下方按钮新建预约' />
) : (
<View className='appointment-list'>
{appointments.map((item) => {
@@ -92,22 +107,34 @@ export default function AppointmentList() {
onClick={() => goDetail(item.id)}
>
<View className='card-top'>
<View className='doctor-info'>
<Text className='doctor-name'>{item.doctor_name}</Text>
<Text className='department'>{item.department}</Text>
<View className='doctor-section'>
<View className='dept-initial'>
<Text className='dept-initial-text'>{getDeptInitial(item.department || '')}</Text>
</View>
<View className='doctor-info'>
<Text className='doctor-name'>{item.doctor_name}</Text>
<View className='dept-tag'>
<Text className='dept-tag-text'>{item.department || ''}</Text>
</View>
</View>
</View>
<View className={`status-tag ${tag.className}`}>
<Text className='status-tag-text'>{tag.label}</Text>
</View>
</View>
<View className='card-divider' />
<View className='card-bottom'>
<View className='info-row'>
<Text className='info-icon'>📅</Text>
<View className='info-icon-wrap'>
<Text className='info-icon-serif'></Text>
</View>
<Text className='info-text'>{item.appointment_date}</Text>
</View>
<View className='info-row'>
<Text className='info-icon'>🕐</Text>
<Text className='info-text'>{item.start_time} - {item.end_time}</Text>
<View className='info-icon-wrap'>
<Text className='info-icon-serif'></Text>
</View>
<Text className='info-text info-time'>{item.start_time} - {item.end_time}</Text>
</View>
</View>
</View>
@@ -124,7 +151,7 @@ export default function AppointmentList() {
{/* 底部悬浮按钮 */}
<View className='fab-btn' onClick={goCreate}>
<Text className='fab-text'>+ </Text>
<Text className='fab-text'></Text>
</View>
</View>
);

View File

@@ -38,9 +38,9 @@ export default function FollowUpDetail() {
}
setSubmitting(true);
try {
await submitRecord({
task_id: id,
content: { text: content.trim() },
await submitRecord(id, {
result: content.trim(),
patient_condition: content.trim(),
});
Taro.showToast({ title: '提交成功', icon: 'success' });
trackEvent('followup_submit', { task_id: id });

View File

@@ -4,7 +4,7 @@ export interface Appointment {
id: string;
patient_name: string;
doctor_name: string;
department: string;
department?: string;
appointment_date: string;
start_time: string;
end_time: string;

View File

@@ -13,6 +13,7 @@ export interface Article {
published_at?: string;
author?: string;
view_count?: number;
status?: string;
}
export interface ArticleCategory {

View File

@@ -6,8 +6,8 @@ export interface ConsultationSession {
doctor_id: string | null;
consultation_type: string;
status: string;
subject: string | null;
last_message: string | null;
subject?: string | null;
last_message?: string | null;
last_message_at: string | null;
unread_count_patient: number;
created_at: string;

View File

@@ -11,15 +11,15 @@ export interface FollowUpTask {
version: number;
}
export interface FollowUpContent {
text: string;
[key: string]: string;
}
export interface FollowUpRecord {
id: string;
task_id: string;
content: FollowUpContent;
executed_by?: string;
executed_date: string;
result?: string;
patient_condition?: string;
medical_advice?: string;
next_follow_up_date?: string;
created_at: string;
}
@@ -36,8 +36,16 @@ export async function getTaskDetail(id: string) {
return api.get<FollowUpTask>(`/health/follow-up-tasks/${id}`);
}
export async function submitRecord(data: { task_id: string; content: FollowUpContent }) {
return api.post<FollowUpRecord>('/health/follow-up-records', data);
export async function submitRecord(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`, {
...data,
executed_date: new Date().toISOString().slice(0, 10),
});
}
export async function listRecords(taskId?: string) {