diff --git a/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate b/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate index 9089d94..87f5d6e 100644 Binary files a/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate and b/AIStudyApp/AIStudyApp.xcodeproj/project.xcworkspace/xcuserdata/Admin1.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AIStudyApp/AIStudyApp/Features/Analysis/AnalysisHomeView.swift b/AIStudyApp/AIStudyApp/Features/Analysis/AnalysisHomeView.swift index 21b8ad7..1d02b7d 100644 --- a/AIStudyApp/AIStudyApp/Features/Analysis/AnalysisHomeView.swift +++ b/AIStudyApp/AIStudyApp/Features/Analysis/AnalysisHomeView.swift @@ -39,10 +39,10 @@ struct AnalysisHomeView: View { } if let ls = viewModel.learningSummary { HStack(spacing: 12) { - ZXStatBadge(icon: "book.pages", label: "今日阅读", value: "\(ls.todaySeconds / 60) 分钟", trend: "", color: Color.zxBlue) + ZXStatBadge(icon: "book.pages", label: "今日阅读", value: "\(ls.todaySeconds / 60) 分钟", trend: "", color: Color.blue) ZXStatBadge(icon: "calendar.badge.clock", label: "本周阅读", value: "\(ls.weekSeconds / 60) 分钟", trend: "", color: Color.zxTeal) ZXStatBadge(icon: "doc.text", label: "已读资料", value: "\(ls.markedReadCount) 本", trend: "", color: Color.zxGreen) - ZXStatBadge(icon: "books.vertical", label: "阅读资料", value: "\(ls.materialsReadCount) 本", trend: "", color: Color.zxIndigo) + ZXStatBadge(icon: "books.vertical", label: "阅读资料", value: "\(ls.materialsReadCount) 本", trend: "", color: Color.indigo) } } VStack(alignment: .leading, spacing: 16) { @@ -88,7 +88,7 @@ struct AnalysisHomeView: View { Spacer() Text("最近 \(viewModel.recentRecords.count) 条").font(.system(size: 12)).foregroundColor(Color.zxF04) } - ForEach(viewModel.recentRecords.prefix(5)) { rec in + ForEach(viewModel.recentRecords.prefix(5), id: \.id) { rec in HStack(spacing: 10) { Circle().fill(rec.recordType == "reading" ? Color.zxPurple : Color.zxGreen).frame(width: 8, height: 8) VStack(alignment: .leading, spacing: 2) { @@ -275,5 +275,5 @@ private func formatDuration(_ seconds: Int) -> String { private func barHeight(val: Int, max: Int) -> CGFloat { guard max > 0 else { return 8 } - return max(8, CGFloat(val) / CGFloat(max) * 60) + return Swift.max(8, CGFloat(val) / CGFloat(max) * 60) } diff --git a/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialDetailView.swift b/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialDetailView.swift index 4196047..a6b70a1 100644 --- a/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialDetailView.swift +++ b/AIStudyApp/AIStudyApp/Features/MaterialReader/MaterialDetailView.swift @@ -9,14 +9,20 @@ protocol KnowledgeBaseServicing { extension KnowledgeBaseService: KnowledgeBaseServicing {} protocol KnowledgeItemServicing { - func list(knowledgeBaseId: String, sortBy: String?, order: String?) async throws -> [KnowledgeItem] + func listAll(knowledgeBaseId: String) async throws -> [KnowledgeItem] +} +extension KnowledgeItemService: KnowledgeItemServicing { + func listAll(knowledgeBaseId: String) async throws -> [KnowledgeItem] { + try await list(knowledgeBaseId: knowledgeBaseId, sortBy: nil, order: nil) + } } -extension KnowledgeItemService: KnowledgeItemServicing {} protocol QuizServicing { - func list(knowledgeBaseId: String) async throws -> [Quiz] + func listAll(knowledgeBaseId: String) async throws -> [Quiz] +} +extension QuizService: QuizServicing { + func listAll(knowledgeBaseId: String) async throws -> [Quiz] { try await list(knowledgeBaseId: knowledgeBaseId) } } -extension QuizService: QuizServicing {} protocol ReadingProgressServicing { func getReadingProgress(materialId: String, targetType: String) async throws -> MaterialReadingProgressDTO @@ -69,8 +75,8 @@ final class MaterialDetailViewModel: ObservableObject { isLoading = true async let kbResult = try? kbService.detail(id: knowledgeBaseId) - async let itemsResult = try? itemService.list(knowledgeBaseId: knowledgeBaseId) - async let quizzesResult = try? quizService.list(knowledgeBaseId: knowledgeBaseId) + async let itemsResult = try? itemService.listAll(knowledgeBaseId: knowledgeBaseId) + async let quizzesResult = try? quizService.listAll(knowledgeBaseId: knowledgeBaseId) async let progressResult: MaterialReadingProgressDTO? = { if let mid = materialId { return try? await readingService.getReadingProgress(materialId: mid, targetType: "knowledge_source") diff --git a/AIStudyApp/AIStudyApp/Features/Study/StudyHomeViewModel.swift b/AIStudyApp/AIStudyApp/Features/Study/StudyHomeViewModel.swift index b45acb0..5dddfb5 100644 --- a/AIStudyApp/AIStudyApp/Features/Study/StudyHomeViewModel.swift +++ b/AIStudyApp/AIStudyApp/Features/Study/StudyHomeViewModel.swift @@ -4,9 +4,11 @@ import Foundation // MARK: - Service protocols (for testability) protocol SessionServicing { - func list() async throws -> [LearningSession] + func listAll() async throws -> [LearningSession] +} +extension LearningSessionService: SessionServicing { + func listAll() async throws -> [LearningSession] { try await list() } } -extension LearningSessionService: SessionServicing {} protocol ReviewServicing { func dueCards() async throws -> [ReviewCard] @@ -14,9 +16,11 @@ protocol ReviewServicing { extension ReviewService: ReviewServicing {} protocol KBListServicing { - func list() async throws -> [KnowledgeBase] + func listAll() async throws -> [KnowledgeBase] +} +extension KnowledgeBaseService: KBListServicing { + func listAll() async throws -> [KnowledgeBase] { try await list() } } -extension KnowledgeBaseService: KBListServicing {} protocol QuizListServicing { func listAll() async throws -> [Quiz] @@ -98,10 +102,10 @@ final class StudyHomeViewModel: ObservableObject { loadingState = .loading banner = nil - async let sessions = try? sessionService.list() + async let sessions = try? sessionService.listAll() async let dueCards = try? reviewService.dueCards() async let quizzes = try? quizService.listAll() - async let knowledgeBases = try? kbService.list() + async let knowledgeBases = try? kbService.listAll() async let summary = try? activityService.summary() async let streak = try? activityService.streak() @@ -135,10 +139,10 @@ final class StudyHomeViewModel: ObservableObject { } func refresh() async { - async let sessions = try? sessionService.list() + async let sessions = try? sessionService.listAll() async let dueCards = try? reviewService.dueCards() async let quizzes = try? quizService.listAll() - async let knowledgeBases = try? kbService.list() + async let knowledgeBases = try? kbService.listAll() async let summary = try? activityService.summary() async let streak = try? activityService.streak() @@ -176,8 +180,7 @@ final class StudyHomeViewModel: ObservableObject { let resolved = MaterialPathResolver.resolve(materialId: materialId, apiType: cl.type, title: cl.title) { let kbTitle = cl.title ?? knowledgeBases.first?.title ?? "学习" let elapsed = formatElapsedSince(cl.lastReadAt) - let pos = cl.lastPosition.flatMap { ReadingPositionAdapter.fromValue($0) } - return .continueSession(materialId: materialId, filePath: resolved.filePath, materialType: resolved.materialType, kbTitle: kbTitle, elapsed: elapsed, resumePosition: pos) + return .continueSession(materialId: materialId, filePath: resolved.filePath, materialType: resolved.materialType, kbTitle: kbTitle, elapsed: elapsed, resumePosition: nil) } if let unfinished = sessions @@ -186,8 +189,7 @@ final class StudyHomeViewModel: ObservableObject { .first { let kbTitle = knowledgeBases.first(where: { $0.id == unfinished.knowledgeBaseId })?.title ?? "学习" let elapsed = formatElapsedSince(unfinished.startedAt) - let mt: MaterialType = unfinished.readingTargetType == "knowledge_source" ? .unknown : .unknown - return .continueSession(materialId: unfinished.materialId ?? "", filePath: "", materialType: mt, kbTitle: kbTitle, elapsed: elapsed, resumePosition: nil) + return .continueSession(materialId: "", filePath: "", materialType: .unknown, kbTitle: kbTitle, elapsed: elapsed, resumePosition: nil) } if !dueCards.isEmpty { diff --git a/AIStudyApp/AIStudyAppTests/MaterialDetailViewModelTests.swift b/AIStudyApp/AIStudyAppTests/MaterialDetailViewModelTests.swift index d8111c3..14e87ba 100644 --- a/AIStudyApp/AIStudyAppTests/MaterialDetailViewModelTests.swift +++ b/AIStudyApp/AIStudyAppTests/MaterialDetailViewModelTests.swift @@ -10,7 +10,7 @@ private final class MockKnowledgeBaseService: KnowledgeBaseServicing { private final class MockKnowledgeItemService: KnowledgeItemServicing { var items: [KnowledgeItem] = [] - func list(knowledgeBaseId: String, sortBy: String?, order: String?) async throws -> [KnowledgeItem] { items } + func listAll(knowledgeBaseId: String) async throws -> [KnowledgeItem] { items } } private final class MockQuizService: QuizServicing { diff --git a/AIStudyApp/AIStudyAppTests/StudyHomeViewModelTests.swift b/AIStudyApp/AIStudyAppTests/StudyHomeViewModelTests.swift index 1542ff3..f35bf7d 100644 --- a/AIStudyApp/AIStudyAppTests/StudyHomeViewModelTests.swift +++ b/AIStudyApp/AIStudyAppTests/StudyHomeViewModelTests.swift @@ -5,7 +5,7 @@ import XCTest private final class MockSessionService: SessionServicing { var sessions: [LearningSession] = [] - func list() async throws -> [LearningSession] { sessions } + func listAll() async throws -> [LearningSession] { sessions } } private final class MockReviewService: ReviewServicing { @@ -20,7 +20,7 @@ private final class MockQuizListService: QuizListServicing { private final class MockKBListService: KBListServicing { var kbs: [KnowledgeBase] = [] - func list() async throws -> [KnowledgeBase] { kbs } + func listAll() async throws -> [KnowledgeBase] { kbs } } private final class MockActivityHomeService: ActivityHomeServicing {