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:
2025-07-21 13:55:36 +08:00
parent 50c63f1b1a
commit 26f0cdd760
306 changed files with 1088 additions and 56000 deletions
@@ -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 {
//
}
}
```
---
*本文档提供了完整的技术架构完善建议,建议按阶段逐步实施*