fix: audit issues - field mismatches, missing endpoints, data loss

- 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
This commit is contained in:
MingNian
2026-05-24 13:24:21 +08:00
parent d6a432aec4
commit ede4a8d29e
11 changed files with 111 additions and 19 deletions

View File

@@ -16,4 +16,5 @@ public record UserProfileResponse(
public record UpdateProfileRequest(
string? Name, string? Gender, DateOnly? Birthday,
decimal? HeightCm, decimal? WeightKg, List<string>? MedicalHistory);
decimal? HeightCm, decimal? WeightKg, List<string>? MedicalHistory,
string? Department, string? Title, string? Introduction, List<string>? Specialty);

View File

@@ -20,7 +20,7 @@ public class FollowUpService(AppDbContext db)
.OrderBy(f => f.ScheduledAt)
.ToListAsync();
public async Task<FollowUp> AddAsync(Guid patientId, string title, string? description, DateTime scheduledAt, bool reminderEnabled, Guid? doctorId = null)
public async Task<FollowUp> 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<bool> DeleteAsync(Guid id)
{
var followUp = await db.FollowUps.FindAsync(id);
if (followUp == null) return false;
db.FollowUps.Remove(followUp);
await db.SaveChangesAsync();
return true;
}
}

View File

@@ -90,4 +90,34 @@ public class MedicationService(AppDbContext db)
return totalCount > 0 ? Math.Round((decimal)takenCount / totalCount * 100, 1) : 0;
}
public async Task<Medication?> UpdateAsync(Guid medicationId, Guid userId, string? drugName,
string? dosage, string? frequency, List<string>? 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<bool> 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;
}
}

View File

@@ -29,14 +29,28 @@ public class AuthController(
{
// Demo: auto-register new users
var db = HttpContext.RequestServices.GetRequiredService<Infrastructure.Data.AppDbContext>();
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();

View File

@@ -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<IActionResult> 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<IActionResult> 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

View File

@@ -73,8 +73,31 @@ public class MedicationController(MedicationService medicationService) : Control
}
}
[HttpPut("{id:guid}")]
public async Task<IActionResult> 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<IActionResult> 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<string> TimeSlots, DateOnly StartDate, DateOnly? EndDate, string? Notes);
public record MedicationUpdateRequest(
string? DrugName, string? Dosage, string? Frequency,
List<string>? TimeSlots, DateOnly? StartDate, DateOnly? EndDate,
string? Notes, string? Status);
public record MarkTakenRequest(string TimeSlot);