638 lines
14 KiB
Swift
638 lines
14 KiB
Swift
//
|
|
// APIModels.swift - 对齐 api-server 实际返回结构
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// MARK: - API Envelope (ResponseInterceptor wraps all responses)
|
|
|
|
struct APIEnvelope<T: Decodable>: Decodable {
|
|
let success: Bool
|
|
let data: T
|
|
let timestamp: String?
|
|
}
|
|
|
|
// MARK: - Pagination
|
|
|
|
struct PaginationMeta: Codable {
|
|
let page: Int
|
|
let limit: Int
|
|
let total: Int
|
|
}
|
|
|
|
struct PaginatedResponse<T: Decodable>: Decodable {
|
|
let data: [T]
|
|
let meta: PaginationMeta
|
|
}
|
|
|
|
// MARK: - Waitlist
|
|
|
|
struct WaitlistEntry: Codable, Identifiable {
|
|
let id: String
|
|
let nickname: String?
|
|
let email: String
|
|
let devices: [String]?
|
|
let interests: [String]?
|
|
let painpoint: String?
|
|
let willingBeta: Bool?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct WaitlistCreateRequest: Codable {
|
|
let email: String
|
|
let nickname: String?
|
|
let devices: [String]?
|
|
let interests: [String]?
|
|
let painpoint: String?
|
|
let willingBeta: Bool?
|
|
|
|
init(email: String, nickname: String? = nil, devices: [String]? = nil,
|
|
interests: [String]? = nil, painpoint: String? = nil, willingBeta: Bool = true) {
|
|
self.email = email
|
|
self.nickname = nickname
|
|
self.devices = devices
|
|
self.interests = interests
|
|
self.painpoint = painpoint
|
|
self.willingBeta = willingBeta
|
|
}
|
|
}
|
|
|
|
struct WaitlistResponse: Codable {
|
|
let success: Bool?
|
|
let message: String?
|
|
let data: WaitlistEntry?
|
|
}
|
|
|
|
struct WaitlistStats: Codable {
|
|
let total: Int?
|
|
let today: Int?
|
|
let deviceBreakdown: [String: Int]?
|
|
let interestBreakdown: [String: Int]?
|
|
}
|
|
|
|
// MARK: - Auth
|
|
|
|
struct AuthResponse: Codable {
|
|
let accessToken: String
|
|
let refreshToken: String?
|
|
let user: AuthUser?
|
|
}
|
|
|
|
struct AuthUser: Codable, Identifiable {
|
|
let id: String
|
|
let email: String?
|
|
let nickname: String?
|
|
let avatarUrl: String?
|
|
let role: String?
|
|
let status: String?
|
|
let onboardingCompleted: Bool?
|
|
}
|
|
|
|
struct AppleAuthRequest: Codable {
|
|
let identityToken: String
|
|
let authorizationCode: String?
|
|
let fullName: AppleFullName?
|
|
let nonce: String?
|
|
|
|
struct AppleFullName: Codable {
|
|
let givenName: String?
|
|
let familyName: String?
|
|
}
|
|
}
|
|
|
|
struct RefreshRequest: Codable {
|
|
let refreshToken: String
|
|
}
|
|
|
|
// MARK: - User Profile (matches GET /users/me with include: profile + preferences)
|
|
|
|
struct UserProfileResponse: Codable, Identifiable {
|
|
let id: String
|
|
let email: String?
|
|
let nickname: String?
|
|
let avatarUrl: String?
|
|
let role: String?
|
|
let status: String?
|
|
let onboardingCompleted: Bool?
|
|
let createdAt: String?
|
|
let profile: UserProfileData?
|
|
let preferences: UserPreferences?
|
|
}
|
|
|
|
struct UserProfileData: Codable {
|
|
let learningIdentity: String?
|
|
let learningDirection: String?
|
|
let bio: String?
|
|
let currentGoal: String?
|
|
}
|
|
|
|
struct UserPreferences: Codable, Equatable {
|
|
let preferredMethods: [String]?
|
|
let defaultFocusMinutes: Int?
|
|
let aiSuggestionLevel: String?
|
|
let language: String?
|
|
let appearance: String?
|
|
let notificationEnabled: Bool?
|
|
}
|
|
|
|
struct UpdateProfileRequest: Codable {
|
|
let nickname: String?
|
|
let avatarUrl: String?
|
|
}
|
|
|
|
struct UpdatePreferencesRequest: Codable {
|
|
let preferredMethods: [String]?
|
|
let defaultFocusMinutes: Int?
|
|
let aiSuggestionLevel: String?
|
|
let language: String?
|
|
let appearance: String?
|
|
let notificationEnabled: Bool?
|
|
}
|
|
|
|
struct UpdateProfileDataRequest: Codable {
|
|
let learningIdentity: String?
|
|
let learningDirection: String?
|
|
let bio: String?
|
|
let currentGoal: String?
|
|
}
|
|
|
|
// MARK: - Knowledge Base (matches Prisma KnowledgeBase model)
|
|
|
|
struct KnowledgeBase: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let title: String
|
|
let description: String?
|
|
let coverKey: String?
|
|
let coverUrl: String?
|
|
let coverType: String?
|
|
let visibility: String?
|
|
let isPinned: Bool?
|
|
let ownerType: String?
|
|
let isVerified: Bool?
|
|
let status: String?
|
|
let itemCount: Int?
|
|
let lastStudiedAt: String?
|
|
let createdAt: String?
|
|
let updatedAt: String?
|
|
}
|
|
|
|
struct CreateKnowledgeBaseRequest: Codable {
|
|
let title: String
|
|
let description: String?
|
|
var coverKey: String? = nil
|
|
}
|
|
|
|
struct KnowledgeBaseCoverUploadResult {
|
|
let fileId: String
|
|
let objectKey: String
|
|
}
|
|
|
|
// MARK: - Knowledge Items (matches Prisma KnowledgeItem model)
|
|
|
|
struct KnowledgeItem: Codable, Identifiable, Hashable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeBaseId: String?
|
|
let parentId: String?
|
|
let itemType: String?
|
|
let title: String
|
|
let content: String?
|
|
let summary: String?
|
|
let sourceType: String?
|
|
let sourceRef: String?
|
|
let orderIndex: Int?
|
|
let status: String?
|
|
let createdAt: String?
|
|
let updatedAt: String?
|
|
}
|
|
|
|
struct CreateKnowledgeItemRequest: Codable {
|
|
let knowledgeBaseId: String
|
|
let title: String
|
|
let content: String?
|
|
let itemType: String?
|
|
}
|
|
|
|
struct UpdateKnowledgeItemRequest: Codable {
|
|
let title: String?
|
|
let content: String?
|
|
let summary: String?
|
|
}
|
|
|
|
// MARK: - Active Recall (matches ActiveRecallQuestion / Answer models)
|
|
|
|
struct ActiveRecallQuestion: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeItemId: String?
|
|
let questionText: String
|
|
let difficulty: String?
|
|
let createdBy: String?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct ActiveRecallAnswer: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let questionId: String?
|
|
let answerType: String?
|
|
let answerText: String?
|
|
let submittedAt: String?
|
|
}
|
|
|
|
struct SubmitAnswerRequest: Codable {
|
|
let answerText: String
|
|
}
|
|
|
|
// MARK: - AI Analysis (matches AiAnalysisResult model)
|
|
|
|
struct AIAnalysisRequest: Codable {
|
|
let questionText: String?
|
|
let knowledgeItemContent: String?
|
|
let userAnswer: String?
|
|
let text: String?
|
|
let type: String?
|
|
}
|
|
|
|
struct AIAnalysisResult: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let summary: String?
|
|
let masteryScore: Int?
|
|
let strengths: [String]?
|
|
let weaknesses: [String]?
|
|
let suggestions: [String]?
|
|
let nextActions: [String]?
|
|
let rawResult: AIAnalysisRawResult?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct AIAnalysisRawResult: Codable {
|
|
let score: Double?
|
|
let analysis: String?
|
|
let focusItems: [String]?
|
|
}
|
|
|
|
// MARK: - Learning Session (matches Prisma LearningSession model)
|
|
|
|
struct LearningSession: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeBaseId: String?
|
|
let knowledgeItemId: String?
|
|
let mode: String?
|
|
let status: String?
|
|
let startedAt: String?
|
|
let endedAt: String?
|
|
let durationSeconds: Int?
|
|
let focusMinutes: Int?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct CreateLearningSessionRequest: Codable {
|
|
let knowledgeBaseId: String?
|
|
let knowledgeItemId: String?
|
|
let mode: String?
|
|
}
|
|
|
|
// MARK: - Review (matches ReviewCard / ReviewLog models)
|
|
|
|
struct ReviewCard: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeItemId: String?
|
|
let frontText: String
|
|
let backText: String?
|
|
let difficulty: String?
|
|
let status: String?
|
|
let nextReviewAt: String?
|
|
let intervalDays: Int?
|
|
let easeFactor: Double?
|
|
let repetitionCount: Int?
|
|
let lapseCount: Int?
|
|
}
|
|
|
|
struct SubmitReviewRequest: Codable {
|
|
let rating: String
|
|
let responseText: String?
|
|
}
|
|
|
|
// MARK: - Focus Items (matches Prisma FocusItem model)
|
|
|
|
struct FocusItem: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeBaseId: String?
|
|
let knowledgeItemId: String?
|
|
let title: String
|
|
let reason: String?
|
|
let suggestion: String?
|
|
let priority: String?
|
|
let status: String?
|
|
let masteryScore: Int?
|
|
let dueAt: String?
|
|
let completedAt: String?
|
|
let createdAt: String?
|
|
}
|
|
|
|
// MARK: - Activity (matches DailyLearningActivity + summary aggregation)
|
|
|
|
struct ActivitySummary: Codable {
|
|
let totalMinutes: Int?
|
|
let totalCardsReviewed: Int?
|
|
let activeDays: Int?
|
|
let dailyAverage: Int?
|
|
}
|
|
|
|
struct ActivityHeatmap: Codable {
|
|
// Dictionary of "YYYY-MM-DD" -> durationSeconds
|
|
}
|
|
|
|
// MARK: - Feedback
|
|
|
|
struct FeedbackCreateRequest: Codable {
|
|
let category: String
|
|
let content: String
|
|
let email: String?
|
|
|
|
init(category: String = "general", content: String, email: String? = nil) {
|
|
self.category = category
|
|
self.content = content
|
|
self.email = email
|
|
}
|
|
}
|
|
|
|
struct FeedbackData: Codable, Identifiable {
|
|
let id: String?
|
|
let category: String?
|
|
let content: String?
|
|
let status: String?
|
|
let createdAt: String?
|
|
}
|
|
|
|
// MARK: - Notifications (matches Prisma Notification model)
|
|
|
|
struct NotificationItem: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let type: String
|
|
let title: String
|
|
let content: String?
|
|
let data: [String: String]?
|
|
let readAt: String?
|
|
let createdAt: String?
|
|
}
|
|
|
|
// MARK: - Generic
|
|
|
|
struct GenericSuccessResponse: Codable {
|
|
let success: Bool?
|
|
let message: String?
|
|
}
|
|
|
|
struct BatchDeleteResponse: Codable {
|
|
let success: Bool?
|
|
let message: String?
|
|
let count: Int?
|
|
}
|
|
|
|
// MARK: - Quiz
|
|
|
|
struct Quiz: Codable, Identifiable {
|
|
let id: String
|
|
let knowledgeBaseId: String?
|
|
let title: String?
|
|
let description: String?
|
|
let questionCount: Int?
|
|
let sourceType: String?
|
|
let status: String?
|
|
let createdAt: String?
|
|
let questions: [QuizQuestion]?
|
|
}
|
|
|
|
struct QuizQuestion: Codable, Identifiable {
|
|
let id: String
|
|
let quizId: String?
|
|
let type: String?
|
|
let stem: String?
|
|
let options: [String]?
|
|
let answer: String?
|
|
let explanation: String?
|
|
let orderIndex: Int?
|
|
}
|
|
|
|
struct QuizAttempt: Codable, Identifiable {
|
|
let id: String
|
|
let quizId: String?
|
|
let totalQuestions: Int?
|
|
let correctCount: Int?
|
|
let score: Int?
|
|
let startedAt: String?
|
|
let finishedAt: String?
|
|
let answers: [QuizAnswer]?
|
|
let quiz: QuizInfo?
|
|
}
|
|
|
|
struct QuizInfo: Codable { let title: String? }
|
|
|
|
struct QuizAnswer: Codable, Identifiable {
|
|
let id: String
|
|
let attemptId: String?
|
|
let questionId: String?
|
|
let userAnswer: String?
|
|
let isCorrect: Bool?
|
|
let answeredAt: String?
|
|
let question: QuizQuestion?
|
|
}
|
|
|
|
struct QuizGenerateRequest: Codable {
|
|
let knowledgeBaseId: String
|
|
let questionCount: Int
|
|
}
|
|
|
|
struct QuizSubmitRequest: Codable {
|
|
let attemptId: String
|
|
let answers: [QuizAnswerItem]
|
|
}
|
|
|
|
struct QuizAnswerItem: Codable {
|
|
let questionId: String
|
|
let answer: String
|
|
}
|
|
|
|
struct QuizSubmitResponse: Codable {
|
|
let score: Int?
|
|
let correctCount: Int?
|
|
let totalQuestions: Int?
|
|
let finishedAt: String?
|
|
}
|
|
|
|
// MARK: - RAG Chat
|
|
|
|
struct ChatSession: Codable, Identifiable {
|
|
let id: String
|
|
let userId: String?
|
|
let knowledgeBaseId: String?
|
|
let title: String?
|
|
let createdAt: String?
|
|
let updatedAt: String?
|
|
}
|
|
|
|
struct ChatMessage: Codable, Identifiable {
|
|
let id: String
|
|
let sessionId: String?
|
|
let role: String
|
|
let content: String
|
|
let tokens: Int?
|
|
let createdAt: String?
|
|
let citations: [ChatCitation]?
|
|
}
|
|
|
|
struct ChatCitation: Codable, Identifiable {
|
|
let id: String
|
|
let chunkId: String?
|
|
let sourceTitle: String?
|
|
let excerptText: String?
|
|
let pageNumber: Int?
|
|
}
|
|
|
|
struct CreateSessionRequest: Codable {
|
|
let knowledgeBaseId: String
|
|
let title: String?
|
|
}
|
|
|
|
struct SendMessageRequest: Codable {
|
|
let content: String
|
|
}
|
|
|
|
struct SendMessageResponse: Codable {
|
|
let id: String?
|
|
let role: String?
|
|
let content: String?
|
|
let tokens: Int?
|
|
let blocked: Bool?
|
|
let message: String?
|
|
let citations: [ChatCitation]?
|
|
}
|
|
|
|
// MARK: - Document Import
|
|
|
|
struct CreateImportRequest: Codable {
|
|
let knowledgeBaseId: String
|
|
let fileName: String?
|
|
let sourceType: String?
|
|
let rawText: String?
|
|
}
|
|
|
|
struct ImportStatusResponse: Codable {
|
|
let id: String
|
|
let status: String?
|
|
let fileName: String?
|
|
let knowledgeBaseId: String?
|
|
let errorMessage: String?
|
|
let itemCount: Int?
|
|
}
|
|
|
|
// MARK: - Import Candidate
|
|
|
|
struct ImportCandidate: Codable, Identifiable {
|
|
let id: String
|
|
let sourceId: String?
|
|
let title: String?
|
|
let content: String?
|
|
let status: String?
|
|
let confidence: Double?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct BatchAcceptRequest: Codable {
|
|
let sourceId: String
|
|
}
|
|
|
|
// MARK: - Knowledge Source
|
|
|
|
struct KnowledgeSource: Codable, Identifiable {
|
|
let id: String
|
|
let knowledgeBaseId: String?
|
|
let title: String?
|
|
let originalFilename: String?
|
|
let type: String?
|
|
let mimeType: String?
|
|
let parseStatus: String?
|
|
let indexStatus: String?
|
|
let textLength: Int?
|
|
let createdAt: String?
|
|
}
|
|
|
|
struct AddSourceRequest: Codable {
|
|
let fileId: String?
|
|
let type: String?
|
|
let title: String?
|
|
}
|
|
|
|
// MARK: - Learning Activity
|
|
|
|
struct ActivityTrend: Codable {
|
|
let date: String?
|
|
let value: Double?
|
|
let label: String?
|
|
}
|
|
|
|
struct ActivityStreak: Codable {
|
|
let currentStreak: Int?
|
|
let longestStreak: Int?
|
|
let lastActiveDate: String?
|
|
}
|
|
|
|
struct ActivityRecommendation: Codable, Identifiable {
|
|
var id: String { title ?? UUID().uuidString }
|
|
let title: String?
|
|
let description: String?
|
|
let type: String?
|
|
let priority: String?
|
|
}
|
|
|
|
// MARK: - File Upload (COS presigned URL flow)
|
|
|
|
struct FileUploadUrlRequest: Codable {
|
|
let filename: String
|
|
let mimeType: String
|
|
let sizeBytes: Int
|
|
}
|
|
|
|
struct FileUploadUrlResponse: Codable {
|
|
let uploadUrl: String
|
|
let objectKey: String
|
|
let bucket: String
|
|
let region: String
|
|
let expiresIn: Int
|
|
}
|
|
|
|
struct FileConfirmUploadRequest: Codable {
|
|
let objectKey: String
|
|
let checksum: String?
|
|
}
|
|
|
|
struct FileConfirmUploadResponse: Codable {
|
|
let id: String
|
|
let filename: String
|
|
let objectKey: String
|
|
let sizeBytes: Int
|
|
}
|
|
|
|
struct FileDetailResponse: Codable {
|
|
let file: FileInfo
|
|
let downloadUrl: String
|
|
}
|
|
|
|
struct FileInfo: Codable {
|
|
let id: String
|
|
let filename: String
|
|
let mimeType: String?
|
|
let sizeBytes: Int
|
|
let objectKey: String?
|
|
let storagePath: String?
|
|
let createdAt: String?
|
|
}
|