import Combine import CoreDomain import CoreModel import Foundation /// Fake 计划仓库(骨架阶段返回假数据,可提交)。 public final class FakePlanRepository: PlanRepository, @unchecked Sendable { private let plans: [PlanType: CurrentValueSubject] private var submits: [String: CurrentValueSubject] = [:] private let lock = NSLock() public init() { var dict: [PlanType: CurrentValueSubject] = [:] for type in PlanType.allCases { dict[type] = CurrentValueSubject(Self.seedPlan(type)) } plans = dict } public func observePlan(type: PlanType) -> AsyncThrowingStream { Self.stream(plans[type]!) } public func observePlanSubmit(type: PlanType, periodKey: String) -> AsyncThrowingStream { Self.stream(submitSubject(type: type, periodKey: periodKey)) } public func savePlan(type: PlanType, periodKey: String, content: String, attachments: [String]) async throws { try await Task.sleep(for: .milliseconds(300)) let submit = submitSubject(type: type, periodKey: periodKey) let currentSubmit = submit.value submit.value = PlanSubmitData( content: content, attachments: attachments, checked: true, canSubmit: currentSubmit.canSubmit, message: currentSubmit.message, history: currentSubmit.history ) let plan = plans[type]! let currentPlan = plan.value plan.value = PlanRecord( type: currentPlan.type, periodKey: currentPlan.periodKey, canSubmit: currentPlan.canSubmit, message: currentPlan.message, myChecked: true, myRecord: MyPlanRecord(status: "已完成", submittedTime: "现在", content: content, attachments: attachments), groups: currentPlan.groups, unassignedCount: currentPlan.unassignedCount ) } private func submitSubject(type: PlanType, periodKey: String) -> CurrentValueSubject { let key = "\(type.rawValue):\(periodKey)" lock.lock() defer { lock.unlock() } if let existing = submits[key] { return existing } let subject = CurrentValueSubject(Self.seedSubmit(type: type)) submits[key] = subject return subject } private static func stream(_ subject: CurrentValueSubject) -> AsyncThrowingStream { AsyncThrowingStream { continuation in let cancellable = subject.sink { continuation.yield($0) } continuation.onTermination = { _ in cancellable.cancel() } } } private static func seedPlan(_ type: PlanType) -> PlanRecord { PlanRecord( type: type, periodKey: periodKey(for: type), canSubmit: true, message: type == .weekly ? "本周计划提交截止:周日晚 23:59" : nil, myChecked: type == .weekly, myRecord: type == .weekly ? MyPlanRecord(status: "已完成", submittedTime: "今天 18:30", content: "本周完成两章学习,整理笔记并复习错题。", attachments: ["图1", "图2"]) : nil, groups: seedGroups(), unassignedCount: 2 ) } private static func seedSubmit(type: PlanType) -> PlanSubmitData { if type == .weekly { return PlanSubmitData( content: "本周完成两章学习,整理笔记并复习错题。", attachments: ["图1", "图2"], checked: true, canSubmit: true, message: nil, history: [] ) } return PlanSubmitData( content: "", attachments: [], checked: false, canSubmit: true, message: nil, history: [ MyPlanRecord(status: "已完成", submittedTime: "2026-08 完成", content: "上月计划内容……", attachments: []), ] ) } private static func periodKey(for type: PlanType) -> String { switch type { case .weekly: return "2026 第 37 周" case .monthly: return "2026 年 9 月" case .yearly: return "2026 年" } } private static func seedGroups() -> [PlanGroup] { [ PlanGroup(id: "g1", name: "进阶一组", checkedCount: 4, memberCount: 6, members: seedMembers()), PlanGroup(id: "g2", name: "进阶二组", checkedCount: 5, memberCount: 5, members: seedMembers()), PlanGroup(id: "g3", name: "进阶三组", checkedCount: 2, memberCount: 7, members: seedMembers()), ] } private static func seedMembers() -> [PlanMember] { [ PlanMember(id: "p1", name: "张三", filled: true), PlanMember(id: "p2", name: "李四", filled: true), PlanMember(id: "p3", name: "王五", filled: false), ] } }