wangdl e1bfda0169 feat(ios): 知识点删除 + 批量选择删除
- 新增单个删除 + 批量删除 API 调用
- LibraryDetailPage 新增选择模式:
  - ⋯ 菜单 → "选择知识点" 进入多选模式
  - 全选/取消全选 + 批量删除按钮
  - 选中的 items 显示选中标记
  - 选择模式下禁止导航进入详情
- LibraryDetailViewModel 新增 deleteItem + batchDeleteItems

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 22:47:24 +08:00

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")
}
}