- 新增单个删除 + 批量删除 API 调用 - LibraryDetailPage 新增选择模式: - ⋯ 菜单 → "选择知识点" 进入多选模式 - 全选/取消全选 + 批量删除按钮 - 选中的 items 显示选中标记 - 选择模式下禁止导航进入详情 - LibraryDetailViewModel 新增 deleteItem + batchDeleteItems Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
294 lines
9.9 KiB
Swift
294 lines
9.9 KiB
Swift
//
|
|
// APIService.swift - 各模块 API 调用封装
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// MARK: - Waitlist
|
|
|
|
@MainActor
|
|
class WaitlistService {
|
|
static let shared = WaitlistService()
|
|
private let client = APIClient.shared
|
|
|
|
func join(email: String, nickname: String?, devices: [String]?,
|
|
interests: [String]?, painpoint: String?, willingBeta: Bool = true) async throws -> WaitlistEntry {
|
|
let body = WaitlistCreateRequest(email: email, nickname: nickname, devices: devices,
|
|
interests: interests, painpoint: painpoint, willingBeta: willingBeta)
|
|
return try await client.request("/waitlist", method: "POST", body: body)
|
|
}
|
|
|
|
func stats() async throws -> WaitlistStats {
|
|
return try await client.request("/waitlist/stats")
|
|
}
|
|
}
|
|
|
|
// MARK: - Auth
|
|
|
|
@MainActor
|
|
class AuthService {
|
|
static let shared = AuthService()
|
|
private let client = APIClient.shared
|
|
|
|
func appleLogin(identityToken: String, authorizationCode: String? = nil, givenName: String?, familyName: String?, nonce: String? = nil) async throws -> AuthResponse {
|
|
let body = AppleAuthRequest(
|
|
identityToken: identityToken,
|
|
authorizationCode: authorizationCode,
|
|
fullName: givenName != nil
|
|
? AppleAuthRequest.AppleFullName(givenName: givenName, familyName: familyName)
|
|
: nil,
|
|
nonce: nonce
|
|
)
|
|
return try await client.request("/auth/apple", method: "POST", body: body)
|
|
}
|
|
}
|
|
|
|
// MARK: - User
|
|
|
|
@MainActor
|
|
class UserService {
|
|
static let shared = UserService()
|
|
private let client = APIClient.shared
|
|
|
|
func myProfile() async throws -> UserProfileResponse {
|
|
return try await client.request("/users/me")
|
|
}
|
|
|
|
func updateProfile(_ dto: UpdateProfileRequest) async throws -> UserProfileResponse {
|
|
return try await client.request("/users/me", method: "PATCH", body: dto)
|
|
}
|
|
|
|
func updatePreferences(_ dto: UpdatePreferencesRequest) async throws -> UserPreferences {
|
|
return try await client.request("/users/me/preferences", method: "PATCH", body: dto)
|
|
}
|
|
|
|
func getProfileDetail() async throws -> UserProfileData {
|
|
return try await client.request("/users/me/profile")
|
|
}
|
|
|
|
func updateProfileDetail(_ dto: UpdateProfileDataRequest) async throws -> UserProfileData {
|
|
return try await client.request("/users/me/profile", method: "PATCH", body: dto)
|
|
}
|
|
}
|
|
|
|
// MARK: - Knowledge Base
|
|
|
|
@MainActor
|
|
class KnowledgeBaseService {
|
|
static let shared = KnowledgeBaseService()
|
|
private let client = APIClient.shared
|
|
|
|
func list(page: Int = 1, limit: Int = 20) async throws -> [KnowledgeBase] {
|
|
return try await client.request("/knowledge-bases", queryItems: [
|
|
URLQueryItem(name: "page", value: String(page)),
|
|
URLQueryItem(name: "limit", value: String(limit)),
|
|
])
|
|
}
|
|
|
|
func create(title: String, description: String?) async throws -> KnowledgeBase {
|
|
let body = CreateKnowledgeBaseRequest(title: title, description: description)
|
|
return try await client.request("/knowledge-bases", method: "POST", body: body)
|
|
}
|
|
|
|
func detail(id: String) async throws -> KnowledgeBase {
|
|
return try await client.request("/knowledge-bases/\(id)")
|
|
}
|
|
|
|
func update(id: String, title: String?, description: String?) async throws -> KnowledgeBase {
|
|
let body = CreateKnowledgeBaseRequest(title: title ?? "", description: description)
|
|
return try await client.request("/knowledge-bases/\(id)", method: "PATCH", body: body)
|
|
}
|
|
|
|
func delete(id: String) async throws -> GenericSuccessResponse {
|
|
return try await client.request("/knowledge-bases/\(id)", method: "DELETE")
|
|
}
|
|
}
|
|
|
|
// MARK: - Knowledge Items
|
|
|
|
@MainActor
|
|
class KnowledgeItemService {
|
|
static let shared = KnowledgeItemService()
|
|
private let client = APIClient.shared
|
|
|
|
func list(knowledgeBaseId: String) async throws -> [KnowledgeItem] {
|
|
return try await client.request("/knowledge-items", queryItems: [
|
|
URLQueryItem(name: "knowledgeBaseId", value: knowledgeBaseId),
|
|
])
|
|
}
|
|
|
|
func detail(id: String) async throws -> KnowledgeItem {
|
|
return try await client.request("/knowledge-items/\(id)")
|
|
}
|
|
|
|
func create(knowledgeBaseId: String, title: String, content: String?, itemType: String? = nil) async throws -> KnowledgeItem {
|
|
let body = CreateKnowledgeItemRequest(
|
|
knowledgeBaseId: knowledgeBaseId,
|
|
title: title,
|
|
content: content,
|
|
itemType: itemType
|
|
)
|
|
return try await client.request("/knowledge-items", method: "POST", body: body)
|
|
}
|
|
|
|
func update(id: String, title: String?, content: String?, summary: String?) async throws -> KnowledgeItem {
|
|
let body = UpdateKnowledgeItemRequest(title: title, content: content, summary: summary)
|
|
return try await client.request("/knowledge-items/\(id)", method: "PATCH", body: body)
|
|
}
|
|
|
|
func delete(id: String) async throws {
|
|
let _: GenericSuccessResponse = try await client.request("/knowledge-items/\(id)", method: "DELETE")
|
|
}
|
|
|
|
func batchDelete(ids: [String]) async throws -> BatchDeleteResponse {
|
|
return try await client.request("/knowledge-items/batch-delete", method: "POST", body: ["ids": ids])
|
|
}
|
|
}
|
|
|
|
// MARK: - Active Recall
|
|
|
|
@MainActor
|
|
class ActiveRecallService {
|
|
static let shared = ActiveRecallService()
|
|
private let client = APIClient.shared
|
|
|
|
func questions(page: Int = 1, limit: Int = 20) async throws -> [ActiveRecallQuestion] {
|
|
return try await client.request("/active-recalls", queryItems: [
|
|
URLQueryItem(name: "page", value: String(page)),
|
|
URLQueryItem(name: "limit", value: String(limit)),
|
|
])
|
|
}
|
|
|
|
func submit(questionId: String, answerText: String) async throws -> ActiveRecallAnswer {
|
|
let body = SubmitAnswerRequest(answerText: answerText)
|
|
return try await client.request("/active-recalls/\(questionId)/submit", method: "POST", body: body)
|
|
}
|
|
}
|
|
|
|
// MARK: - AI Analysis
|
|
|
|
@MainActor
|
|
class AIAnalysisService {
|
|
static let shared = AIAnalysisService()
|
|
private let client = APIClient.shared
|
|
|
|
func analyze(questionText: String, knowledgeItemContent: String, userAnswer: String) async throws -> AIAnalysisResult {
|
|
let body = AIAnalysisRequest(
|
|
questionText: questionText,
|
|
knowledgeItemContent: knowledgeItemContent,
|
|
userAnswer: userAnswer,
|
|
text: nil,
|
|
type: nil
|
|
)
|
|
return try await client.request("/ai-analysis", method: "POST", body: body)
|
|
}
|
|
}
|
|
|
|
// MARK: - Learning Sessions
|
|
|
|
@MainActor
|
|
class LearningSessionService {
|
|
static let shared = LearningSessionService()
|
|
private let client = APIClient.shared
|
|
|
|
func list(page: Int = 1, limit: Int = 20) async throws -> [LearningSession] {
|
|
return try await client.request("/learning-sessions", queryItems: [
|
|
URLQueryItem(name: "page", value: String(page)),
|
|
URLQueryItem(name: "limit", value: String(limit)),
|
|
])
|
|
}
|
|
|
|
func start(knowledgeBaseId: String? = nil, knowledgeItemId: String? = nil, mode: String? = nil) async throws -> LearningSession {
|
|
let body = CreateLearningSessionRequest(
|
|
knowledgeBaseId: knowledgeBaseId,
|
|
knowledgeItemId: knowledgeItemId,
|
|
mode: mode
|
|
)
|
|
return try await client.request("/learning-sessions", method: "POST", body: body)
|
|
}
|
|
|
|
func end(id: String) async throws -> LearningSession {
|
|
return try await client.request("/learning-sessions/\(id)/end", method: "POST")
|
|
}
|
|
}
|
|
|
|
// MARK: - Review
|
|
|
|
@MainActor
|
|
class ReviewService {
|
|
static let shared = ReviewService()
|
|
private let client = APIClient.shared
|
|
|
|
func dueCards() async throws -> [ReviewCard] {
|
|
return try await client.request("/reviews/due")
|
|
}
|
|
|
|
func submit(id: String, rating: String, responseText: String? = nil) async throws -> GenericSuccessResponse {
|
|
let body = SubmitReviewRequest(rating: rating, responseText: responseText)
|
|
return try await client.request("/reviews/\(id)/submit", method: "POST", body: body)
|
|
}
|
|
}
|
|
|
|
// MARK: - Focus Items
|
|
|
|
@MainActor
|
|
class FocusItemService {
|
|
static let shared = FocusItemService()
|
|
private let client = APIClient.shared
|
|
|
|
func list(page: Int = 1, limit: Int = 20) async throws -> [FocusItem] {
|
|
return try await client.request("/focus-items", queryItems: [
|
|
URLQueryItem(name: "page", value: String(page)),
|
|
URLQueryItem(name: "limit", value: String(limit)),
|
|
])
|
|
}
|
|
}
|
|
|
|
// MARK: - Activity & Stats
|
|
|
|
@MainActor
|
|
class ActivityService {
|
|
static let shared = ActivityService()
|
|
private let client = APIClient.shared
|
|
|
|
func summary() async throws -> ActivitySummary {
|
|
return try await client.request("/activity/summary")
|
|
}
|
|
|
|
func heatmap() async throws -> [String: Int] {
|
|
return try await client.request("/activity/heatmap")
|
|
}
|
|
}
|
|
|
|
// MARK: - Feedback
|
|
|
|
@MainActor
|
|
class FeedbackService {
|
|
static let shared = FeedbackService()
|
|
private let client = APIClient.shared
|
|
|
|
func submit(category: String = "general", content: String, email: String? = nil) async throws -> FeedbackData {
|
|
let body = FeedbackCreateRequest(category: category, content: content, email: email)
|
|
return try await client.request("/feedback", method: "POST", body: body)
|
|
}
|
|
}
|
|
|
|
// MARK: - Notifications
|
|
|
|
@MainActor
|
|
class NotificationService {
|
|
static let shared = NotificationService()
|
|
private let client = APIClient.shared
|
|
|
|
func list(page: Int = 1, limit: Int = 20) async throws -> [NotificationItem] {
|
|
return try await client.request("/notifications", queryItems: [
|
|
URLQueryItem(name: "page", value: String(page)),
|
|
URLQueryItem(name: "limit", value: String(limit)),
|
|
])
|
|
}
|
|
|
|
func markRead(id: String) async throws -> NotificationItem {
|
|
return try await client.request("/notifications/\(id)/read", method: "PATCH")
|
|
}
|
|
}
|