按 W2 plan Task 2 落地数据模型: - Indicator 加 report / asset / pinned 字段 - Report 加 indicators / assets @Relationship(cascade) - DiaryEntry 加 tags - 新增 @Model Asset (原图元数据) - 新增 @Model ChatTurn (问答历史 + 引用) - TijiApp Schema 加入新 model 注:Schema 破坏性变更,用户需在 Xcode 里 Erase Simulator 后重启 App。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
157 lines
3.7 KiB
Swift
157 lines
3.7 KiB
Swift
import Foundation
|
|
import SwiftData
|
|
|
|
enum IndicatorStatus: String, Codable, CaseIterable {
|
|
case high, low, normal
|
|
}
|
|
|
|
enum ReportType: String, Codable, CaseIterable {
|
|
case checkup, lab, imaging, prescription, other
|
|
|
|
var label: String {
|
|
switch self {
|
|
case .checkup: return "体检报告"
|
|
case .lab: return "化验单"
|
|
case .imaging: return "影像报告"
|
|
case .prescription: return "处方"
|
|
case .other: return "其他"
|
|
}
|
|
}
|
|
}
|
|
|
|
@Model
|
|
final class Indicator {
|
|
var name: String
|
|
var value: String
|
|
var unit: String
|
|
var range: String
|
|
var statusRaw: String
|
|
var note: String?
|
|
var capturedAt: Date
|
|
|
|
var report: Report?
|
|
var asset: Asset?
|
|
var pinned: Bool = false
|
|
|
|
init(name: String,
|
|
value: String,
|
|
unit: String,
|
|
range: String,
|
|
status: IndicatorStatus,
|
|
note: String? = nil,
|
|
capturedAt: Date = .now,
|
|
report: Report? = nil,
|
|
asset: Asset? = nil,
|
|
pinned: Bool = false) {
|
|
self.name = name
|
|
self.value = value
|
|
self.unit = unit
|
|
self.range = range
|
|
self.statusRaw = status.rawValue
|
|
self.note = note
|
|
self.capturedAt = capturedAt
|
|
self.report = report
|
|
self.asset = asset
|
|
self.pinned = pinned
|
|
}
|
|
|
|
var status: IndicatorStatus {
|
|
IndicatorStatus(rawValue: statusRaw) ?? .normal
|
|
}
|
|
}
|
|
|
|
@Model
|
|
final class Report {
|
|
var title: String
|
|
var typeRaw: String
|
|
var reportDate: Date
|
|
var institution: String?
|
|
var note: String?
|
|
var summary: String?
|
|
var pageCount: Int
|
|
var createdAt: Date
|
|
|
|
@Relationship(deleteRule: .cascade, inverse: \Indicator.report)
|
|
var indicators: [Indicator] = []
|
|
|
|
@Relationship(deleteRule: .cascade)
|
|
var assets: [Asset] = []
|
|
|
|
init(title: String,
|
|
type: ReportType,
|
|
reportDate: Date,
|
|
institution: String? = nil,
|
|
note: String? = nil,
|
|
summary: String? = nil,
|
|
pageCount: Int = 1,
|
|
createdAt: Date = .now) {
|
|
self.title = title
|
|
self.typeRaw = type.rawValue
|
|
self.reportDate = reportDate
|
|
self.institution = institution
|
|
self.note = note
|
|
self.summary = summary
|
|
self.pageCount = pageCount
|
|
self.createdAt = createdAt
|
|
}
|
|
|
|
var type: ReportType {
|
|
ReportType(rawValue: typeRaw) ?? .other
|
|
}
|
|
}
|
|
|
|
@Model
|
|
final class DiaryEntry {
|
|
var content: String
|
|
var createdAt: Date
|
|
var tags: [String]
|
|
|
|
init(content: String, createdAt: Date = .now, tags: [String] = []) {
|
|
self.content = content
|
|
self.createdAt = createdAt
|
|
self.tags = tags
|
|
}
|
|
}
|
|
|
|
@Model
|
|
final class Asset {
|
|
var relativePath: String
|
|
var mimeType: String
|
|
var bytes: Int
|
|
var createdAt: Date
|
|
|
|
init(relativePath: String,
|
|
mimeType: String = "image/jpeg",
|
|
bytes: Int = 0,
|
|
createdAt: Date = .now) {
|
|
self.relativePath = relativePath
|
|
self.mimeType = mimeType
|
|
self.bytes = bytes
|
|
self.createdAt = createdAt
|
|
}
|
|
}
|
|
|
|
@Model
|
|
final class ChatTurn {
|
|
var question: String
|
|
var answer: String
|
|
var referencedIndicatorIDs: [String]
|
|
var referencedReportIDs: [String]
|
|
var createdAt: Date
|
|
var decodeRate: Double
|
|
|
|
init(question: String,
|
|
answer: String,
|
|
referencedIndicatorIDs: [String] = [],
|
|
referencedReportIDs: [String] = [],
|
|
createdAt: Date = .now,
|
|
decodeRate: Double = 0) {
|
|
self.question = question
|
|
self.answer = answer
|
|
self.referencedIndicatorIDs = referencedIndicatorIDs
|
|
self.referencedReportIDs = referencedReportIDs
|
|
self.createdAt = createdAt
|
|
self.decodeRate = decodeRate
|
|
}
|
|
}
|