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个微服务模块完整 - 前后端分离架构 - 容器化部署 - 统一配置管理 - 完整的部署和运维体系
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
# 情绪博物馆Spring Cloud Alibaba微服务架构设计
|
||||
|
||||
**版本**: v1.0
|
||||
**创建时间**: 2025-07-12
|
||||
**技术栈**: Spring Cloud Alibaba 2022.0.0.0
|
||||
**Spring Boot版本**: 3.0.2
|
||||
**JDK版本**: 17+
|
||||
|
||||
---
|
||||
|
||||
## 📋 架构分析
|
||||
|
||||
### 业务模块分析
|
||||
基于功能需求文档分析,情绪博物馆包含以下核心业务模块:
|
||||
|
||||
1. **用户认证模块** - 账号、密码、手机号登录
|
||||
2. **AI对话模块** - 智能对话、情绪分析
|
||||
3. **情绪记录模块** - 情绪日历、记录管理
|
||||
4. **成长课题模块** - 课题系统、互动记录
|
||||
5. **地图探索模块** - 地点标记、社区分享
|
||||
6. **成就奖励模块** - 成就系统、积分奖励
|
||||
7. **用户统计模块** - 数据统计、分析报告
|
||||
|
||||
### 技术架构选型
|
||||
|
||||
#### Spring Cloud Alibaba 2022.0.0.0 组件
|
||||
- **Nacos**: 服务注册发现 + 配置中心
|
||||
- **Sentinel**: 流量控制、熔断降级
|
||||
- **Seata**: 分布式事务
|
||||
- **Gateway**: API网关
|
||||
- **OpenFeign**: 服务间调用
|
||||
- **Dubbo**: RPC通信(可选)
|
||||
|
||||
#### 基础设施组件
|
||||
- **MySQL 8.0**: 主数据库
|
||||
- **Redis 7.0**: 缓存 + 分布式锁
|
||||
- **RocketMQ**: 消息队列
|
||||
- **Elasticsearch**: 搜索引擎
|
||||
- **MinIO**: 对象存储
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 微服务架构设计
|
||||
|
||||
### 服务拆分策略
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[API Gateway] --> B[用户服务]
|
||||
A --> C[AI对话服务]
|
||||
A --> D[情绪记录服务]
|
||||
A --> E[成长课题服务]
|
||||
A --> F[地图探索服务]
|
||||
A --> G[成就奖励服务]
|
||||
A --> H[统计分析服务]
|
||||
|
||||
B --> I[MySQL-用户库]
|
||||
C --> J[MySQL-对话库]
|
||||
D --> K[MySQL-情绪库]
|
||||
E --> L[MySQL-成长库]
|
||||
F --> M[MySQL-地图库]
|
||||
G --> N[MySQL-奖励库]
|
||||
H --> O[MySQL-统计库]
|
||||
|
||||
P[Nacos] --> A
|
||||
P --> B
|
||||
P --> C
|
||||
P --> D
|
||||
P --> E
|
||||
P --> F
|
||||
P --> G
|
||||
P --> H
|
||||
|
||||
Q[Redis] --> B
|
||||
Q --> C
|
||||
Q --> D
|
||||
Q --> E
|
||||
Q --> F
|
||||
Q --> G
|
||||
Q --> H
|
||||
```
|
||||
|
||||
### 微服务清单
|
||||
|
||||
| 服务名称 | 端口 | 职责 | 数据库 |
|
||||
|---------|------|------|--------|
|
||||
| emotion-gateway | 8080 | API网关、路由、鉴权 | - |
|
||||
| emotion-user | 8081 | 用户认证、资料管理 | user, user_stats |
|
||||
| emotion-ai | 8082 | AI对话、情绪分析 | conversation, message |
|
||||
| emotion-record | 8083 | 情绪记录、日历管理 | emotion_record |
|
||||
| emotion-growth | 8084 | 成长课题、互动记录 | growth_topic, topic_interaction |
|
||||
| emotion-explore | 8085 | 地图探索、社区分享 | location_pin, community_post, comment |
|
||||
| emotion-reward | 8086 | 成就奖励、积分管理 | achievement, reward |
|
||||
| emotion-stats | 8087 | 数据统计、分析报告 | 跨库查询 |
|
||||
|
||||
---
|
||||
|
||||
## 📦 技术版本选择
|
||||
|
||||
### Spring Cloud Alibaba 2022.0.0.0
|
||||
这是当前最稳定的版本,具有以下优势:
|
||||
- ✅ 与Spring Boot 3.0.x完美兼容
|
||||
- ✅ 支持JDK 17+
|
||||
- ✅ 生产环境验证充分
|
||||
- ✅ 社区活跃,文档完善
|
||||
- ✅ 阿里云原生支持
|
||||
|
||||
### 版本依赖关系
|
||||
```xml
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring-boot.version>3.0.2</spring-boot.version>
|
||||
<spring-cloud.version>2022.0.0</spring-cloud.version>
|
||||
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<redis.version>7.0.8</redis.version>
|
||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||
</properties>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 核心配置设计
|
||||
|
||||
### Nacos配置中心
|
||||
```yaml
|
||||
# application-dev.yml
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
namespace: emotion-dev
|
||||
group: DEFAULT_GROUP
|
||||
config:
|
||||
server-addr: localhost:8848
|
||||
namespace: emotion-dev
|
||||
group: DEFAULT_GROUP
|
||||
file-extension: yml
|
||||
shared-configs:
|
||||
- data-id: common-mysql.yml
|
||||
group: DEFAULT_GROUP
|
||||
refresh: true
|
||||
- data-id: common-redis.yml
|
||||
group: DEFAULT_GROUP
|
||||
refresh: true
|
||||
```
|
||||
|
||||
### 数据库配置
|
||||
```yaml
|
||||
# common-mysql.yml
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/emotion_museum?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
username: ${DB_USERNAME:root}
|
||||
password: ${DB_PASSWORD:123456}
|
||||
hikari:
|
||||
minimum-idle: 5
|
||||
maximum-pool-size: 20
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
connection-timeout: 30000
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: assign_uuid
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
```
|
||||
|
||||
### Redis配置
|
||||
```yaml
|
||||
# common-redis.yml
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: ${REDIS_PASSWORD:}
|
||||
database: 0
|
||||
timeout: 3000ms
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 20
|
||||
max-idle: 10
|
||||
min-idle: 5
|
||||
max-wait: 3000ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 实施计划
|
||||
|
||||
### Phase 1: 基础设施搭建 (1周)
|
||||
1. **父工程创建**
|
||||
- Maven多模块项目结构
|
||||
- 版本依赖管理
|
||||
- 公共组件抽取
|
||||
|
||||
2. **注册中心部署**
|
||||
- Nacos Server安装配置
|
||||
- 命名空间和分组设置
|
||||
- 配置文件管理
|
||||
|
||||
3. **数据库初始化**
|
||||
- MySQL数据库创建
|
||||
- 表结构和索引创建
|
||||
- 初始数据导入
|
||||
|
||||
### Phase 2: 核心服务开发 (2-3周)
|
||||
1. **网关服务** (emotion-gateway)
|
||||
- 路由配置
|
||||
- 统一鉴权
|
||||
- 限流熔断
|
||||
|
||||
2. **用户服务** (emotion-user)
|
||||
- 用户注册登录
|
||||
- JWT Token管理
|
||||
- 用户资料CRUD
|
||||
|
||||
3. **AI对话服务** (emotion-ai)
|
||||
- 对话管理
|
||||
- 消息处理
|
||||
- AI接口集成
|
||||
|
||||
### Phase 3: 业务服务开发 (3-4周)
|
||||
1. **情绪记录服务** (emotion-record)
|
||||
2. **成长课题服务** (emotion-growth)
|
||||
3. **地图探索服务** (emotion-explore)
|
||||
4. **成就奖励服务** (emotion-reward)
|
||||
|
||||
### Phase 4: 数据服务开发 (1-2周)
|
||||
1. **统计分析服务** (emotion-stats)
|
||||
2. **监控告警配置**
|
||||
3. **性能优化调试**
|
||||
|
||||
---
|
||||
|
||||
## 📊 服务间通信设计
|
||||
|
||||
### API调用关系
|
||||
```
|
||||
emotion-gateway
|
||||
├── emotion-user (用户认证)
|
||||
├── emotion-ai (AI对话)
|
||||
│ └── emotion-user (用户信息)
|
||||
├── emotion-record (情绪记录)
|
||||
│ └── emotion-user (用户验证)
|
||||
├── emotion-growth (成长课题)
|
||||
│ ├── emotion-user (用户信息)
|
||||
│ └── emotion-reward (奖励发放)
|
||||
├── emotion-explore (地图探索)
|
||||
│ └── emotion-user (用户信息)
|
||||
├── emotion-reward (成就奖励)
|
||||
│ └── emotion-user (用户信息)
|
||||
└── emotion-stats (统计分析)
|
||||
├── emotion-user (用户数据)
|
||||
├── emotion-ai (对话数据)
|
||||
├── emotion-record (情绪数据)
|
||||
├── emotion-growth (成长数据)
|
||||
└── emotion-explore (探索数据)
|
||||
```
|
||||
|
||||
### 消息队列设计
|
||||
```yaml
|
||||
# RocketMQ Topic设计
|
||||
topics:
|
||||
- emotion-user-events # 用户事件
|
||||
- emotion-conversation # 对话事件
|
||||
- emotion-record-events # 情绪记录事件
|
||||
- emotion-growth-events # 成长事件
|
||||
- emotion-explore-events # 探索事件
|
||||
- emotion-reward-events # 奖励事件
|
||||
- emotion-stats-events # 统计事件
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*接下来将按照此架构设计逐步创建各个微服务模块*
|
||||
@@ -0,0 +1,610 @@
|
||||
# 情绪博物馆技术架构完善建议
|
||||
|
||||
**文档版本**: 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 {
|
||||
// 实现逻辑
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*本文档提供了完整的技术架构完善建议,建议按阶段逐步实施*
|
||||
Reference in New Issue
Block a user