Files
happy-life-star/web-new/前端技术方案Augment.md
T

1139 lines
30 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.
# 情绪博物馆Web端技术方案
## 技术选型说明
### WebSocket通信方案选择
本方案选择 **STOMP + 原生WebSocket** 而非 Socket.io 的原因:
1. **后端集成优势**: 项目后端使用Spring BootSTOMP是Spring WebSocket的原生支持协议
2. **标准化协议**: STOMP是标准的消息传递协议,不依赖特定实现
3. **消息队列支持**: 天然支持点对点和发布订阅模式,适合聊天和通知场景
4. **轻量级**: 相比Socket.io更轻量,减少前端包体积
5. **Token认证支持**: 原生WebSocket支持在握手时传递自定义请求头进行Token认证
### 技术栈版本策略
- **稳定性优先**: 选择经过验证的稳定版本
- **生态兼容**: 确保各组件间良好兼容
- **长期支持**: 优先选择有LTS支持的版本
- **性能考虑**: 新版本的性能优化和bug修复
## 1. 核心技术栈(推荐版本)
### 1.1 前端框架
- **Vue.js**: `3.4.21` (最新稳定版)
- **TypeScript**: `5.4.2` (最新稳定版)
- **Vite**: `5.1.6` (最新稳定版,更好的构建性能)
### 1.2 UI框架与样式
- **Element Plus**: `2.6.1` (最新稳定版,更好的Vue3支持)
- **Tailwind CSS**: `3.4.1` (最新稳定版)
- **@tailwindcss/forms**: `0.5.7` (表单样式增强)
- **@tailwindcss/typography**: `0.5.10` (文本排版增强)
### 1.3 状态管理与路由
- **Pinia**: `2.1.7` (保持现有版本,稳定可靠)
- **Vue Router**: `4.3.0` (最新稳定版)
- **@pinia/nuxt**: `0.5.1` (如果需要SSR支持)
### 1.4 HTTP客户端与实时通信
- **Axios**: `1.6.8` (最新稳定版)
- **@stomp/stompjs**: `7.1.1` (WebSocket通信,与Spring Boot后端集成,支持Token认证)
### 1.5 数据可视化
- **ECharts**: `5.5.0` (最新稳定版)
- **vue-echarts**: `6.7.3` (Vue3专用ECharts组件)
- **@antv/g2**: `5.1.15` (备选图表库,更现代化)
### 1.6 工具库
- **Day.js**: `1.11.10` (最新稳定版)
- **Lodash-es**: `4.17.21` (ES模块版本)
- **Zod**: `3.22.4` (数据验证)
- **VueUse**: `10.9.0` (Vue组合式API工具集)
### 1.7 开发工具
- **@vitejs/plugin-vue**: `5.0.4`
- **@vue/tsconfig**: `0.5.1`
- **vue-tsc**: `2.0.6`
- **unplugin-auto-import**: `0.17.5` (自动导入)
- **unplugin-vue-components**: `0.26.0` (组件自动导入)
## 2. 新增推荐技术栈
### 2.1 表单处理
- **@vuelidate/core**: `2.0.3` (表单验证)
- **@vuelidate/validators**: `2.0.4`
- **vue-hooks-form**: `0.8.6` (表单状态管理)
### 2.2 动画与交互
- **@vueuse/motion**: `2.0.0` (动画库)
- **vue-toastification**: `2.0.0-rc.5` (通知组件)
- **nprogress**: `0.2.0` (页面加载进度条)
### 2.3 文件处理
- **vue-upload-component**: `3.1.4` (文件上传)
- **cropperjs**: `1.6.1` (图片裁剪)
- **file-saver**: `2.0.5` (文件下载)
### 2.4 富文本编辑
- **@tiptap/vue-3**: `2.2.4` (现代富文本编辑器)
- **@tiptap/starter-kit**: `2.2.4`
- **@tiptap/extension-image**: `2.2.4`
### 2.5 PWA支持
- **vite-plugin-pwa**: `0.19.2` (PWA支持)
- **workbox-window**: `7.0.0` (Service Worker管理)
## 3. 开发工具与代码质量
### 3.1 代码规范
- **ESLint**: `8.57.0`
- **@vue/eslint-config-typescript**: `12.0.0`
- **@vue/eslint-config-prettier**: `9.0.0`
- **Prettier**: `3.2.5`
- **lint-staged**: `15.2.2`
- **husky**: `9.0.11`
### 3.2 测试框架
- **Vitest**: `1.4.0` (单元测试)
- **@vue/test-utils**: `2.4.5` (Vue组件测试)
- **jsdom**: `24.0.0` (DOM环境模拟)
- **Cypress**: `13.7.1` (E2E测试)
### 3.3 构建优化
- **rollup-plugin-visualizer**: `5.12.0` (构建分析)
- **vite-plugin-compression**: `0.5.1` (Gzip压缩)
- **vite-plugin-mock**: `3.0.1` (Mock数据)
## 4. 项目结构设计
```
src/
├── api/ # API接口定义
│ ├── auth.ts # 认证相关接口
│ ├── chat.ts # 聊天相关接口
│ ├── diary.ts # 日记相关接口
│ └── user.ts # 用户相关接口
├── assets/ # 静态资源
│ ├── images/ # 图片资源
│ ├── icons/ # 图标资源
│ └── styles/ # 全局样式
├── components/ # 公共组件
│ ├── common/ # 通用组件
│ ├── forms/ # 表单组件
│ ├── charts/ # 图表组件
│ └── layout/ # 布局组件
├── composables/ # 组合式API
│ ├── useAuth.ts # 认证逻辑
│ ├── useChat.ts # 聊天逻辑
│ ├── useWebSocket.ts # WebSocket逻辑
│ └── useApi.ts # API调用逻辑
├── config/ # 配置文件
│ ├── env.ts # 环境配置
│ ├── constants.ts # 常量定义
│ └── routes.ts # 路由配置
├── layouts/ # 页面布局
│ ├── DefaultLayout.vue # 默认布局
│ ├── AuthLayout.vue # 认证布局
│ └── ChatLayout.vue # 聊天布局
├── pages/ # 页面组件
│ ├── auth/ # 认证页面
│ ├── chat/ # 聊天页面
│ ├── diary/ # 日记页面
│ └── dashboard/ # 仪表盘页面
├── stores/ # 状态管理
│ ├── auth.ts # 认证状态
│ ├── chat.ts # 聊天状态
│ ├── user.ts # 用户状态
│ └── app.ts # 应用状态
├── types/ # 类型定义
│ ├── api.ts # API类型
│ ├── user.ts # 用户类型
│ ├── chat.ts # 聊天类型
│ └── global.d.ts # 全局类型
├── utils/ # 工具函数
│ ├── request.ts # HTTP请求工具
│ ├── websocket.ts # WebSocket工具
│ ├── storage.ts # 存储工具
│ ├── validation.ts # 验证工具
│ └── format.ts # 格式化工具
└── views/ # 页面视图
├── Home.vue # 首页
├── Login.vue # 登录页
├── Chat.vue # 聊天页
└── Dashboard.vue # 仪表盘
```
## 5. 核心功能实现方案
### 5.1 认证系统
- **JWT Token管理**: 使用Pinia存储,自动刷新机制
- **路由守卫**: 基于Vue Router的权限控制
- **第三方登录**: 支持微信、QQ、GitHub等
- **验证码**: 图形验证码 + 短信验证码
### 5.2 实时通信
- **STOMP协议**: 基于@stomp/stompjs,与Spring Boot WebSocket集成
- **原生WebSocket**: 支持Token认证,无需降级方案
- **连接管理**: 自动重连、心跳检测、连接状态监控
- **消息队列**: 支持点对点和发布订阅模式
- **Token认证**: 在WebSocket握手时传递Authorization头部
- **消息类型**: 文本、图片、表情、文件、系统通知
- **离线处理**: 离线消息缓存和同步机制
### 5.3 数据可视化
- **情绪趋势图**: 基于ECharts的时间序列图
- **情绪雷达图**: 多维度情绪分析
- **成长轨迹**: 交互式时间轴
- **数据导出**: 支持PDF、Excel导出
### 5.4 响应式设计
- **移动端适配**: 基于Tailwind CSS的响应式布局
- **触摸手势**: 支持滑动、缩放等手势操作
- **PWA支持**: 离线缓存、桌面安装
- **性能优化**: 虚拟滚动、懒加载
## 6. 环境配置优化
### 6.1 多环境配置
```typescript
// config/env.ts
export const envConfigs = {
local: {
name: '本地环境',
apiBaseUrl: 'http://localhost:19089/api',
wsBaseUrl: 'ws://localhost:19089',
uploadUrl: 'http://localhost:19089/api/upload',
debug: true,
mock: false
},
dev: {
name: '开发环境',
apiBaseUrl: 'https://dev-api.emotion-museum.com/api',
wsBaseUrl: 'wss://dev-api.emotion-museum.com',
uploadUrl: 'https://dev-api.emotion-museum.com/api/upload',
debug: true,
mock: false
},
test: {
name: '测试环境',
apiBaseUrl: 'https://test-api.emotion-museum.com/api',
wsBaseUrl: 'wss://test-api.emotion-museum.com',
uploadUrl: 'https://test-api.emotion-museum.com/api/upload',
debug: false,
mock: false
},
prod: {
name: '生产环境',
apiBaseUrl: 'https://api.emotion-museum.com/api',
wsBaseUrl: 'wss://api.emotion-museum.com',
uploadUrl: 'https://api.emotion-museum.com/api/upload',
debug: false,
mock: false
}
}
```
### 6.2 构建优化配置
```typescript
// vite.config.ts
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
dts: true
}),
Components({
resolvers: [ElementPlusResolver()],
dts: true
}),
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}']
}
})
],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
elementPlus: ['element-plus'],
echarts: ['echarts'],
utils: ['axios', 'dayjs', 'lodash-es']
}
}
}
}
})
```
## 7. 部署方案
### 7.1 Docker部署
```dockerfile
# Dockerfile
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### 7.2 CI/CD配置
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- run: npm ci
- run: npm run build
- run: npm run test
- name: Deploy to server
run: |
# 部署脚本
```
### 7.3 Nginx配置
```nginx
# nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 处理Vue Router的history模式
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# API代理
location /api/ {
proxy_pass http://backend:19089/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket代理
location /ws/ {
proxy_pass http://backend:19089/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
## 8. 性能优化策略
### 8.1 代码分割
- **路由级别懒加载**: 使用动态import()
- **组件级别动态导入**: 按需加载大型组件
- **第三方库按需加载**: Tree-shaking优化
- **图片懒加载**: Intersection Observer API
### 8.2 缓存策略
- **HTTP缓存配置**: 静态资源长期缓存
- **Service Worker缓存**: 离线访问支持
- **本地存储优化**: IndexedDB存储大量数据
- **CDN加速**: 静态资源CDN分发
### 8.3 监控与分析
- **Sentry**: `7.108.0` (错误监控)
- **Google Analytics**: `gtag` (用户行为分析)
- **Web Vitals**: `3.5.2` (性能指标监控)
- **Bundle Analyzer**: 构建分析工具
### 8.4 性能指标
- **首屏加载时间**: < 2秒
- **交互响应时间**: < 100ms
- **代码分割**: 单个chunk < 250KB
- **图片优化**: WebP格式,响应式图片
## 9. 安全考虑
### 9.1 前端安全
- **XSS防护**: 内容安全策略(CSP)
- **CSRF防护**: Token验证
- **敏感信息加密**: 本地存储加密
- **依赖安全**: 定期更新依赖包
### 9.2 API安全
- **Token自动刷新**: JWT令牌管理
- **请求签名验证**: HMAC签名
- **接口限流**: 防止恶意请求
- **数据加密传输**: HTTPS强制
### 9.3 内容安全策略
```html
<!-- CSP配置 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' wss: https:;">
```
## 10. 开发规范
### 10.1 代码规范
- **命名规范**:
- 组件: PascalCase (UserProfile.vue)
- 文件: kebab-case (user-profile.ts)
- 变量: camelCase (userName)
- 常量: UPPER_SNAKE_CASE (API_BASE_URL)
### 10.2 Git提交规范
```
feat: 新功能
fix: 修复bug
docs: 文档更新
style: 代码格式调整
refactor: 代码重构
test: 测试相关
chore: 构建工具或辅助工具的变动
```
### 10.3 组件开发规范
```vue
<template>
<!-- 模板内容 -->
</template>
<script setup lang="ts">
// 导入
import { ref, computed, onMounted } from 'vue'
import type { User } from '@/types/user'
// Props定义
interface Props {
user: User
readonly?: boolean
}
const props = withDefaults(defineProps<Props>(), {
readonly: false
})
// Emits定义
interface Emits {
update: [user: User]
delete: [id: string]
}
const emit = defineEmits<Emits>()
// 响应式数据
const loading = ref(false)
// 计算属性
const displayName = computed(() => {
return props.user.nickname || props.user.username
})
// 生命周期
onMounted(() => {
// 初始化逻辑
})
</script>
<style scoped>
/* 组件样式 */
</style>
```
### 10.4 API接口规范
```typescript
// api/user.ts
import type { User, UserProfile, UpdateUserRequest } from '@/types/user'
import { request } from '@/utils/request'
export const userApi = {
// 获取用户信息
getProfile(): Promise<UserProfile> {
return request.get('/user/profile')
},
// 更新用户信息
updateProfile(data: UpdateUserRequest): Promise<User> {
return request.put('/user/profile', data)
},
// 上传头像
uploadAvatar(file: File): Promise<{ url: string }> {
const formData = new FormData()
formData.append('avatar', file)
return request.post('/user/avatar/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
}
```
### 10.5 WebSocket实现方案
#### 为什么选择STOMP而不是Socket.io
1. **后端集成**: 项目后端使用Spring BootSTOMP是Spring WebSocket的标准协议
2. **消息队列**: STOMP天然支持消息队列模式,适合聊天和通知场景
3. **标准化**: STOMP是标准协议,不依赖特定实现
4. **轻量级**: 相比Socket.io更轻量,减少包体积
#### WebSocket工具类实现
```typescript
// utils/websocket.ts
import { Client } from '@stomp/stompjs'
import { envConfig } from '@/config/env'
export class WebSocketService {
private client: Client
private connected = false
private reconnectAttempts = 0
private maxReconnectAttempts = 5
private currentToken = ''
constructor() {
this.client = new Client({
// 使用原生WebSocket,支持Token认证
brokerURL: `${envConfig.wsBaseUrl}/ws`,
// 心跳检测
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
// 重连配置
reconnectDelay: 5000,
// 调试模式
debug: envConfig.debug ? console.log : undefined,
onConnect: () => {
this.connected = true
this.reconnectAttempts = 0
console.log('WebSocket连接成功')
},
onDisconnect: () => {
this.connected = false
console.log('WebSocket连接断开')
},
onStompError: (frame) => {
console.error('STOMP错误:', frame)
this.handleReconnect()
},
// WebSocket连接前的配置
beforeConnect: () => {
// 在WebSocket握手时添加Token
if (this.currentToken) {
this.client.configure({
connectHeaders: {
Authorization: `Bearer ${this.currentToken}`,
'X-Requested-With': 'XMLHttpRequest'
}
})
}
}
})
}
// 连接WebSocket
connect(token: string) {
this.currentToken = token
this.client.configure({
connectHeaders: {
Authorization: `Bearer ${token}`,
'X-Requested-With': 'XMLHttpRequest'
}
})
this.client.activate()
}
// 更新Token(用于Token刷新场景)
updateToken(newToken: string) {
this.currentToken = newToken
if (this.connected) {
// 断开当前连接
this.disconnect()
// 使用新Token重新连接
setTimeout(() => {
this.connect(newToken)
}, 1000)
}
}
// 断开连接
disconnect() {
this.client.deactivate()
}
// 订阅消息
subscribe(destination: string, callback: (message: any) => void) {
if (!this.connected) {
console.warn('WebSocket未连接')
return
}
return this.client.subscribe(destination, (message) => {
try {
const data = JSON.parse(message.body)
callback(data)
} catch (error) {
console.error('消息解析失败:', error)
}
})
}
// 发送消息
send(destination: string, body: any) {
if (!this.connected) {
console.warn('WebSocket未连接,消息将被缓存')
// 这里可以实现消息缓存逻辑
return
}
this.client.publish({
destination,
body: JSON.stringify(body)
})
}
// 处理重连
private handleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++
setTimeout(() => {
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`)
this.client.activate()
}, 5000 * this.reconnectAttempts)
}
}
}
```
#### 聊天功能使用示例
```typescript
// composables/useChat.ts
import { ref, onMounted, onUnmounted } from 'vue'
import { WebSocketService } from '@/utils/websocket'
import { useAuthStore } from '@/stores/auth'
export function useChat() {
const authStore = useAuthStore()
const wsService = new WebSocketService()
const messages = ref<any[]>([])
const isConnected = ref(false)
// 连接WebSocket
const connect = () => {
if (authStore.token) {
wsService.connect(authStore.token)
// 订阅个人消息
wsService.subscribe(`/user/${authStore.user.id}/queue/messages`, (message) => {
messages.value.push(message)
})
// 订阅聊天室消息
wsService.subscribe('/topic/chat', (message) => {
messages.value.push(message)
})
isConnected.value = true
}
}
// 发送消息
const sendMessage = (content: string, type: 'text' | 'image' = 'text') => {
const message = {
content,
type,
timestamp: Date.now(),
userId: authStore.user.id
}
wsService.send('/app/chat.send', message)
}
// 组件挂载时连接
onMounted(() => {
connect()
})
// 组件卸载时断开连接
onUnmounted(() => {
wsService.disconnect()
})
return {
messages,
isConnected,
sendMessage,
connect
}
}
```
### 10.6 WebSocket Token认证详解
#### Token传递方式
原生WebSocket + STOMP协议支持Token认证:
1. **WebSocket握手时传递** (推荐)
```typescript
// 在WebSocket连接建立时传递Token
brokerURL: `${envConfig.wsBaseUrl}/ws`,
connectHeaders: {
Authorization: `Bearer ${token}`,
'X-Requested-With': 'XMLHttpRequest'
}
```
2. **URL参数传递** (备选方案)
```typescript
// 如果后端不支持连接头部,可以通过URL传递
brokerURL: `${envConfig.wsBaseUrl}/ws?token=${token}`
```
**为什么移除SockJS**
- ❌ SockJS不支持在WebSocket握手时传递自定义请求头
- ❌ 无法在连接建立时进行Token认证
- ❌ 只能通过URL参数传递Token,安全性较低
- ✅ 原生WebSocket完全支持Token认证
- ✅ 现代浏览器WebSocket支持度已经很好
#### WebSocket方案对比
| 特性 | 原生WebSocket + STOMP | SockJS + STOMP | Socket.io |
|------|---------------------|----------------|-----------|
| **Token认证** | ✅ 支持请求头传递 | ❌ 不支持请求头 | ✅ 支持 |
| **浏览器兼容** | ✅ 现代浏览器完全支持 | ✅ 兼容性最好 | ✅ 兼容性好 |
| **包体积** | ✅ 最小 | ⚠️ 中等 | ❌ 最大 |
| **Spring Boot集成** | ✅ 原生支持 | ✅ 原生支持 | ❌ 需要额外配置 |
| **标准化** | ✅ 标准协议 | ✅ 标准协议 | ❌ 私有协议 |
| **安全性** | ✅ 握手时认证 | ⚠️ URL参数认证 | ✅ 握手时认证 |
**最终选择:原生WebSocket + STOMP**
- 🎯 **安全性优先**: 支持在握手时进行Token认证
- 🎯 **性能最优**: 无额外协议层,直接使用WebSocket
- 🎯 **标准化**: 基于标准STOMP协议
- 🎯 **轻量级**: 最小的包体积
- 🎯 **兼容性**: 现代浏览器支持度已经足够
#### Token刷新处理
```typescript
// stores/auth.ts - 认证状态管理
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { WebSocketService } from '@/utils/websocket'
export const useAuthStore = defineStore('auth', () => {
const token = ref<string>('')
const user = ref<any>(null)
const wsService = ref<WebSocketService | null>(null)
// 监听Token变化,自动更新WebSocket连接
watch(token, (newToken, oldToken) => {
if (newToken && newToken !== oldToken && wsService.value) {
wsService.value.updateToken(newToken)
}
})
// 登录
const login = async (credentials: LoginRequest) => {
try {
const response = await authApi.login(credentials)
token.value = response.token
user.value = response.user
// 登录成功后建立WebSocket连接
if (!wsService.value) {
wsService.value = new WebSocketService()
}
wsService.value.connect(token.value)
return response
} catch (error) {
throw error
}
}
// 登出
const logout = () => {
// 断开WebSocket连接
if (wsService.value) {
wsService.value.disconnect()
wsService.value = null
}
token.value = ''
user.value = null
}
// Token刷新
const refreshToken = async () => {
try {
const response = await authApi.refreshToken()
token.value = response.token
// watch会自动处理WebSocket重连
return response
} catch (error) {
// Token刷新失败,执行登出
logout()
throw error
}
}
return {
token,
user,
wsService,
login,
logout,
refreshToken
}
})
```
#### 后端WebSocket安全配置
对应的Spring Boot后端配置示例:
```java
// WebSocketConfig.java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*");
// 移除.withSockJS(),使用原生WebSocket支持Token认证
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new AuthChannelInterceptor());
}
}
// AuthChannelInterceptor.java - Token验证拦截器
@Component
public class AuthChannelInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String token = accessor.getFirstNativeHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
String jwt = token.substring(7);
// 验证JWT Token
if (jwtTokenProvider.validateToken(jwt)) {
String userId = jwtTokenProvider.getUserIdFromToken(jwt);
accessor.setUser(new StompPrincipal(userId));
} else {
throw new IllegalArgumentException("Invalid token");
}
} else {
throw new IllegalArgumentException("Missing token");
}
}
return message;
}
}
```
#### 错误处理和重连机制
```typescript
// 增强的WebSocket错误处理
export class WebSocketService {
private tokenExpiredCallback?: () => void
constructor(onTokenExpired?: () => void) {
this.tokenExpiredCallback = onTokenExpired
// ... 其他初始化代码
}
private handleStompError(frame: any) {
console.error('STOMP错误:', frame)
// 检查是否是Token相关错误
if (frame.headers && frame.headers.message) {
const errorMessage = frame.headers.message.toLowerCase()
if (errorMessage.includes('unauthorized') ||
errorMessage.includes('invalid token') ||
errorMessage.includes('token expired')) {
console.warn('Token认证失败,触发重新登录')
this.tokenExpiredCallback?.()
return
}
}
// 其他错误进行重连
this.handleReconnect()
}
}
// 在应用中使用
const authStore = useAuthStore()
const wsService = new WebSocketService(() => {
// Token过期回调
authStore.logout()
router.push('/login')
})
```
#### 安全最佳实践
1. **Token验证**: 每次WebSocket连接都验证Token有效性
2. **权限控制**: 基于用户角色限制订阅和发送权限
3. **连接限制**: 限制单用户的并发连接数
4. **消息加密**: 敏感消息内容加密传输
5. **审计日志**: 记录WebSocket连接和消息日志
这个方案完全支持Token认证,并且提供了完整的Token生命周期管理,包括刷新、过期处理和安全重连机制。
## 11. 测试策略
### 11.1 单元测试
```typescript
// tests/components/UserProfile.test.ts
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import UserProfile from '@/components/UserProfile.vue'
describe('UserProfile', () => {
it('renders user information correctly', () => {
const user = {
id: '1',
username: 'testuser',
nickname: 'Test User',
avatar: 'https://example.com/avatar.jpg'
}
const wrapper = mount(UserProfile, {
props: { user }
})
expect(wrapper.text()).toContain('Test User')
expect(wrapper.find('img').attributes('src')).toBe(user.avatar)
})
})
```
### 11.2 E2E测试
```typescript
// cypress/e2e/auth.cy.ts
describe('Authentication', () => {
it('should login successfully', () => {
cy.visit('/login')
cy.get('[data-cy=username]').type('testuser')
cy.get('[data-cy=password]').type('password123')
cy.get('[data-cy=login-btn]').click()
cy.url().should('include', '/dashboard')
cy.get('[data-cy=user-menu]').should('be.visible')
})
})
```
### 11.3 测试配置
```typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./tests/setup.ts']
},
resolve: {
alias: {
'@': '/src'
}
}
})
```
## 12. 国际化支持
### 12.1 i18n配置
- **vue-i18n**: `9.10.2` (国际化支持)
- **@intlify/unplugin-vue-i18n**: `4.0.0` (构建时优化)
```typescript
// i18n/index.ts
import { createI18n } from 'vue-i18n'
import zh from './locales/zh.json'
import en from './locales/en.json'
export const i18n = createI18n({
legacy: false,
locale: 'zh',
fallbackLocale: 'en',
messages: {
zh,
en
}
})
```
### 12.2 语言文件结构
```json
// i18n/locales/zh.json
{
"common": {
"confirm": "确认",
"cancel": "取消",
"save": "保存",
"delete": "删除"
},
"auth": {
"login": "登录",
"register": "注册",
"logout": "退出登录"
},
"chat": {
"sendMessage": "发送消息",
"typing": "正在输入...",
"offline": "离线"
}
}
```
## 13. 移动端适配
### 13.1 响应式断点
```css
/* tailwind.config.js */
module.exports = {
theme: {
screens: {
'xs': '475px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
}
}
}
```
### 13.2 移动端优化
- **触摸优化**: 44px最小触摸目标
- **手势支持**: 滑动、缩放、长按
- **性能优化**: 虚拟滚动、图片懒加载
- **离线支持**: Service Worker缓存
### 13.3 PWA配置
```typescript
// vite.config.ts PWA配置
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon.png'],
manifest: {
name: '情绪博物馆',
short_name: '情绪博物馆',
description: '记录情绪,分享心情的温暖空间',
theme_color: '#4A90E2',
background_color: '#ffffff',
display: 'standalone',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
})
```
## 14. 总结
本技术方案基于现代化的Vue3生态系统,采用TypeScript提供类型安全,使用Vite构建工具提升开发体验。方案涵盖了:
### 14.1 技术优势
- **现代化技术栈**: Vue3 + TypeScript + Vite
- **完整的工具链**: 从开发到部署的全流程支持
- **性能优化**: 代码分割、懒加载、缓存策略
- **开发体验**: 热更新、自动导入、类型检查
### 14.2 可扩展性
- **模块化设计**: 清晰的项目结构和职责分离
- **组件化开发**: 可复用的UI组件库
- **插件系统**: 支持功能扩展和第三方集成
- **国际化支持**: 多语言适配能力
### 14.3 维护性
- **代码规范**: ESLint + Prettier统一代码风格
- **测试覆盖**: 单元测试 + E2E测试
- **文档完善**: 组件文档和API文档
- **版本管理**: 语义化版本控制
### 14.4 部署方案
- **多环境支持**: local/dev/test/prod环境配置
- **容器化部署**: Docker + Nginx部署方案
- **CI/CD流程**: 自动化构建和部署
- **监控告警**: 错误监控和性能分析
这个技术方案能够很好地支持情绪博物馆Web端的所有功能需求,同时具备良好的可维护性和扩展性,为项目的长期发展奠定坚实基础。