From ede4a8d29e8a001473a6499e25a6cebef91620ce Mon Sep 17 00:00:00 2001 From: MingNian <1281442923@qq.com> Date: Sun, 24 May 2026 13:24:21 +0800 Subject: [PATCH] fix: audit issues - field mismatches, missing endpoints, data loss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Report frontends: createdAt→uploadedAt field alignment with backend - Dashboard: fix pending reports endpoint /api/reports/pending - FollowUpListPage: status labels upcoming/cancelled - MedicationController: add PUT/DELETE endpoints + service methods - FollowUpController: add DELETE endpoint, Notes to CreateRequest - Auth: UpdateProfileRequest includes doctor fields - Auth: login restores soft-deleted users instead of crashing --- .../DTOs/AuthDtos.cs | 3 +- .../Services/FollowUpService.cs | 12 ++++++- .../Services/MedicationService.cs | 30 +++++++++++++++++ .../Controllers/AuthController.cs | 32 +++++++++++++++---- .../Controllers/FollowUpController.cs | 12 ++++++- .../Controllers/MedicationController.cs | 23 +++++++++++++ .../src/pages/dashboard/DashboardPage.tsx | 2 +- .../src/pages/followups/FollowUpListPage.tsx | 4 +-- .../src/pages/reports/ReportDetailPage.tsx | 4 +-- .../src/pages/reports/ReportListPage.tsx | 4 +-- .../src/services/report.service.ts | 4 +-- 11 files changed, 111 insertions(+), 19 deletions(-) diff --git a/backend/src/HealthManager.Application/DTOs/AuthDtos.cs b/backend/src/HealthManager.Application/DTOs/AuthDtos.cs index a2f3db2..a018bab 100644 --- a/backend/src/HealthManager.Application/DTOs/AuthDtos.cs +++ b/backend/src/HealthManager.Application/DTOs/AuthDtos.cs @@ -16,4 +16,5 @@ public record UserProfileResponse( public record UpdateProfileRequest( string? Name, string? Gender, DateOnly? Birthday, - decimal? HeightCm, decimal? WeightKg, List? MedicalHistory); + decimal? HeightCm, decimal? WeightKg, List? MedicalHistory, + string? Department, string? Title, string? Introduction, List? Specialty); diff --git a/backend/src/HealthManager.Application/Services/FollowUpService.cs b/backend/src/HealthManager.Application/Services/FollowUpService.cs index 96d96bd..d2fb021 100644 --- a/backend/src/HealthManager.Application/Services/FollowUpService.cs +++ b/backend/src/HealthManager.Application/Services/FollowUpService.cs @@ -20,7 +20,7 @@ public class FollowUpService(AppDbContext db) .OrderBy(f => f.ScheduledAt) .ToListAsync(); - public async Task AddAsync(Guid patientId, string title, string? description, DateTime scheduledAt, bool reminderEnabled, Guid? doctorId = null) + public async Task AddAsync(Guid patientId, string title, string? description, DateTime scheduledAt, bool reminderEnabled, Guid? doctorId = null, string? notes = null) { var followUp = new FollowUp { @@ -30,6 +30,7 @@ public class FollowUpService(AppDbContext db) Description = description, ScheduledAt = DateTime.SpecifyKind(scheduledAt, DateTimeKind.Utc), ReminderEnabled = reminderEnabled, + Notes = notes, }; db.FollowUps.Add(followUp); await db.SaveChangesAsync(); @@ -59,4 +60,13 @@ public class FollowUpService(AppDbContext db) await db.SaveChangesAsync(); return followUp; } + + public async Task DeleteAsync(Guid id) + { + var followUp = await db.FollowUps.FindAsync(id); + if (followUp == null) return false; + db.FollowUps.Remove(followUp); + await db.SaveChangesAsync(); + return true; + } } diff --git a/backend/src/HealthManager.Application/Services/MedicationService.cs b/backend/src/HealthManager.Application/Services/MedicationService.cs index f062fb8..d69f2af 100644 --- a/backend/src/HealthManager.Application/Services/MedicationService.cs +++ b/backend/src/HealthManager.Application/Services/MedicationService.cs @@ -90,4 +90,34 @@ public class MedicationService(AppDbContext db) return totalCount > 0 ? Math.Round((decimal)takenCount / totalCount * 100, 1) : 0; } + + public async Task UpdateAsync(Guid medicationId, Guid userId, string? drugName, + string? dosage, string? frequency, List? timeSlots, + DateOnly? startDate, DateOnly? endDate, string? notes, string? status) + { + var med = await db.Medications.FindAsync(medicationId); + if (med == null || med.UserId != userId) return null; + + if (drugName != null) med.DrugName = drugName; + if (dosage != null) med.Dosage = dosage; + if (frequency != null) med.Frequency = frequency; + if (timeSlots != null) med.TimeSlots = timeSlots; + if (startDate.HasValue) med.StartDate = startDate.Value; + if (endDate.HasValue) med.EndDate = endDate; + if (notes != null) med.Notes = notes; + if (status != null) med.Status = status; + med.UpdatedAt = DateTime.UtcNow; + + await db.SaveChangesAsync(); + return med; + } + + public async Task DeleteAsync(Guid medicationId, Guid userId) + { + var med = await db.Medications.FindAsync(medicationId); + if (med == null || med.UserId != userId) return false; + db.Medications.Remove(med); + await db.SaveChangesAsync(); + return true; + } } diff --git a/backend/src/HealthManager.WebApi/Controllers/AuthController.cs b/backend/src/HealthManager.WebApi/Controllers/AuthController.cs index 6a9998c..bb3ef7b 100644 --- a/backend/src/HealthManager.WebApi/Controllers/AuthController.cs +++ b/backend/src/HealthManager.WebApi/Controllers/AuthController.cs @@ -29,14 +29,28 @@ public class AuthController( { // Demo: auto-register new users var db = HttpContext.RequestServices.GetRequiredService(); - user = new User + + // Check if this phone was soft-deleted — restore instead of creating duplicate + var deleted = await db.Users.IgnoreQueryFilters() + .FirstOrDefaultAsync(u => u.Phone == request.Phone && u.IsDeleted); + if (deleted != null) { - Phone = request.Phone, - Name = "用户" + request.Phone[^4..], - Role = "patient", - PasswordHash = AuthService.HashPassword("demo123"), - }; - db.Users.Add(user); + deleted.IsDeleted = false; + deleted.DeletedAt = null; + deleted.UpdatedAt = DateTime.UtcNow; + user = deleted; + } + else + { + user = new User + { + Phone = request.Phone, + Name = "用户" + request.Phone[^4..], + Role = "patient", + PasswordHash = AuthService.HashPassword("demo123"), + }; + db.Users.Add(user); + } await db.SaveChangesAsync(); } @@ -121,6 +135,10 @@ public class AuthController( if (request.HeightCm.HasValue) user.HeightCm = request.HeightCm; if (request.WeightKg.HasValue) user.WeightKg = request.WeightKg; if (request.MedicalHistory != null) user.MedicalHistory = request.MedicalHistory; + if (request.Department != null) user.Department = request.Department; + if (request.Title != null) user.Title = request.Title; + if (request.Introduction != null) user.Introduction = request.Introduction; + if (request.Specialty != null) user.Specialty = request.Specialty; user.UpdatedAt = DateTime.UtcNow; await db.SaveChangesAsync(); diff --git a/backend/src/HealthManager.WebApi/Controllers/FollowUpController.cs b/backend/src/HealthManager.WebApi/Controllers/FollowUpController.cs index f19d42a..c35cd5f 100644 --- a/backend/src/HealthManager.WebApi/Controllers/FollowUpController.cs +++ b/backend/src/HealthManager.WebApi/Controllers/FollowUpController.cs @@ -55,10 +55,19 @@ public class FollowUpController(FollowUpService followUpService) : ControllerBas } var followUp = await followUpService.AddAsync(patientId, request.Title, request.Description, - request.ScheduledAt, request.ReminderEnabled, doctorId); + request.ScheduledAt, request.ReminderEnabled, doctorId, request.Notes); return Ok(new { followUp.Id, followUp.Title, followUp.Status }); } + [HttpDelete("{id:guid}")] + [Authorize(Roles = "doctor")] + public async Task DeleteFollowUp(Guid id) + { + var ok = await followUpService.DeleteAsync(id); + if (!ok) return NotFound(new { message = "复查不存在" }); + return Ok(new { message = "删除成功" }); + } + [HttpPut("{id:guid}")] [Authorize(Roles = "doctor")] public async Task UpdateFollowUp(Guid id, [FromBody] FollowUpUpdateRequest request) @@ -77,6 +86,7 @@ public class FollowUpCreateRequest public DateTime ScheduledAt { get; set; } public bool ReminderEnabled { get; set; } = true; public Guid? PatientId { get; set; } + public string? Notes { get; set; } } public class FollowUpUpdateRequest diff --git a/backend/src/HealthManager.WebApi/Controllers/MedicationController.cs b/backend/src/HealthManager.WebApi/Controllers/MedicationController.cs index d451f86..a926e39 100644 --- a/backend/src/HealthManager.WebApi/Controllers/MedicationController.cs +++ b/backend/src/HealthManager.WebApi/Controllers/MedicationController.cs @@ -73,8 +73,31 @@ public class MedicationController(MedicationService medicationService) : Control } } + [HttpPut("{id:guid}")] + public async Task UpdateMedication(Guid id, [FromBody] MedicationUpdateRequest request) + { + var med = await medicationService.UpdateAsync(id, UserId, request.DrugName, request.Dosage, + request.Frequency, request.TimeSlots, request.StartDate, request.EndDate, request.Notes, request.Status); + if (med == null) return NotFound(new { message = "药品不存在" }); + return Ok(new { med.Id, med.DrugName, med.Dosage, med.Status }); + } + + [HttpDelete("{id:guid}")] + public async Task DeleteMedication(Guid id) + { + var ok = await medicationService.DeleteAsync(id, UserId); + if (!ok) return NotFound(new { message = "药品不存在" }); + return Ok(new { message = "删除成功" }); + } +} + public record MedicationCreateRequest( string DrugName, string Dosage, string Frequency, List TimeSlots, DateOnly StartDate, DateOnly? EndDate, string? Notes); +public record MedicationUpdateRequest( + string? DrugName, string? Dosage, string? Frequency, + List? TimeSlots, DateOnly? StartDate, DateOnly? EndDate, + string? Notes, string? Status); + public record MarkTakenRequest(string TimeSlot); diff --git a/frontend-doctor/src/pages/dashboard/DashboardPage.tsx b/frontend-doctor/src/pages/dashboard/DashboardPage.tsx index ea09f82..8fd8c36 100644 --- a/frontend-doctor/src/pages/dashboard/DashboardPage.tsx +++ b/frontend-doctor/src/pages/dashboard/DashboardPage.tsx @@ -57,7 +57,7 @@ export function DashboardPage() { const [patients, consultations, reports, followUps] = await Promise.all([ api.get('/api/patients'), api.get('/api/consultations'), - api.get('/api/reports?status=pending'), + api.get('/api/reports/pending'), api.get('/api/follow-ups'), ]); setStats({ diff --git a/frontend-doctor/src/pages/followups/FollowUpListPage.tsx b/frontend-doctor/src/pages/followups/FollowUpListPage.tsx index 9dd40ac..9330063 100644 --- a/frontend-doctor/src/pages/followups/FollowUpListPage.tsx +++ b/frontend-doctor/src/pages/followups/FollowUpListPage.tsx @@ -16,9 +16,9 @@ export function FollowUpListPage() { const statusLabel = (s: string) => { switch (s) { - case 'pending': return { text: '待随访', color: '#F59E0B', bg: '#FFF8E6' }; + case 'upcoming': return { text: '待随访', color: '#F59E0B', bg: '#FFF8E6' }; case 'completed': return { text: '已完成', color: '#20C997', bg: '#E6F9F2' }; - case 'missed': return { text: '已错过', color: '#EF4444', bg: '#FEE9E9' }; + case 'cancelled': return { text: '已取消', color: '#EF4444', bg: '#FEE9E9' }; default: return { text: s, color: '#9BA0B4', bg: '#F5F6F9' }; } }; diff --git a/frontend-doctor/src/pages/reports/ReportDetailPage.tsx b/frontend-doctor/src/pages/reports/ReportDetailPage.tsx index 004fcd6..93ac094 100644 --- a/frontend-doctor/src/pages/reports/ReportDetailPage.tsx +++ b/frontend-doctor/src/pages/reports/ReportDetailPage.tsx @@ -7,7 +7,7 @@ interface RawReport { imageUrls: string[]; status: string; riskLevel?: string; summary?: string; suggestions?: string; patientName?: string; doctorName?: string; - createdAt: string; completedAt?: string; + uploadedAt: string; completedAt?: string; items?: RawItem[]; } @@ -81,7 +81,7 @@ export function ReportDetailPage() {
患者:{report.patientName || '未知'}  |  分类:{categoryMap[report.category] || report.category}  |  - 日期:{report.createdAt?.split('T')[0]} + 日期:{report.uploadedAt?.split('T')[0]}
- {r.createdAt?.split('T')[0]} + {r.uploadedAt?.split('T')[0]}