211 lines
7.3 KiB
Swift
211 lines
7.3 KiB
Swift
import Foundation
|
||
|
||
class NoteService {
|
||
static let shared = NoteService()
|
||
private let apiClient = APIClient.shared
|
||
|
||
private init() {}
|
||
|
||
// MARK: - 创建笔记
|
||
func createNote(_ request: CreateNoteRequest) async throws -> Note {
|
||
// 将 Codable 结构体转换为字典
|
||
let encoder = JSONEncoder()
|
||
guard let jsonData = try? encoder.encode(request),
|
||
let bodyDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
||
throw APIError.serverError("请求体编码失败")
|
||
}
|
||
|
||
let response: NoteResponse = try await apiClient.request(
|
||
endpoint: "/api/notes",
|
||
method: "POST",
|
||
body: bodyDict,
|
||
requiresAuth: true
|
||
)
|
||
|
||
guard response.success else {
|
||
throw APIError.serverError("创建笔记失败")
|
||
}
|
||
|
||
return response.data
|
||
}
|
||
|
||
// MARK: - 获取笔记列表
|
||
func getNotes(
|
||
courseId: String? = nil,
|
||
nodeId: String? = nil,
|
||
notebookId: String? = nil, // ✅ Phase 3: 新增按笔记本过滤
|
||
type: NoteType? = nil,
|
||
page: Int = 1,
|
||
limit: Int = 20
|
||
) async throws -> (notes: [Note], total: Int) {
|
||
// 构建查询参数
|
||
var queryComponents: [String] = []
|
||
if let courseId = courseId {
|
||
queryComponents.append("course_id=\(courseId.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? courseId)")
|
||
}
|
||
if let nodeId = nodeId {
|
||
queryComponents.append("node_id=\(nodeId.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? nodeId)")
|
||
}
|
||
// ✅ Phase 3: 新增按笔记本过滤
|
||
if let notebookId = notebookId {
|
||
queryComponents.append("notebook_id=\(notebookId.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? notebookId)")
|
||
}
|
||
if let type = type {
|
||
queryComponents.append("type=\(type.rawValue)")
|
||
}
|
||
queryComponents.append("page=\(page)")
|
||
queryComponents.append("limit=\(limit)")
|
||
|
||
let queryString = queryComponents.isEmpty ? "" : "?\(queryComponents.joined(separator: "&"))"
|
||
|
||
let response: NoteListResponse = try await apiClient.request(
|
||
endpoint: "/api/notes\(queryString)",
|
||
method: "GET",
|
||
requiresAuth: true
|
||
)
|
||
|
||
guard response.success else {
|
||
throw APIError.serverError("获取笔记列表失败")
|
||
}
|
||
|
||
return (response.data.notes, response.data.total)
|
||
}
|
||
|
||
// MARK: - ✅ Phase 3: 获取笔记本下的所有笔记
|
||
func getNotesByNotebook(notebookId: String, page: Int = 1, limit: Int = 50) async throws -> (notes: [Note], total: Int) {
|
||
return try await getNotes(notebookId: notebookId, page: page, limit: limit)
|
||
}
|
||
|
||
// MARK: - ✅ Phase 3: 更新笔记层级和排序
|
||
func updateNoteHierarchy(noteId: String, parentId: String?, order: Int?, level: Int?) async throws -> Note {
|
||
let request = UpdateNoteRequest(
|
||
parentId: parentId,
|
||
order: order,
|
||
level: level,
|
||
content: nil,
|
||
quotedText: nil,
|
||
startIndex: nil,
|
||
length: nil,
|
||
style: nil
|
||
)
|
||
|
||
return try await updateNote(noteId: noteId, request: request)
|
||
}
|
||
|
||
// MARK: - ✅ Phase 3: 移动笔记(调整层级)
|
||
enum MoveDirection {
|
||
case up // 上移(同级调整顺序)
|
||
case down // 下移(同级调整顺序)
|
||
case indent // 缩进(降级)
|
||
case outdent // 升级
|
||
}
|
||
|
||
func moveNote(noteId: String, direction: MoveDirection) async throws -> Note {
|
||
// 先获取当前笔记
|
||
let currentNote = try await getNote(noteId: noteId)
|
||
|
||
// 根据方向计算新的层级和排序
|
||
var newParentId: String? = currentNote.parentId
|
||
var newOrder: Int = currentNote.order
|
||
var newLevel: Int = currentNote.level
|
||
|
||
switch direction {
|
||
case .up:
|
||
// 上移:order - 1(需要与同级前一个笔记交换)
|
||
newOrder = max(0, currentNote.order - 1)
|
||
|
||
case .down:
|
||
// 下移:order + 1(需要与同级后一个笔记交换)
|
||
newOrder = currentNote.order + 1
|
||
|
||
case .indent:
|
||
// 缩进(降级):成为前一个笔记的子节点
|
||
// 需要找到前一个同级笔记
|
||
// 这里简化处理,实际需要查询前一个笔记
|
||
if currentNote.level < 2 {
|
||
newLevel = currentNote.level + 1
|
||
// TODO: 实现查找前一个笔记的逻辑
|
||
}
|
||
|
||
case .outdent:
|
||
// 升级:成为父节点的兄弟节点
|
||
if let parentId = currentNote.parentId {
|
||
// 获取父笔记
|
||
let parentNote = try await getNote(noteId: parentId)
|
||
newParentId = parentNote.parentId
|
||
newLevel = max(0, currentNote.level - 1)
|
||
// TODO: 实现计算新 order 的逻辑
|
||
}
|
||
}
|
||
|
||
return try await updateNoteHierarchy(
|
||
noteId: noteId,
|
||
parentId: newParentId,
|
||
order: newOrder,
|
||
level: newLevel
|
||
)
|
||
}
|
||
|
||
// MARK: - 获取单个笔记
|
||
func getNote(noteId: String) async throws -> Note {
|
||
let response: NoteResponse = try await apiClient.request(
|
||
endpoint: "/api/notes/\(noteId)",
|
||
method: "GET",
|
||
requiresAuth: true
|
||
)
|
||
|
||
guard response.success else {
|
||
throw APIError.serverError("获取笔记失败")
|
||
}
|
||
|
||
return response.data
|
||
}
|
||
|
||
// MARK: - 获取节点下的所有笔记(用于在课程内容中显示)
|
||
func getNotesForNode(nodeId: String) async throws -> [Note] {
|
||
let response: NoteListResponse = try await apiClient.request(
|
||
endpoint: "/api/lessons/\(nodeId)/notes",
|
||
method: "GET",
|
||
requiresAuth: true
|
||
)
|
||
|
||
guard response.success else {
|
||
throw APIError.serverError("获取节点笔记失败")
|
||
}
|
||
|
||
return response.data.notes
|
||
}
|
||
|
||
// MARK: - 更新笔记
|
||
func updateNote(noteId: String, request: UpdateNoteRequest) async throws -> Note {
|
||
// 将 Codable 结构体转换为字典
|
||
let encoder = JSONEncoder()
|
||
guard let jsonData = try? encoder.encode(request),
|
||
let bodyDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
||
throw APIError.serverError("请求体编码失败")
|
||
}
|
||
|
||
let response: NoteResponse = try await apiClient.request(
|
||
endpoint: "/api/notes/\(noteId)",
|
||
method: "PUT",
|
||
body: bodyDict,
|
||
requiresAuth: true
|
||
)
|
||
|
||
guard response.success else {
|
||
throw APIError.serverError("更新笔记失败")
|
||
}
|
||
|
||
return response.data
|
||
}
|
||
|
||
// MARK: - 删除笔记
|
||
func deleteNote(noteId: String) async throws {
|
||
let _: EmptyResponse = try await apiClient.request(
|
||
endpoint: "/api/notes/\(noteId)",
|
||
method: "DELETE",
|
||
requiresAuth: true
|
||
)
|
||
}
|
||
}
|