# 情绪博物馆技术架构完善建议 **文档版本**: v1.0 **创建时间**: 2025-07-12 **基于**: 现有代码架构分析 --- ## 📋 目录 - [1. 当前架构分析](#1-当前架构分析) - [2. 架构完善建议](#2-架构完善建议) - [3. 具体实施方案](#3-具体实施方案) - [4. 性能优化建议](#4-性能优化建议) - [5. 安全性增强](#5-安全性增强) - [6. 可维护性提升](#6-可维护性提升) --- ## 1. 当前架构分析 ### 1.1 架构优势 ✅ #### 清晰的分层结构 ``` ┌─────────────────────────────────────┐ │ SwiftUI Views │ ← 表现层 ├─────────────────────────────────────┤ │ ViewModel/Manager │ ← 业务逻辑层 ├─────────────────────────────────────┤ │ Services │ ← 服务层 ├─────────────────────────────────────┤ │ Models │ ← 数据模型层 └─────────────────────────────────────┘ ``` #### 良好的状态管理 - ✅ 使用 `@ObservableObject` 进行状态管理 - ✅ `NavigationManager` 统一管理导航状态 - ✅ `ThemeManager` 管理主题切换 - ✅ `MockDataManager` 提供数据服务 #### 模块化设计 - ✅ 清晰的文件组织结构 - ✅ 功能模块相对独立 - ✅ 可复用的UI组件 ### 1.2 架构不足 ❌ #### 数据持久化缺失 - ❌ 缺少真实的数据存储层 - ❌ 依赖模拟数据,无法持久化用户数据 - ❌ 没有数据同步机制 #### 服务层不完整 - ❌ AI服务只有接口定义,缺少实现 - ❌ 缺少网络服务层 - ❌ 缺少错误处理机制 #### 依赖注入不规范 - ❌ 大量使用 `shared` 单例模式 - ❌ 组件间耦合度较高 - ❌ 测试困难 --- ## 2. 架构完善建议 ### 2.1 引入依赖注入容器 #### 创建DI容器 ```swift // DIContainer.swift class DIContainer: ObservableObject { static let shared = DIContainer() private var services: [String: Any] = [:] func register(_ type: T.Type, instance: T) { let key = String(describing: type) services[key] = instance } func resolve(_ type: T.Type) -> T { let key = String(describing: type) guard let service = services[key] as? T else { fatalError("Service \(key) not registered") } return service } } ``` #### 服务注册 ```swift // ServiceRegistration.swift extension DIContainer { func registerServices() { // 数据服务 register(DataServiceProtocol.self, instance: CoreDataService()) // AI服务 register(AIServiceProtocol.self, instance: OpenAIService()) // 位置服务 register(LocationServiceProtocol.self, instance: LocationService()) // 网络服务 register(NetworkServiceProtocol.self, instance: NetworkService()) } } ``` ### 2.2 完善服务层架构 #### 网络服务层 ```swift // NetworkService.swift protocol NetworkServiceProtocol { func request(_ endpoint: APIEndpoint) async throws -> T func upload(data: Data, to endpoint: APIEndpoint) async throws -> UploadResponse } class NetworkService: NetworkServiceProtocol { private let session: URLSession private let baseURL: URL init(session: URLSession = .shared, baseURL: URL) { self.session = session self.baseURL = baseURL } func request(_ endpoint: APIEndpoint) async throws -> T { let request = try buildRequest(for: endpoint) let (data, response) = try await session.data(for: request) guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else { throw NetworkError.invalidResponse } return try JSONDecoder().decode(T.self, from: data) } } ``` #### 数据服务层 ```swift // DataService.swift protocol DataServiceProtocol { func save(_ object: T) async throws func fetch(_ type: T.Type, predicate: NSPredicate?) async throws -> [T] func delete(_ object: T) async throws } class CoreDataService: DataServiceProtocol { private let container: NSPersistentContainer init() { container = NSPersistentContainer(name: "EmotionMuseum") container.loadPersistentStores { _, error in if let error = error { fatalError("Core Data error: \(error)") } } } func save(_ object: T) async throws { let context = container.viewContext // 实现保存逻辑 try context.save() } } ``` ### 2.3 引入Repository模式 #### 数据仓库接口 ```swift // Repository.swift protocol Repository { associatedtype Entity func create(_ entity: Entity) async throws func read(id: UUID) async throws -> Entity? func update(_ entity: Entity) async throws func delete(id: UUID) async throws func list(predicate: NSPredicate?) async throws -> [Entity] } // 具体实现 class ConversationRepository: Repository { typealias Entity = Conversation private let dataService: DataServiceProtocol init(dataService: DataServiceProtocol) { self.dataService = dataService } func create(_ conversation: Conversation) async throws { try await dataService.save(conversation) } func list(predicate: NSPredicate? = nil) async throws -> [Conversation] { return try await dataService.fetch(Conversation.self, predicate: predicate) } } ``` ### 2.4 改进ViewModel架构 #### 基础ViewModel ```swift // BaseViewModel.swift @MainActor class BaseViewModel: ObservableObject { @Published var isLoading = false @Published var error: AppError? protected func withLoading(_ operation: () async throws -> T) async rethrows -> T { isLoading = true defer { isLoading = false } return try await operation() } protected func handleError(_ error: Error) { self.error = AppError(from: error) } } ``` #### 具体ViewModel实现 ```swift // ConversationViewModel.swift @MainActor class ConversationViewModel: BaseViewModel { @Published var conversations: [Conversation] = [] @Published var currentConversation: Conversation? private let repository: ConversationRepository private let aiService: AIServiceProtocol init(repository: ConversationRepository, aiService: AIServiceProtocol) { self.repository = repository self.aiService = aiService super.init() } func loadConversations() async { await withLoading { do { conversations = try await repository.list() } catch { handleError(error) } } } func sendMessage(_ content: String) async { guard let conversation = currentConversation else { return } do { // 添加用户消息 let userMessage = Message( conversationId: conversation.id, content: content, type: .text, sender: .user ) // 获取AI回复 let aiResponse = try await aiService.sendMessage(content) let aiMessage = Message( conversationId: conversation.id, content: aiResponse.content, type: .text, sender: .ai ) // 更新对话 var updatedConversation = conversation updatedConversation.messages.append(contentsOf: [userMessage, aiMessage]) try await repository.update(updatedConversation) currentConversation = updatedConversation } catch { handleError(error) } } } ``` --- ## 3. 具体实施方案 ### 3.1 阶段性重构计划 #### Phase 1: 基础设施搭建 (1周) 1. **创建DI容器** - 实现依赖注入容器 - 注册核心服务 - 更新App启动流程 2. **完善错误处理** - 定义统一错误类型 - 实现错误处理机制 - 添加用户友好的错误提示 3. **网络服务层** - 实现基础网络服务 - 添加请求/响应拦截器 - 实现重试机制 #### Phase 2: 数据层重构 (1-2周) 1. **Core Data集成** - 设计数据模型 - 实现数据服务 - 数据迁移策略 2. **Repository模式** - 实现各个Repository - 数据缓存策略 - 离线数据支持 #### Phase 3: 业务层重构 (1-2周) 1. **ViewModel重构** - 重构现有ViewModel - 添加单元测试 - 优化状态管理 2. **服务集成** - AI服务真实实现 - 地图服务集成 - 推送服务集成 ### 3.2 代码重构示例 #### 重构前 (现状) ```swift // RecordView.swift - 现状 struct RecordView: View { @EnvironmentObject var mockData: MockDataManager @EnvironmentObject var navigationManager: NavigationManager var body: some View { // 直接使用mockData // 紧耦合,难以测试 } } ``` #### 重构后 (建议) ```swift // RecordView.swift - 重构后 struct RecordView: View { @StateObject private var viewModel: RecordViewModel init(viewModel: RecordViewModel = DIContainer.shared.resolve(RecordViewModel.self)) { self._viewModel = StateObject(wrappedValue: viewModel) } var body: some View { // 使用viewModel // 松耦合,易于测试 } } // RecordViewModel.swift @MainActor class RecordViewModel: BaseViewModel { @Published var conversations: [Conversation] = [] @Published var currentMessage: String = "" private let conversationRepository: ConversationRepository private let aiService: AIServiceProtocol init(conversationRepository: ConversationRepository, aiService: AIServiceProtocol) { self.conversationRepository = conversationRepository self.aiService = aiService super.init() } } ``` --- ## 4. 性能优化建议 ### 4.1 内存管理优化 #### 图片缓存策略 ```swift // ImageCache.swift class ImageCache { static let shared = ImageCache() private let cache = NSCache() private let fileManager = FileManager.default func image(for url: String) async -> UIImage? { // 内存缓存 -> 磁盘缓存 -> 网络下载 } func store(_ image: UIImage, for url: String) { // 存储到内存和磁盘缓存 } } ``` #### 数据分页加载 ```swift // PaginationManager.swift class PaginationManager: ObservableObject { @Published var items: [T] = [] @Published var isLoading = false @Published var hasMoreData = true private var currentPage = 0 private let pageSize = 20 func loadNextPage() async { guard !isLoading && hasMoreData else { return } isLoading = true defer { isLoading = false } // 加载下一页数据 } } ``` ### 4.2 UI性能优化 #### LazyLoading实现 ```swift // LazyImageView.swift struct LazyImageView: View { let url: String @State private var image: UIImage? @State private var isLoading = true var body: some View { Group { if let image = image { Image(uiImage: image) .resizable() .aspectRatio(contentMode: .fit) } else if isLoading { ProgressView() } else { Image(systemName: "photo") .foregroundColor(.gray) } } .onAppear { loadImage() } } private func loadImage() { Task { image = await ImageCache.shared.image(for: url) isLoading = false } } } ``` --- ## 5. 安全性增强 ### 5.1 数据加密 ```swift // EncryptionService.swift protocol EncryptionServiceProtocol { func encrypt(_ data: Data) throws -> Data func decrypt(_ encryptedData: Data) throws -> Data } class EncryptionService: EncryptionServiceProtocol { private let key: SymmetricKey init() { // 从Keychain获取或生成密钥 self.key = SymmetricKey(size: .bits256) } func encrypt(_ data: Data) throws -> Data { let sealedBox = try AES.GCM.seal(data, using: key) return sealedBox.combined! } func decrypt(_ encryptedData: Data) throws -> Data { let sealedBox = try AES.GCM.SealedBox(combined: encryptedData) return try AES.GCM.open(sealedBox, using: key) } } ``` ### 5.2 API安全 ```swift // APIKeyManager.swift class APIKeyManager { private let keychain = Keychain(service: "com.emotionmuseum.app") func storeAPIKey(_ key: String, for service: String) { keychain[service] = key } func getAPIKey(for service: String) -> String? { return keychain[service] } } ``` --- ## 6. 可维护性提升 ### 6.1 代码规范 ```swift // SwiftLint配置 // .swiftlint.yml rules: - line_length - function_body_length - type_body_length - cyclomatic_complexity - force_cast - force_try line_length: warning: 120 error: 150 function_body_length: warning: 50 error: 100 ``` ### 6.2 单元测试框架 ```swift // ConversationViewModelTests.swift @MainActor class ConversationViewModelTests: XCTestCase { var viewModel: ConversationViewModel! var mockRepository: MockConversationRepository! var mockAIService: MockAIService! override func setUp() { super.setUp() mockRepository = MockConversationRepository() mockAIService = MockAIService() viewModel = ConversationViewModel( repository: mockRepository, aiService: mockAIService ) } func testLoadConversations() async { // 测试加载对话功能 await viewModel.loadConversations() XCTAssertFalse(viewModel.isLoading) XCTAssertEqual(viewModel.conversations.count, mockRepository.mockConversations.count) } func testSendMessage() async { // 测试发送消息功能 let testMessage = "Hello" await viewModel.sendMessage(testMessage) XCTAssertTrue(mockAIService.sendMessageCalled) XCTAssertEqual(mockAIService.lastMessage, testMessage) } } ``` ### 6.3 文档和注释规范 ```swift /// 对话管理的ViewModel /// /// 负责管理用户对话的状态和业务逻辑,包括: /// - 加载历史对话 /// - 发送和接收消息 /// - AI服务集成 /// - 错误处理 @MainActor class ConversationViewModel: BaseViewModel { /// 当前显示的对话列表 @Published var conversations: [Conversation] = [] /// 当前活跃的对话 @Published var currentConversation: Conversation? /// 加载用户的所有对话 /// - Returns: 无返回值,结果通过 `conversations` 属性发布 func loadConversations() async { // 实现逻辑 } } ``` --- *本文档提供了完整的技术架构完善建议,建议按阶段逐步实施*