Files
happy-life-star/技术架构完善建议.md
T

15 KiB

情绪博物馆技术架构完善建议

文档版本: v1.0
创建时间: 2025-07-12
基于: 现有代码架构分析


📋 目录


1. 当前架构分析

1.1 架构优势

清晰的分层结构

┌─────────────────────────────────────┐
│            SwiftUI Views            │  ← 表现层
├─────────────────────────────────────┤
│         ViewModel/Manager           │  ← 业务逻辑层
├─────────────────────────────────────┤
│            Services                 │  ← 服务层
├─────────────────────────────────────┤
│             Models                  │  ← 数据模型层
└─────────────────────────────────────┘

良好的状态管理

  • 使用 @ObservableObject 进行状态管理
  • NavigationManager 统一管理导航状态
  • ThemeManager 管理主题切换
  • MockDataManager 提供数据服务

模块化设计

  • 清晰的文件组织结构
  • 功能模块相对独立
  • 可复用的UI组件

1.2 架构不足

数据持久化缺失

  • 缺少真实的数据存储层
  • 依赖模拟数据,无法持久化用户数据
  • 没有数据同步机制

服务层不完整

  • AI服务只有接口定义,缺少实现
  • 缺少网络服务层
  • 缺少错误处理机制

依赖注入不规范

  • 大量使用 shared 单例模式
  • 组件间耦合度较高
  • 测试困难

2. 架构完善建议

2.1 引入依赖注入容器

创建DI容器

// DIContainer.swift
class DIContainer: ObservableObject {
    static let shared = DIContainer()
    
    private var services: [String: Any] = [:]
    
    func register<T>(_ type: T.Type, instance: T) {
        let key = String(describing: type)
        services[key] = instance
    }
    
    func resolve<T>(_ type: T.Type) -> T {
        let key = String(describing: type)
        guard let service = services[key] as? T else {
            fatalError("Service \(key) not registered")
        }
        return service
    }
}

服务注册

// 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 完善服务层架构

网络服务层

// NetworkService.swift
protocol NetworkServiceProtocol {
    func request<T: Codable>(_ 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<T: Codable>(_ 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)
    }
}

数据服务层

// DataService.swift
protocol DataServiceProtocol {
    func save<T: Codable>(_ object: T) async throws
    func fetch<T: Codable>(_ type: T.Type, predicate: NSPredicate?) async throws -> [T]
    func delete<T: Codable>(_ 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<T: Codable>(_ object: T) async throws {
        let context = container.viewContext
        // 实现保存逻辑
        try context.save()
    }
}

2.3 引入Repository模式

数据仓库接口

// 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

// BaseViewModel.swift
@MainActor
class BaseViewModel: ObservableObject {
    @Published var isLoading = false
    @Published var error: AppError?
    
    protected func withLoading<T>(_ 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实现

// 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 代码重构示例

重构前 (现状)

// RecordView.swift - 现状
struct RecordView: View {
    @EnvironmentObject var mockData: MockDataManager
    @EnvironmentObject var navigationManager: NavigationManager
    
    var body: some View {
        // 直接使用mockData
        // 紧耦合,难以测试
    }
}

重构后 (建议)

// 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 内存管理优化

图片缓存策略

// ImageCache.swift
class ImageCache {
    static let shared = ImageCache()
    private let cache = NSCache<NSString, UIImage>()
    private let fileManager = FileManager.default
    
    func image(for url: String) async -> UIImage? {
        // 内存缓存 -> 磁盘缓存 -> 网络下载
    }
    
    func store(_ image: UIImage, for url: String) {
        // 存储到内存和磁盘缓存
    }
}

数据分页加载

// PaginationManager.swift
class PaginationManager<T>: 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实现

// 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 数据加密

// 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安全

// 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 代码规范

// 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 单元测试框架

// 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 文档和注释规范

/// 对话管理的ViewModel
/// 
/// 负责管理用户对话的状态和业务逻辑,包括:
/// - 加载历史对话
/// - 发送和接收消息
/// - AI服务集成
/// - 错误处理
@MainActor
class ConversationViewModel: BaseViewModel {
    
    /// 当前显示的对话列表
    @Published var conversations: [Conversation] = []
    
    /// 当前活跃的对话
    @Published var currentConversation: Conversation?
    
    /// 加载用户的所有对话
    /// - Returns: 无返回值,结果通过 `conversations` 属性发布
    func loadConversations() async {
        // 实现逻辑
    }
}

本文档提供了完整的技术架构完善建议,建议按阶段逐步实施