docs(health-profile): 添加防编造加固修订记录到导出健康档案设计文档

补充了关于导出摘要出现虚构病例问题的详细分析和修复方案,
包括检索策略优化、空数据兜底处理和prompt重写等三层防护措施。
```
This commit is contained in:
link2026
2026-05-30 20:06:12 +08:00
parent dad9d43486
commit 7ad41c5f09
26 changed files with 9062 additions and 7697 deletions

View File

@@ -37,6 +37,8 @@ struct HomeView: View {
.padding(.top, 4)
.padding(.bottom, 18)
TodayRemindersCard()
OngoingSymptomsCard()
.padding(.bottom, 18)

View File

@@ -0,0 +1,118 @@
import SwiftUI
import SwiftData
import Combine
/// :(CustomReminder)+ (MetricReminder),
/// ;(,)
/// ( EmptyView,)
/// ; (RemindersListView)
struct TodayRemindersCard: View {
@Query(sort: \CustomReminder.updatedAt, order: .reverse)
private var customReminders: [CustomReminder]
@Query(sort: \MetricReminder.updatedAt, order: .reverse)
private var metricReminders: [MetricReminder]
@State private var showingCenter = false
/// ,( OngoingSymptomsCard )
@State private var tick: Date = .now
private let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
/// , + ,
private var items: [TodayItem] {
let cal = Calendar.current
var arr: [TodayItem] = []
for r in customReminders where r.occurs(on: tick, calendar: cal) {
arr.append(TodayItem(id: "c-\(r.id.uuidString)",
hour: r.hour, minute: r.minute, title: r.title))
}
for r in metricReminders where r.occurs(on: tick, calendar: cal) {
arr.append(TodayItem(id: "m-\(r.metricId)",
hour: r.hour, minute: r.minute, title: r.displayName))
}
return arr.sorted { ($0.hour, $0.minute) < ($1.hour, $1.minute) }
}
var body: some View {
let rows = items
if rows.isEmpty {
EmptyView()
} else {
VStack(alignment: .leading, spacing: 10) {
header(count: rows.count)
VStack(spacing: 8) {
ForEach(rows) { row($0) }
}
}
.padding(.bottom, 18)
.onReceive(timer) { now in tick = now }
.sheet(isPresented: $showingCenter) {
// NavigationStack ;sheet
NavigationStack { RemindersListView(presentedAsSheet: true) }
}
}
}
private func header(count: Int) -> some View {
HStack(spacing: 8) {
Circle()
.fill(Tj.Palette.amber)
.frame(width: 7, height: 7)
Text("今日提醒")
.font(.tjH2())
.foregroundStyle(Tj.Palette.text)
Text("\(count)")
.font(.system(size: 12))
.foregroundStyle(Tj.Palette.text3)
Spacer()
Button { showingCenter = true } label: {
Text("全部 ")
.font(.system(size: 12))
.foregroundStyle(Tj.Palette.text3)
}
.buttonStyle(.plain)
}
}
private func row(_ item: TodayItem) -> some View {
let isPast = item.isPast(now: tick)
return HStack(spacing: 12) {
Text(item.timeLabel)
.font(.system(size: 14, weight: .semibold).monospacedDigit())
.foregroundStyle(isPast ? Tj.Palette.text3 : Tj.Palette.ink)
.frame(width: 46, alignment: .leading)
Image(systemName: "bell.fill")
.font(.system(size: 12))
.foregroundStyle(isPast ? Tj.Palette.text3 : Tj.Palette.amber)
Text(item.title)
.font(.system(size: 15, weight: .medium))
.foregroundStyle(isPast ? Tj.Palette.text3 : Tj.Palette.text)
.lineLimit(1)
Spacer(minLength: 0)
}
.padding(.horizontal, 14)
.padding(.vertical, 12)
.background(
RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous)
.fill(Tj.Palette.paper)
)
.shadow(color: Color(red: 0.196, green: 0.157, blue: 0.098).opacity(0.04),
radius: 2, x: 0, y: 1)
}
}
/// ()
private struct TodayItem: Identifiable {
let id: String
let hour: Int
let minute: Int
let title: String
var timeLabel: String { String(format: "%02d:%02d", hour, minute) }
/// ()
func isPast(now: Date) -> Bool {
let c = Calendar.current.dateComponents([.hour, .minute], from: now)
let nowMinutes = (c.hour ?? 0) * 60 + (c.minute ?? 0)
return hour * 60 + minute < nowMinutes
}
}