Files
happy-life-star/docs/architecture/技术架构完善建议.md
T
peanut 26f0cdd760 feat: 完成项目整理优化和生产环境配置
🧹 项目结构优化:
- 删除重复和过时的文件
- 整理文档到docs目录结构
- 优化配置文件到configs目录
- 创建清晰的PROJECT_STRUCTURE.md

🔧 中间件配置:
- 重启MySQL/Redis/Nacos中间件
- 使用现有数据目录,确保数据完整性
- 统一密码配置: MySQL(EmotionMuseum2025*#), Nacos(Peanut2817*#)

🌐 Nginx配置:
- 配置前端路径: /emotion-museum
- 配置API代理: /api/ -> 19000
- 配置WebSocket代理: /ws/ -> 19007
- 添加健康检查端点: /health

📋 部署脚本优化:
- restart-middleware.sh - 中间件重启脚本
- setup-nginx.sh - Nginx配置脚本
- cleanup-project.sh - 项目清理脚本
- one-click-deploy.sh - 一键部署脚本

📖 文档完善:
- DEPLOYMENT_FINAL.md - 最终部署指南
- PROJECT_STRUCTURE.md - 项目结构说明
- 完整的运维和故障排查指南

 生产环境就绪:
- 中间件: MySQL/Redis/Nacos 运行正常
- Nginx: 反向代理配置完成
- 访问地址: http://47.111.10.27/emotion-museum
- 健康检查: http://47.111.10.27/health

🎯 项目现状:
- 10个微服务模块完整
- 前后端分离架构
- 容器化部署
- 统一配置管理
- 完整的部署和运维体系
2025-07-21 13:55:36 +08:00

611 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 情绪博物馆技术架构完善建议
**文档版本**: 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<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
}
}
```
#### 服务注册
```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<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)
}
}
```
#### 数据服务层
```swift
// 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模式
#### 数据仓库接口
```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<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实现
```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<NSString, UIImage>()
private let fileManager = FileManager.default
func image(for url: String) async -> UIImage? {
// -> ->
}
func store(_ image: UIImage, for url: String) {
//
}
}
```
#### 数据分页加载
```swift
// 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实现
```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 {
//
}
}
```
---
*本文档提供了完整的技术架构完善建议,建议按阶段逐步实施*