Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2278 lines
51 KiB
Markdown
2278 lines
51 KiB
Markdown
# Telegram 管理系统架构文档
|
||
|
||
> **版本**: v2.0.0
|
||
> **更新时间**: 2024-01-20
|
||
> **维护者**: 开发团队
|
||
|
||
## 📋 文档目录
|
||
|
||
- [系统概述](#系统概述)
|
||
- [技术架构](#技术架构)
|
||
- [前端架构](#前端架构)
|
||
- [后端架构](#后端架构)
|
||
- [数据库设计](#数据库设计)
|
||
- [安全架构](#安全架构)
|
||
- [部署架构](#部署架构)
|
||
- [性能优化](#性能优化)
|
||
- [监控体系](#监控体系)
|
||
- [扩展性设计](#扩展性设计)
|
||
|
||
---
|
||
|
||
## 🏗️ 系统概述
|
||
|
||
### 系统简介
|
||
|
||
Telegram 管理系统是一个基于现代Web技术栈构建的企业级营销管理平台,专注于Telegram渠道的用户管理、消息群发、营销活动等核心功能。
|
||
|
||
### 核心特性
|
||
|
||
- 🚀 **高性能**: 基于Vue 3 + Vite构建,支持秒级响应
|
||
- 🔒 **安全可靠**: 完整的权限控制体系,支持多角色管理
|
||
- 🌐 **国际化**: 支持中英文双语,可扩展更多语言
|
||
- 📱 **响应式设计**: 完美适配桌面端、平板和移动端
|
||
- ⚡ **实时通信**: 基于WebSocket的实时状态同步
|
||
- 🔧 **高度可配置**: 支持灵活的系统配置和个性化定制
|
||
|
||
### 业务模块
|
||
|
||
```mermaid
|
||
graph TB
|
||
A[Telegram管理系统] --> B[用户管理]
|
||
A --> C[消息管理]
|
||
A --> D[营销中心]
|
||
A --> E[系统管理]
|
||
|
||
B --> B1[账号管理]
|
||
B --> B2[权限控制]
|
||
B --> B3[角色管理]
|
||
|
||
C --> C1[私信群发]
|
||
C --> C2[群组管理]
|
||
C --> C3[消息模板]
|
||
C --> C4[发送记录]
|
||
|
||
D --> D1[营销活动]
|
||
D --> D2[客户分析]
|
||
D --> D3[效果统计]
|
||
D --> D4[智能推荐]
|
||
|
||
E --> E1[系统配置]
|
||
E --> E2[日志管理]
|
||
E --> E3[监控报警]
|
||
E --> E4[数据统计]
|
||
```
|
||
|
||
---
|
||
|
||
## 🏛️ 技术架构
|
||
|
||
### 整体架构图
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "前端层"
|
||
A1[Vue 3 + TypeScript]
|
||
A2[Vben Admin Framework]
|
||
A3[Ant Design Vue]
|
||
A4[Vite 构建工具]
|
||
end
|
||
|
||
subgraph "API网关层"
|
||
B1[Nginx 反向代理]
|
||
B2[API Gateway]
|
||
B3[负载均衡]
|
||
B4[SSL终端]
|
||
end
|
||
|
||
subgraph "应用服务层"
|
||
C1[Node.js/Express]
|
||
C2[业务逻辑层]
|
||
C3[权限中间件]
|
||
C4[WebSocket 服务]
|
||
end
|
||
|
||
subgraph "数据存储层"
|
||
D1[MySQL 主数据库]
|
||
D2[Redis 缓存]
|
||
D3[MongoDB 日志]
|
||
D4[文件存储 OSS]
|
||
end
|
||
|
||
subgraph "外部服务"
|
||
E1[Telegram Bot API]
|
||
E2[短信服务商]
|
||
E3[邮件服务]
|
||
E4[CDN 服务]
|
||
end
|
||
|
||
A1 --> B1
|
||
A2 --> B2
|
||
B1 --> C1
|
||
B2 --> C2
|
||
C1 --> D1
|
||
C2 --> D2
|
||
C3 --> D3
|
||
C4 --> E1
|
||
```
|
||
|
||
### 技术栈选型
|
||
|
||
#### 前端技术栈
|
||
|
||
| 技术 | 版本 | 用途 | 选型理由 |
|
||
| --- | --- | --- | --- |
|
||
| Vue 3 | ^3.4.0 | 前端框架 | 组合式API,更好的TypeScript支持,性能提升 |
|
||
| TypeScript | ^5.0.0 | 类型系统 | 提供类型安全,减少运行时错误 |
|
||
| Vben Admin | ^5.5.8 | 管理后台框架 | 功能丰富,开发效率高,社区活跃 |
|
||
| Ant Design Vue | ^4.0.0 | UI组件库 | 组件丰富,设计规范,企业级应用首选 |
|
||
| Vite | ^5.0.0 | 构建工具 | 快速的开发服务器,优化的生产构建 |
|
||
| Pinia | ^2.1.0 | 状态管理 | Vue 3官方推荐,简洁的API |
|
||
| Vue Router | ^4.2.0 | 路由管理 | Vue 3官方路由器 |
|
||
| VueUse | ^10.0.0 | 组合式工具库 | 丰富的组合式函数 |
|
||
|
||
#### 后端技术栈
|
||
|
||
| 技术 | 版本 | 用途 | 选型理由 |
|
||
| --------- | ------- | ------------ | -------------------------- |
|
||
| Node.js | ^18.0.0 | 运行时环境 | 高性能,丰富的生态系统 |
|
||
| Express | ^4.18.0 | Web框架 | 成熟稳定,中间件丰富 |
|
||
| MySQL | ^8.0.0 | 关系型数据库 | ACID特性,适合业务数据存储 |
|
||
| Redis | ^7.0.0 | 内存数据库 | 高性能缓存,会话存储 |
|
||
| MongoDB | ^6.0.0 | 文档数据库 | 适合日志和非结构化数据 |
|
||
| Socket.io | ^4.7.0 | 实时通信 | 可靠的WebSocket实现 |
|
||
|
||
#### 开发工具
|
||
|
||
| 工具 | 版本 | 用途 |
|
||
| ---------- | ------- | -------------- |
|
||
| ESLint | ^8.50.0 | 代码规范检查 |
|
||
| Prettier | ^3.0.0 | 代码格式化 |
|
||
| Husky | ^8.0.0 | Git钩子管理 |
|
||
| Commitizen | ^4.3.0 | 规范化提交信息 |
|
||
| Playwright | ^1.40.0 | 端到端测试 |
|
||
|
||
---
|
||
|
||
## 🎨 前端架构
|
||
|
||
### 项目结构
|
||
|
||
```
|
||
apps/web-antd/
|
||
├── build/ # 构建相关配置
|
||
│ ├── compression.ts # 资源压缩配置
|
||
│ ├── image-optimizer.ts # 图片优化工具
|
||
│ ├── cdn-config.ts # CDN配置
|
||
│ └── optimize.ts # 构建优化脚本
|
||
├── docs/ # 项目文档
|
||
├── public/ # 静态资源
|
||
├── src/ # 源码目录
|
||
│ ├── api/ # API接口层
|
||
│ ├── assets/ # 静态资源
|
||
│ ├── components/ # 通用组件
|
||
│ ├── hooks/ # 组合式函数
|
||
│ ├── layouts/ # 布局组件
|
||
│ ├── locales/ # 国际化资源
|
||
│ ├── router/ # 路由配置
|
||
│ ├── stores/ # 状态管理
|
||
│ ├── types/ # 类型定义
|
||
│ ├── utils/ # 工具函数
|
||
│ └── views/ # 页面组件
|
||
├── tests/ # 测试文件
|
||
│ ├── e2e/ # 端到端测试
|
||
│ ├── global-setup.ts # 全局测试设置
|
||
│ └── global-teardown.ts # 全局测试清理
|
||
└── types/ # 全局类型定义
|
||
```
|
||
|
||
### 组件架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "页面层 (Views)"
|
||
A1[业务页面组件]
|
||
A2[路由页面组件]
|
||
end
|
||
|
||
subgraph "布局层 (Layouts)"
|
||
B1[主布局 Layout]
|
||
B2[侧边栏 Sidebar]
|
||
B3[头部 Header]
|
||
B4[内容区 Content]
|
||
end
|
||
|
||
subgraph "组件层 (Components)"
|
||
C1[业务组件]
|
||
C2[通用组件]
|
||
C3[基础组件]
|
||
end
|
||
|
||
subgraph "基础层 (Base)"
|
||
D1[Ant Design Vue]
|
||
D2[Vue 3 内置组件]
|
||
end
|
||
|
||
A1 --> B1
|
||
A2 --> B2
|
||
B1 --> C1
|
||
B2 --> C2
|
||
C1 --> D1
|
||
C2 --> D2
|
||
```
|
||
|
||
### 状态管理架构
|
||
|
||
采用 Pinia 进行状态管理,按模块划分:
|
||
|
||
```typescript
|
||
// stores/index.ts
|
||
export interface AppState {
|
||
// 用户状态
|
||
user: UserState;
|
||
// 权限状态
|
||
permission: PermissionState;
|
||
// 应用设置
|
||
settings: SettingsState;
|
||
// 业务数据
|
||
business: BusinessState;
|
||
}
|
||
|
||
// 状态模块示例
|
||
export const useUserStore = defineStore('user', () => {
|
||
const userInfo = ref<UserInfo | null>(null);
|
||
const token = ref<string>('');
|
||
const permissions = ref<string[]>([]);
|
||
|
||
const login = async (credentials: LoginCredentials) => {
|
||
// 登录逻辑
|
||
};
|
||
|
||
const logout = async () => {
|
||
// 登出逻辑
|
||
};
|
||
|
||
return {
|
||
userInfo,
|
||
token,
|
||
permissions,
|
||
login,
|
||
logout,
|
||
};
|
||
});
|
||
```
|
||
|
||
### 路由架构
|
||
|
||
采用动态路由 + 权限控制的方式:
|
||
|
||
```typescript
|
||
// router/index.ts
|
||
const router = createRouter({
|
||
history: createWebHistory(),
|
||
routes: [
|
||
// 静态路由
|
||
{
|
||
path: '/login',
|
||
component: LoginView,
|
||
meta: { requiresAuth: false },
|
||
},
|
||
// 动态路由(根据权限加载)
|
||
{
|
||
path: '/dashboard',
|
||
component: DashboardLayout,
|
||
meta: { requiresAuth: true },
|
||
children: dynamicRoutes,
|
||
},
|
||
],
|
||
});
|
||
|
||
// 路由守卫
|
||
router.beforeEach(async (to, from, next) => {
|
||
const userStore = useUserStore();
|
||
|
||
if (to.meta.requiresAuth && !userStore.token) {
|
||
next('/login');
|
||
} else {
|
||
// 权限验证
|
||
const hasPermission = await checkPermission(to);
|
||
hasPermission ? next() : next('/403');
|
||
}
|
||
});
|
||
```
|
||
|
||
### API架构
|
||
|
||
统一的API请求层:
|
||
|
||
```typescript
|
||
// api/base.ts
|
||
class ApiClient {
|
||
private instance: AxiosInstance;
|
||
|
||
constructor() {
|
||
this.instance = axios.create({
|
||
baseURL: import.meta.env.VITE_API_URL,
|
||
timeout: 10000,
|
||
});
|
||
|
||
this.setupInterceptors();
|
||
}
|
||
|
||
private setupInterceptors() {
|
||
// 请求拦截器
|
||
this.instance.interceptors.request.use((config) => {
|
||
const token = getToken();
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`;
|
||
}
|
||
return config;
|
||
});
|
||
|
||
// 响应拦截器
|
||
this.instance.interceptors.response.use(
|
||
(response) => response.data,
|
||
(error) => this.handleError(error),
|
||
);
|
||
}
|
||
}
|
||
|
||
// API模块化
|
||
export const accountApi = {
|
||
getList: (params: AccountListParams) =>
|
||
apiClient.get<AccountListResponse>('/accounts', { params }),
|
||
|
||
create: (data: CreateAccountData) =>
|
||
apiClient.post<Account>('/accounts', data),
|
||
|
||
update: (id: string, data: UpdateAccountData) =>
|
||
apiClient.put<Account>(`/accounts/${id}`, data),
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## ⚙️ 后端架构
|
||
|
||
### 分层架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "控制层 (Controller)"
|
||
A1[路由控制器]
|
||
A2[参数验证]
|
||
A3[权限验证]
|
||
end
|
||
|
||
subgraph "服务层 (Service)"
|
||
B1[业务逻辑]
|
||
B2[数据处理]
|
||
B3[外部服务调用]
|
||
end
|
||
|
||
subgraph "数据访问层 (DAO)"
|
||
C1[MySQL 操作]
|
||
C2[Redis 操作]
|
||
C3[MongoDB 操作]
|
||
end
|
||
|
||
subgraph "中间件层 (Middleware)"
|
||
D1[认证中间件]
|
||
D2[日志中间件]
|
||
D3[错误处理]
|
||
D4[限流中间件]
|
||
end
|
||
|
||
A1 --> B1
|
||
A2 --> B2
|
||
B1 --> C1
|
||
B2 --> C2
|
||
D1 --> A1
|
||
D2 --> A2
|
||
```
|
||
|
||
### 核心模块
|
||
|
||
#### 1. 用户认证模块
|
||
|
||
```javascript
|
||
// auth/auth.service.js
|
||
class AuthService {
|
||
async login(credentials) {
|
||
// 用户验证
|
||
const user = await this.validateUser(credentials);
|
||
|
||
// 生成token
|
||
const token = this.generateJWT(user);
|
||
|
||
// 记录登录日志
|
||
await this.logLoginActivity(user);
|
||
|
||
return { user, token };
|
||
}
|
||
|
||
async validateToken(token) {
|
||
try {
|
||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||
const user = await User.findById(decoded.userId);
|
||
return user;
|
||
} catch (error) {
|
||
throw new UnauthorizedError('Invalid token');
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 权限控制模块
|
||
|
||
```javascript
|
||
// permission/permission.service.js
|
||
class PermissionService {
|
||
async checkPermission(userId, resource, action) {
|
||
const userRoles = await this.getUserRoles(userId);
|
||
const permissions = await this.getRolePermissions(userRoles);
|
||
|
||
return permissions.some(
|
||
(permission) =>
|
||
permission.resource === resource && permission.action === action,
|
||
);
|
||
}
|
||
|
||
middleware() {
|
||
return async (req, res, next) => {
|
||
const { user } = req;
|
||
const { resource, action } = req.route.meta;
|
||
|
||
const hasPermission = await this.checkPermission(
|
||
user.id,
|
||
resource,
|
||
action,
|
||
);
|
||
|
||
if (hasPermission) {
|
||
next();
|
||
} else {
|
||
res.status(403).json({ error: 'Forbidden' });
|
||
}
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 消息处理模块
|
||
|
||
```javascript
|
||
// message/message.service.js
|
||
class MessageService {
|
||
async sendBulkMessage(taskData) {
|
||
const { templateId, targetUsers, scheduledTime } = taskData;
|
||
|
||
// 创建发送任务
|
||
const task = await this.createTask(taskData);
|
||
|
||
// 如果是定时发送
|
||
if (scheduledTime) {
|
||
await this.scheduleTask(task, scheduledTime);
|
||
} else {
|
||
await this.executeTask(task);
|
||
}
|
||
|
||
return task;
|
||
}
|
||
|
||
async executeTask(task) {
|
||
const { id, targetUsers, template } = task;
|
||
const results = [];
|
||
|
||
for (const user of targetUsers) {
|
||
try {
|
||
// 渲染消息模板
|
||
const message = this.renderTemplate(template, user);
|
||
|
||
// 发送消息
|
||
const result = await this.sendTelegramMessage(user.telegramId, message);
|
||
|
||
// 记录发送结果
|
||
await this.recordSendResult(task.id, user.id, result);
|
||
|
||
results.push({ userId: user.id, status: 'success' });
|
||
} catch (error) {
|
||
results.push({
|
||
userId: user.id,
|
||
status: 'failed',
|
||
error: error.message,
|
||
});
|
||
}
|
||
}
|
||
|
||
// 更新任务状态
|
||
await this.updateTaskStatus(id, 'completed', results);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 数据模型
|
||
|
||
#### 用户模型
|
||
|
||
```javascript
|
||
// models/User.js
|
||
const UserSchema = new Schema({
|
||
username: { type: String, required: true, unique: true },
|
||
email: { type: String, required: true, unique: true },
|
||
password: { type: String, required: true },
|
||
profile: {
|
||
firstName: String,
|
||
lastName: String,
|
||
avatar: String,
|
||
phone: String,
|
||
},
|
||
roles: [{ type: Schema.Types.ObjectId, ref: 'Role' }],
|
||
settings: {
|
||
language: { type: String, default: 'zh-CN' },
|
||
timezone: { type: String, default: 'Asia/Shanghai' },
|
||
notifications: {
|
||
email: { type: Boolean, default: true },
|
||
sms: { type: Boolean, default: false },
|
||
},
|
||
},
|
||
status: {
|
||
type: String,
|
||
enum: ['active', 'inactive', 'suspended'],
|
||
default: 'active',
|
||
},
|
||
lastLogin: Date,
|
||
createdAt: { type: Date, default: Date.now },
|
||
updatedAt: { type: Date, default: Date.now },
|
||
});
|
||
```
|
||
|
||
#### 消息任务模型
|
||
|
||
```javascript
|
||
// models/MessageTask.js
|
||
const MessageTaskSchema = new Schema({
|
||
name: { type: String, required: true },
|
||
description: String,
|
||
template: { type: Schema.Types.ObjectId, ref: 'MessageTemplate' },
|
||
targetUsers: [{ type: Schema.Types.ObjectId, ref: 'TelegramAccount' }],
|
||
scheduling: {
|
||
type: { type: String, enum: ['immediate', 'scheduled', 'recurring'] },
|
||
scheduledTime: Date,
|
||
recurrence: {
|
||
frequency: { type: String, enum: ['daily', 'weekly', 'monthly'] },
|
||
interval: Number,
|
||
endDate: Date,
|
||
},
|
||
},
|
||
status: {
|
||
type: String,
|
||
enum: ['pending', 'running', 'completed', 'failed', 'cancelled'],
|
||
default: 'pending',
|
||
},
|
||
progress: {
|
||
total: { type: Number, default: 0 },
|
||
sent: { type: Number, default: 0 },
|
||
failed: { type: Number, default: 0 },
|
||
percentage: { type: Number, default: 0 },
|
||
},
|
||
results: [
|
||
{
|
||
userId: { type: Schema.Types.ObjectId, ref: 'TelegramAccount' },
|
||
status: { type: String, enum: ['success', 'failed'] },
|
||
sentAt: Date,
|
||
error: String,
|
||
},
|
||
],
|
||
createdBy: { type: Schema.Types.ObjectId, ref: 'User' },
|
||
createdAt: { type: Date, default: Date.now },
|
||
updatedAt: { type: Date, default: Date.now },
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 🗄️ 数据库设计
|
||
|
||
### 数据库架构
|
||
|
||
```mermaid
|
||
erDiagram
|
||
User ||--o{ UserRole : has
|
||
Role ||--o{ RolePermission : has
|
||
Permission ||--o{ RolePermission : belongs
|
||
|
||
User ||--o{ MessageTask : creates
|
||
MessageTask ||--o{ MessageResult : contains
|
||
MessageTemplate ||--o{ MessageTask : used_in
|
||
|
||
TelegramAccount ||--o{ MessageResult : receives
|
||
TelegramAccount }o--|| User : managed_by
|
||
|
||
User {
|
||
string id PK
|
||
string username UK
|
||
string email UK
|
||
string password_hash
|
||
json profile
|
||
enum status
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
|
||
Role {
|
||
string id PK
|
||
string name UK
|
||
string description
|
||
boolean is_system
|
||
datetime created_at
|
||
}
|
||
|
||
Permission {
|
||
string id PK
|
||
string resource
|
||
string action
|
||
string description
|
||
}
|
||
|
||
TelegramAccount {
|
||
string id PK
|
||
string telegram_id UK
|
||
string username
|
||
string first_name
|
||
string last_name
|
||
string phone
|
||
enum status
|
||
json metadata
|
||
string managed_by FK
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
|
||
MessageTask {
|
||
string id PK
|
||
string name
|
||
string description
|
||
string template_id FK
|
||
json target_users
|
||
json scheduling
|
||
enum status
|
||
json progress
|
||
string created_by FK
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
|
||
MessageTemplate {
|
||
string id PK
|
||
string name
|
||
string content
|
||
json variables
|
||
enum type
|
||
string created_by FK
|
||
datetime created_at
|
||
datetime updated_at
|
||
}
|
||
```
|
||
|
||
### 数据表设计
|
||
|
||
#### 核心业务表
|
||
|
||
1. **用户表 (users)**
|
||
|
||
```sql
|
||
CREATE TABLE users (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
username VARCHAR(50) UNIQUE NOT NULL,
|
||
email VARCHAR(100) UNIQUE NOT NULL,
|
||
password_hash VARCHAR(255) NOT NULL,
|
||
profile JSON,
|
||
status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
|
||
last_login TIMESTAMP NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_username (username),
|
||
INDEX idx_email (email),
|
||
INDEX idx_status (status)
|
||
);
|
||
```
|
||
|
||
2. **Telegram账号表 (telegram_accounts)**
|
||
|
||
```sql
|
||
CREATE TABLE telegram_accounts (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
telegram_id VARCHAR(20) UNIQUE NOT NULL,
|
||
username VARCHAR(50),
|
||
first_name VARCHAR(50),
|
||
last_name VARCHAR(50),
|
||
phone VARCHAR(20),
|
||
status ENUM('active', 'inactive', 'blocked') DEFAULT 'active',
|
||
metadata JSON,
|
||
managed_by VARCHAR(36),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_telegram_id (telegram_id),
|
||
INDEX idx_username (username),
|
||
INDEX idx_managed_by (managed_by),
|
||
FOREIGN KEY (managed_by) REFERENCES users(id)
|
||
);
|
||
```
|
||
|
||
3. **消息任务表 (message_tasks)**
|
||
|
||
```sql
|
||
CREATE TABLE message_tasks (
|
||
id VARCHAR(36) PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
description TEXT,
|
||
template_id VARCHAR(36),
|
||
target_users JSON NOT NULL,
|
||
scheduling JSON,
|
||
status ENUM('pending', 'running', 'completed', 'failed', 'cancelled') DEFAULT 'pending',
|
||
progress JSON,
|
||
created_by VARCHAR(36) NOT NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_status (status),
|
||
INDEX idx_created_by (created_by),
|
||
INDEX idx_created_at (created_at),
|
||
FOREIGN KEY (created_by) REFERENCES users(id),
|
||
FOREIGN KEY (template_id) REFERENCES message_templates(id)
|
||
);
|
||
```
|
||
|
||
### 缓存策略
|
||
|
||
#### Redis 数据结构设计
|
||
|
||
```javascript
|
||
// 缓存键命名规范
|
||
const CACHE_KEYS = {
|
||
// 用户相关
|
||
USER_INFO: 'user:info:{userId}',
|
||
USER_PERMISSIONS: 'user:permissions:{userId}',
|
||
USER_SESSION: 'session:{sessionId}',
|
||
|
||
// 任务相关
|
||
TASK_STATUS: 'task:status:{taskId}',
|
||
TASK_PROGRESS: 'task:progress:{taskId}',
|
||
TASK_QUEUE: 'task:queue',
|
||
|
||
// 统计数据
|
||
DAILY_STATS: 'stats:daily:{date}',
|
||
USER_ACTIVITY: 'activity:user:{userId}',
|
||
|
||
// 配置缓存
|
||
SYSTEM_CONFIG: 'config:system',
|
||
RATE_LIMIT: 'rate_limit:{userId}:{endpoint}',
|
||
};
|
||
|
||
// 缓存操作示例
|
||
class CacheService {
|
||
async getUserInfo(userId) {
|
||
const cacheKey = CACHE_KEYS.USER_INFO.replace('{userId}', userId);
|
||
let userInfo = await redis.get(cacheKey);
|
||
|
||
if (!userInfo) {
|
||
userInfo = await User.findById(userId);
|
||
await redis.setex(cacheKey, 3600, JSON.stringify(userInfo)); // 1小时过期
|
||
} else {
|
||
userInfo = JSON.parse(userInfo);
|
||
}
|
||
|
||
return userInfo;
|
||
}
|
||
|
||
async setTaskProgress(taskId, progress) {
|
||
const cacheKey = CACHE_KEYS.TASK_PROGRESS.replace('{taskId}', taskId);
|
||
await redis.setex(cacheKey, 86400, JSON.stringify(progress)); // 24小时过期
|
||
|
||
// 发布进度更新事件
|
||
await redis.publish(
|
||
'task:progress:update',
|
||
JSON.stringify({
|
||
taskId,
|
||
progress,
|
||
}),
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔐 安全架构
|
||
|
||
### 认证与授权
|
||
|
||
#### JWT认证流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Client
|
||
participant Gateway
|
||
participant AuthService
|
||
participant Database
|
||
|
||
Client->>Gateway: 登录请求 (username, password)
|
||
Gateway->>AuthService: 验证凭据
|
||
AuthService->>Database: 查询用户信息
|
||
Database-->>AuthService: 返回用户数据
|
||
AuthService->>AuthService: 验证密码
|
||
AuthService->>AuthService: 生成JWT Token
|
||
AuthService-->>Gateway: 返回Token和用户信息
|
||
Gateway-->>Client: 返回认证结果
|
||
|
||
Note over Client: 后续请求携带Token
|
||
Client->>Gateway: API请求 (Header: Authorization)
|
||
Gateway->>AuthService: 验证Token
|
||
AuthService-->>Gateway: 返回用户信息
|
||
Gateway->>Gateway: 检查权限
|
||
Gateway-->>Client: 返回API响应
|
||
```
|
||
|
||
#### 权限控制模型
|
||
|
||
采用RBAC (Role-Based Access Control) 模型:
|
||
|
||
```typescript
|
||
// 权限定义
|
||
interface Permission {
|
||
id: string;
|
||
resource: string; // 资源类型: account, message, task
|
||
action: string; // 操作类型: create, read, update, delete
|
||
conditions?: {
|
||
// 条件限制
|
||
owner?: boolean; // 只能操作自己的资源
|
||
department?: string; // 部门限制
|
||
};
|
||
}
|
||
|
||
// 角色定义
|
||
interface Role {
|
||
id: string;
|
||
name: string;
|
||
permissions: Permission[];
|
||
inherits?: string[]; // 继承其他角色
|
||
}
|
||
|
||
// 权限检查
|
||
class PermissionChecker {
|
||
async checkPermission(
|
||
userId: string,
|
||
resource: string,
|
||
action: string,
|
||
resourceData?: any,
|
||
): Promise<boolean> {
|
||
const userRoles = await this.getUserRoles(userId);
|
||
const permissions = await this.flattenPermissions(userRoles);
|
||
|
||
return permissions.some((permission) => {
|
||
// 基础权限匹配
|
||
if (permission.resource !== resource || permission.action !== action) {
|
||
return false;
|
||
}
|
||
|
||
// 条件检查
|
||
if (permission.conditions?.owner && resourceData?.ownerId !== userId) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 数据安全
|
||
|
||
#### 敏感数据加密
|
||
|
||
```javascript
|
||
// 数据加密服务
|
||
class CryptoService {
|
||
constructor() {
|
||
this.algorithm = 'aes-256-gcm';
|
||
this.secretKey = process.env.ENCRYPTION_KEY;
|
||
}
|
||
|
||
encrypt(text) {
|
||
const iv = crypto.randomBytes(16);
|
||
const cipher = crypto.createCipher(this.algorithm, this.secretKey);
|
||
cipher.setAAD(Buffer.from('additional-data'));
|
||
|
||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||
encrypted += cipher.final('hex');
|
||
|
||
const authTag = cipher.getAuthTag();
|
||
|
||
return {
|
||
encrypted,
|
||
iv: iv.toString('hex'),
|
||
authTag: authTag.toString('hex'),
|
||
};
|
||
}
|
||
|
||
decrypt(encryptedData) {
|
||
const decipher = crypto.createDecipher(this.algorithm, this.secretKey);
|
||
decipher.setAAD(Buffer.from('additional-data'));
|
||
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
|
||
|
||
let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
|
||
decrypted += decipher.final('utf8');
|
||
|
||
return decrypted;
|
||
}
|
||
}
|
||
|
||
// 模型层数据加密
|
||
class User extends Model {
|
||
static encrypt = ['password', 'phone', 'email'];
|
||
|
||
static beforeSave(user) {
|
||
this.encrypt.forEach((field) => {
|
||
if (user[field] && user.isModified(field)) {
|
||
user[field] = cryptoService.encrypt(user[field]);
|
||
}
|
||
});
|
||
}
|
||
|
||
static afterFind(user) {
|
||
this.encrypt.forEach((field) => {
|
||
if (user[field] && typeof user[field] === 'object') {
|
||
user[field] = cryptoService.decrypt(user[field]);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### API安全防护
|
||
|
||
```javascript
|
||
// API限流中间件
|
||
class RateLimitMiddleware {
|
||
constructor(options = {}) {
|
||
this.windowMs = options.windowMs || 60000; // 1分钟
|
||
this.maxRequests = options.max || 100;
|
||
this.skipSuccessfulRequests = options.skipSuccessfulRequests || false;
|
||
}
|
||
|
||
middleware() {
|
||
return async (req, res, next) => {
|
||
const key = `rate_limit:${req.ip}:${req.route.path}`;
|
||
const current = await redis.get(key);
|
||
|
||
if (current && parseInt(current) >= this.maxRequests) {
|
||
return res.status(429).json({
|
||
error: 'Too Many Requests',
|
||
retryAfter: this.windowMs / 1000,
|
||
});
|
||
}
|
||
|
||
await redis
|
||
.multi()
|
||
.incr(key)
|
||
.expire(key, this.windowMs / 1000)
|
||
.exec();
|
||
|
||
next();
|
||
};
|
||
}
|
||
}
|
||
|
||
// XSS防护
|
||
app.use(
|
||
helmet({
|
||
contentSecurityPolicy: {
|
||
directives: {
|
||
defaultSrc: ["'self'"],
|
||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||
scriptSrc: ["'self'"],
|
||
imgSrc: ["'self'", 'data:', 'https:'],
|
||
},
|
||
},
|
||
hsts: {
|
||
maxAge: 31536000,
|
||
includeSubDomains: true,
|
||
preload: true,
|
||
},
|
||
}),
|
||
);
|
||
|
||
// SQL注入防护 (使用参数化查询)
|
||
class DatabaseService {
|
||
async findUser(conditions) {
|
||
const query = 'SELECT * FROM users WHERE username = ? AND status = ?';
|
||
return await this.connection.query(query, [
|
||
conditions.username,
|
||
conditions.status,
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 部署架构
|
||
|
||
### 容器化部署
|
||
|
||
#### Docker配置
|
||
|
||
```dockerfile
|
||
# Dockerfile.frontend
|
||
FROM node:18-alpine AS builder
|
||
|
||
WORKDIR /app
|
||
COPY package*.json ./
|
||
RUN npm install
|
||
|
||
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;"]
|
||
```
|
||
|
||
```dockerfile
|
||
# Dockerfile.backend
|
||
FROM node:18-alpine
|
||
|
||
WORKDIR /app
|
||
COPY package*.json ./
|
||
RUN npm install --only=production
|
||
|
||
COPY . .
|
||
EXPOSE 3000
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||
CMD curl -f http://localhost:3000/health || exit 1
|
||
|
||
CMD ["node", "server.js"]
|
||
```
|
||
|
||
#### Docker Compose配置
|
||
|
||
```yaml
|
||
# docker-compose.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
frontend:
|
||
build:
|
||
context: .
|
||
dockerfile: Dockerfile.frontend
|
||
ports:
|
||
- '80:80'
|
||
depends_on:
|
||
- backend
|
||
networks:
|
||
- app-network
|
||
|
||
backend:
|
||
build:
|
||
context: ./backend
|
||
dockerfile: Dockerfile.backend
|
||
ports:
|
||
- '3000:3000'
|
||
environment:
|
||
- NODE_ENV=production
|
||
- DB_HOST=mysql
|
||
- REDIS_HOST=redis
|
||
depends_on:
|
||
- mysql
|
||
- redis
|
||
networks:
|
||
- app-network
|
||
|
||
mysql:
|
||
image: mysql:8.0
|
||
environment:
|
||
- MYSQL_ROOT_PASSWORD=rootpassword
|
||
- MYSQL_DATABASE=telegram_system
|
||
- MYSQL_USER=app_user
|
||
- MYSQL_PASSWORD=app_password
|
||
volumes:
|
||
- mysql_data:/var/lib/mysql
|
||
- ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||
networks:
|
||
- app-network
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
command: redis-server --appendonly yes
|
||
volumes:
|
||
- redis_data:/data
|
||
networks:
|
||
- app-network
|
||
|
||
nginx:
|
||
image: nginx:alpine
|
||
ports:
|
||
- '443:443'
|
||
volumes:
|
||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||
- ./ssl:/etc/nginx/ssl
|
||
depends_on:
|
||
- frontend
|
||
- backend
|
||
networks:
|
||
- app-network
|
||
|
||
volumes:
|
||
mysql_data:
|
||
redis_data:
|
||
|
||
networks:
|
||
app-network:
|
||
driver: bridge
|
||
```
|
||
|
||
### Kubernetes部署
|
||
|
||
#### 部署清单
|
||
|
||
```yaml
|
||
# k8s/namespace.yaml
|
||
apiVersion: v1
|
||
kind: Namespace
|
||
metadata:
|
||
name: telegram-system
|
||
|
||
---
|
||
# k8s/frontend-deployment.yaml
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: frontend
|
||
namespace: telegram-system
|
||
spec:
|
||
replicas: 3
|
||
selector:
|
||
matchLabels:
|
||
app: frontend
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: frontend
|
||
spec:
|
||
containers:
|
||
- name: frontend
|
||
image: telegram-system/frontend:latest
|
||
ports:
|
||
- containerPort: 80
|
||
resources:
|
||
requests:
|
||
memory: '128Mi'
|
||
cpu: '100m'
|
||
limits:
|
||
memory: '256Mi'
|
||
cpu: '200m'
|
||
|
||
---
|
||
# k8s/backend-deployment.yaml
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: backend
|
||
namespace: telegram-system
|
||
spec:
|
||
replicas: 3
|
||
selector:
|
||
matchLabels:
|
||
app: backend
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: backend
|
||
spec:
|
||
containers:
|
||
- name: backend
|
||
image: telegram-system/backend:latest
|
||
ports:
|
||
- containerPort: 3000
|
||
env:
|
||
- name: NODE_ENV
|
||
value: 'production'
|
||
- name: DB_HOST
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: db-secret
|
||
key: host
|
||
resources:
|
||
requests:
|
||
memory: '256Mi'
|
||
cpu: '200m'
|
||
limits:
|
||
memory: '512Mi'
|
||
cpu: '500m'
|
||
livenessProbe:
|
||
httpGet:
|
||
path: /health
|
||
port: 3000
|
||
initialDelaySeconds: 30
|
||
periodSeconds: 10
|
||
readinessProbe:
|
||
httpGet:
|
||
path: /ready
|
||
port: 3000
|
||
initialDelaySeconds: 5
|
||
periodSeconds: 5
|
||
|
||
---
|
||
# k8s/service.yaml
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: frontend-service
|
||
namespace: telegram-system
|
||
spec:
|
||
selector:
|
||
app: frontend
|
||
ports:
|
||
- port: 80
|
||
targetPort: 80
|
||
type: LoadBalancer
|
||
|
||
---
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: backend-service
|
||
namespace: telegram-system
|
||
spec:
|
||
selector:
|
||
app: backend
|
||
ports:
|
||
- port: 3000
|
||
targetPort: 3000
|
||
type: ClusterIP
|
||
```
|
||
|
||
### CI/CD流水线
|
||
|
||
#### GitHub Actions配置
|
||
|
||
```yaml
|
||
# .github/workflows/deploy.yml
|
||
name: Build and Deploy
|
||
|
||
on:
|
||
push:
|
||
branches: [main, develop]
|
||
pull_request:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
test:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v3
|
||
with:
|
||
node-version: '18'
|
||
cache: 'npm'
|
||
|
||
- name: Install dependencies
|
||
run: npm install
|
||
|
||
- name: Run tests
|
||
run: npm test
|
||
|
||
- name: Run E2E tests
|
||
run: npm run test:e2e
|
||
|
||
build-and-push:
|
||
needs: test
|
||
runs-on: ubuntu-latest
|
||
if: github.ref == 'refs/heads/main'
|
||
|
||
steps:
|
||
- uses: actions/checkout@v3
|
||
|
||
- name: Login to Docker Hub
|
||
uses: docker/login-action@v2
|
||
with:
|
||
username: ${{ secrets.DOCKER_USERNAME }}
|
||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||
|
||
- name: Build and push frontend
|
||
uses: docker/build-push-action@v4
|
||
with:
|
||
context: .
|
||
file: ./Dockerfile.frontend
|
||
push: true
|
||
tags: telegram-system/frontend:${{ github.sha }},telegram-system/frontend:latest
|
||
|
||
- name: Build and push backend
|
||
uses: docker/build-push-action@v4
|
||
with:
|
||
context: ./backend
|
||
file: ./backend/Dockerfile
|
||
push: true
|
||
tags: telegram-system/backend:${{ github.sha }},telegram-system/backend:latest
|
||
|
||
deploy:
|
||
needs: build-and-push
|
||
runs-on: ubuntu-latest
|
||
if: github.ref == 'refs/heads/main'
|
||
|
||
steps:
|
||
- name: Deploy to Kubernetes
|
||
uses: azure/k8s-deploy@v1
|
||
with:
|
||
manifests: |
|
||
k8s/namespace.yaml
|
||
k8s/frontend-deployment.yaml
|
||
k8s/backend-deployment.yaml
|
||
k8s/service.yaml
|
||
images: |
|
||
telegram-system/frontend:${{ github.sha }}
|
||
telegram-system/backend:${{ github.sha }}
|
||
kubectl-version: 'latest'
|
||
```
|
||
|
||
---
|
||
|
||
## ⚡ 性能优化
|
||
|
||
### 前端性能优化
|
||
|
||
#### 1. 构建优化
|
||
|
||
```typescript
|
||
// vite.config.ts
|
||
export default defineConfig({
|
||
build: {
|
||
rollupOptions: {
|
||
output: {
|
||
// 代码分割
|
||
manualChunks: {
|
||
vendor: ['vue', 'vue-router', 'pinia'],
|
||
antd: ['ant-design-vue'],
|
||
utils: ['lodash', 'dayjs'],
|
||
},
|
||
},
|
||
},
|
||
// 压缩配置
|
||
minify: 'terser',
|
||
terserOptions: {
|
||
compress: {
|
||
drop_console: true,
|
||
drop_debugger: true,
|
||
},
|
||
},
|
||
},
|
||
|
||
// 开发服务器优化
|
||
server: {
|
||
hmr: {
|
||
overlay: false,
|
||
},
|
||
},
|
||
|
||
// 依赖预构建
|
||
optimizeDeps: {
|
||
include: ['vue', 'vue-router', 'pinia', 'ant-design-vue'],
|
||
},
|
||
});
|
||
```
|
||
|
||
#### 2. 资源优化
|
||
|
||
```typescript
|
||
// build/image-optimizer.ts
|
||
export async function optimizeImages(options: OptimizationOptions) {
|
||
const imageFiles = await glob('**/*.{png,jpg,jpeg,gif,svg}', {
|
||
cwd: options.inputDir,
|
||
});
|
||
|
||
for (const file of imageFiles) {
|
||
const inputPath = path.join(options.inputDir, file);
|
||
const outputPath = path.join(options.outputDir, file);
|
||
|
||
// PNG/JPEG压缩
|
||
if (file.match(/\.(png|jpe?g)$/)) {
|
||
await sharp(inputPath)
|
||
.resize(options.maxWidth, options.maxHeight, {
|
||
fit: 'inside',
|
||
withoutEnlargement: true,
|
||
})
|
||
.jpeg({ quality: options.quality })
|
||
.png({ compressionLevel: 9 })
|
||
.toFile(outputPath);
|
||
}
|
||
|
||
// 生成WebP格式
|
||
const webpPath = outputPath.replace(/\.(png|jpe?g)$/, '.webp');
|
||
await sharp(inputPath)
|
||
.webp({ quality: options.webpQuality })
|
||
.toFile(webpPath);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 运行时优化
|
||
|
||
```vue
|
||
<!-- 组件懒加载 -->
|
||
<template>
|
||
<div>
|
||
<!-- 虚拟滚动 -->
|
||
<VirtualList :items="largeDataset" :item-height="50" :visible-count="20">
|
||
<template #default="{ item }">
|
||
<ListItem :data="item" />
|
||
</template>
|
||
</VirtualList>
|
||
|
||
<!-- 图片懒加载 -->
|
||
<img v-lazy="imageUrl" :alt="description" loading="lazy" />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
// 组合式函数优化
|
||
const { data, loading } = usePagination({
|
||
api: accountApi.getList,
|
||
pageSize: 20,
|
||
immediate: true,
|
||
});
|
||
|
||
// 防抖搜索
|
||
const searchKeyword = ref('');
|
||
const debouncedSearch = useDebounceFn(() => {
|
||
// 执行搜索
|
||
}, 300);
|
||
|
||
watch(searchKeyword, debouncedSearch);
|
||
|
||
// 缓存计算结果
|
||
const expensiveComputation = computed(() => {
|
||
return heavyCalculation(data.value);
|
||
});
|
||
</script>
|
||
```
|
||
|
||
### 后端性能优化
|
||
|
||
#### 1. 数据库优化
|
||
|
||
```sql
|
||
-- 索引优化
|
||
CREATE INDEX idx_users_status_created ON users(status, created_at);
|
||
CREATE INDEX idx_message_tasks_status_priority ON message_tasks(status, priority, created_at);
|
||
CREATE INDEX idx_telegram_accounts_managed_status ON telegram_accounts(managed_by, status);
|
||
|
||
-- 分区表(按时间分区)
|
||
CREATE TABLE message_logs (
|
||
id BIGINT AUTO_INCREMENT,
|
||
task_id VARCHAR(36),
|
||
user_id VARCHAR(36),
|
||
message TEXT,
|
||
status ENUM('sent', 'failed'),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id, created_at)
|
||
) PARTITION BY RANGE (TO_DAYS(created_at)) (
|
||
PARTITION p2024_01 VALUES LESS THAN (TO_DAYS('2024-02-01')),
|
||
PARTITION p2024_02 VALUES LESS THAN (TO_DAYS('2024-03-01')),
|
||
-- ... 更多分区
|
||
);
|
||
```
|
||
|
||
#### 2. 缓存策略
|
||
|
||
```javascript
|
||
// 多级缓存
|
||
class CacheManager {
|
||
constructor() {
|
||
this.l1Cache = new Map(); // 内存缓存
|
||
this.l2Cache = redis; // Redis缓存
|
||
}
|
||
|
||
async get(key) {
|
||
// L1缓存
|
||
if (this.l1Cache.has(key)) {
|
||
return this.l1Cache.get(key);
|
||
}
|
||
|
||
// L2缓存
|
||
const l2Value = await this.l2Cache.get(key);
|
||
if (l2Value) {
|
||
const value = JSON.parse(l2Value);
|
||
this.l1Cache.set(key, value);
|
||
return value;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
async set(key, value, ttl = 3600) {
|
||
// 同时设置两级缓存
|
||
this.l1Cache.set(key, value);
|
||
await this.l2Cache.setex(key, ttl, JSON.stringify(value));
|
||
}
|
||
}
|
||
|
||
// 查询优化
|
||
class UserService {
|
||
async getUsers(filters, pagination) {
|
||
const cacheKey = `users:${JSON.stringify({ filters, pagination })}`;
|
||
|
||
// 尝试从缓存获取
|
||
let result = await cacheManager.get(cacheKey);
|
||
if (result) {
|
||
return result;
|
||
}
|
||
|
||
// 数据库查询优化
|
||
const query = User.find(filters)
|
||
.populate('roles', 'name permissions') // 只选择需要的字段
|
||
.limit(pagination.pageSize)
|
||
.skip(pagination.offset)
|
||
.sort({ createdAt: -1 });
|
||
|
||
result = await query.exec();
|
||
|
||
// 缓存结果
|
||
await cacheManager.set(cacheKey, result, 300); // 5分钟缓存
|
||
|
||
return result;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 消息队列优化
|
||
|
||
```javascript
|
||
// 批量处理消息发送
|
||
class MessageQueue {
|
||
constructor() {
|
||
this.queue = [];
|
||
this.batchSize = 100;
|
||
this.processingInterval = 1000; // 1秒
|
||
|
||
this.startProcessing();
|
||
}
|
||
|
||
add(message) {
|
||
this.queue.push(message);
|
||
}
|
||
|
||
startProcessing() {
|
||
setInterval(async () => {
|
||
if (this.queue.length === 0) return;
|
||
|
||
const batch = this.queue.splice(0, this.batchSize);
|
||
await this.processBatch(batch);
|
||
}, this.processingInterval);
|
||
}
|
||
|
||
async processBatch(messages) {
|
||
const promises = messages.map((message) =>
|
||
this.sendMessage(message).catch((error) => ({
|
||
message,
|
||
error: error.message,
|
||
})),
|
||
);
|
||
|
||
const results = await Promise.allSettled(promises);
|
||
|
||
// 记录失败的消息,用于重试
|
||
const failures = results
|
||
.filter((result) => result.status === 'rejected' || result.value.error)
|
||
.map((result) => result.value || result.reason);
|
||
|
||
if (failures.length > 0) {
|
||
await this.handleFailures(failures);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 监控体系
|
||
|
||
### 应用性能监控
|
||
|
||
#### 1. 前端监控
|
||
|
||
```typescript
|
||
// 性能监控 SDK
|
||
class PerformanceMonitor {
|
||
constructor() {
|
||
this.metrics = {
|
||
FCP: 0, // First Contentful Paint
|
||
LCP: 0, // Largest Contentful Paint
|
||
FID: 0, // First Input Delay
|
||
CLS: 0, // Cumulative Layout Shift
|
||
};
|
||
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// Web Vitals监控
|
||
import('web-vitals').then(({ getFCP, getLCP, getFID, getCLS }) => {
|
||
getFCP(this.onFCP.bind(this));
|
||
getLCP(this.onLCP.bind(this));
|
||
getFID(this.onFID.bind(this));
|
||
getCLS(this.onCLS.bind(this));
|
||
});
|
||
|
||
// 错误监控
|
||
window.addEventListener('error', this.onError.bind(this));
|
||
window.addEventListener(
|
||
'unhandledrejection',
|
||
this.onUnhandledRejection.bind(this),
|
||
);
|
||
}
|
||
|
||
onFCP(metric) {
|
||
this.metrics.FCP = metric.value;
|
||
this.sendMetric('FCP', metric);
|
||
}
|
||
|
||
onError(event) {
|
||
this.sendError({
|
||
type: 'javascript',
|
||
message: event.message,
|
||
filename: event.filename,
|
||
lineno: event.lineno,
|
||
colno: event.colno,
|
||
stack: event.error?.stack,
|
||
userAgent: navigator.userAgent,
|
||
url: window.location.href,
|
||
timestamp: Date.now(),
|
||
});
|
||
}
|
||
|
||
sendMetric(name, metric) {
|
||
fetch('/api/monitoring/metrics', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
name,
|
||
value: metric.value,
|
||
delta: metric.delta,
|
||
id: metric.id,
|
||
timestamp: Date.now(),
|
||
page: window.location.pathname,
|
||
}),
|
||
});
|
||
}
|
||
}
|
||
|
||
// Vue应用监控插件
|
||
const monitoringPlugin = {
|
||
install(app) {
|
||
// 全局错误处理
|
||
app.config.errorHandler = (err, vm, info) => {
|
||
console.error('Vue Error:', err, info);
|
||
|
||
// 发送错误报告
|
||
fetch('/api/monitoring/errors', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
type: 'vue',
|
||
message: err.message,
|
||
stack: err.stack,
|
||
info,
|
||
component: vm?.$options.name,
|
||
timestamp: Date.now(),
|
||
}),
|
||
});
|
||
};
|
||
|
||
// 路由变化监控
|
||
const router = app.config.globalProperties.$router;
|
||
router.beforeEach((to, from) => {
|
||
const startTime = performance.now();
|
||
|
||
router.afterEach(() => {
|
||
const endTime = performance.now();
|
||
const navigationTime = endTime - startTime;
|
||
|
||
// 记录路由性能
|
||
fetch('/api/monitoring/navigation', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
from: from.path,
|
||
to: to.path,
|
||
duration: navigationTime,
|
||
timestamp: Date.now(),
|
||
}),
|
||
});
|
||
});
|
||
});
|
||
},
|
||
};
|
||
```
|
||
|
||
#### 2. 后端监控
|
||
|
||
```javascript
|
||
// APM (Application Performance Monitoring)
|
||
const apm = require('elastic-apm-node').start({
|
||
serviceName: 'telegram-system-api',
|
||
secretToken: process.env.ELASTIC_APM_SECRET_TOKEN,
|
||
serverUrl: process.env.ELASTIC_APM_SERVER_URL,
|
||
});
|
||
|
||
// 自定义监控中间件
|
||
class MonitoringMiddleware {
|
||
static requestTracking() {
|
||
return (req, res, next) => {
|
||
const startTime = Date.now();
|
||
|
||
// 监听响应结束
|
||
res.on('finish', () => {
|
||
const duration = Date.now() - startTime;
|
||
|
||
// 记录请求指标
|
||
const metrics = {
|
||
method: req.method,
|
||
url: req.url,
|
||
statusCode: res.statusCode,
|
||
duration,
|
||
userAgent: req.get('User-Agent'),
|
||
ip: req.ip,
|
||
userId: req.user?.id,
|
||
timestamp: new Date(),
|
||
};
|
||
|
||
// 发送到监控系统
|
||
this.sendMetrics(metrics);
|
||
|
||
// 慢查询警报
|
||
if (duration > 5000) {
|
||
this.sendAlert('slow_request', metrics);
|
||
}
|
||
});
|
||
|
||
next();
|
||
};
|
||
}
|
||
|
||
static errorTracking() {
|
||
return (err, req, res, next) => {
|
||
// 记录错误
|
||
const errorInfo = {
|
||
message: err.message,
|
||
stack: err.stack,
|
||
url: req.url,
|
||
method: req.method,
|
||
userId: req.user?.id,
|
||
timestamp: Date.now(),
|
||
};
|
||
|
||
// 发送错误报告
|
||
this.sendError(errorInfo);
|
||
|
||
next(err);
|
||
};
|
||
}
|
||
|
||
static sendMetrics(metrics) {
|
||
// 发送到 Prometheus/Grafana
|
||
prometheus.httpRequestDuration.observe(
|
||
{ method: metrics.method, status_code: metrics.statusCode },
|
||
metrics.duration / 1000,
|
||
);
|
||
|
||
prometheus.httpRequestTotal.inc({
|
||
method: metrics.method,
|
||
status_code: metrics.statusCode,
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### 系统监控
|
||
|
||
#### 1. 基础设施监控
|
||
|
||
```yaml
|
||
# docker-compose.monitoring.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
prometheus:
|
||
image: prom/prometheus:latest
|
||
ports:
|
||
- '9090:9090'
|
||
volumes:
|
||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||
- prometheus_data:/prometheus
|
||
|
||
grafana:
|
||
image: grafana/grafana:latest
|
||
ports:
|
||
- '3001:3000'
|
||
environment:
|
||
- GF_SECURITY_ADMIN_PASSWORD=admin123
|
||
volumes:
|
||
- grafana_data:/var/lib/grafana
|
||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||
|
||
node-exporter:
|
||
image: prom/node-exporter:latest
|
||
ports:
|
||
- '9100:9100'
|
||
volumes:
|
||
- /proc:/host/proc:ro
|
||
- /sys:/host/sys:ro
|
||
- /:/rootfs:ro
|
||
|
||
alertmanager:
|
||
image: prom/alertmanager:latest
|
||
ports:
|
||
- '9093:9093'
|
||
volumes:
|
||
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
||
|
||
volumes:
|
||
prometheus_data:
|
||
grafana_data:
|
||
```
|
||
|
||
#### 2. 告警规则
|
||
|
||
```yaml
|
||
# prometheus/rules.yml
|
||
groups:
|
||
- name: telegram-system-alerts
|
||
rules:
|
||
# 系统资源告警
|
||
- alert: HighCPUUsage
|
||
expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: 'High CPU usage detected'
|
||
description: 'CPU usage is above 80% for more than 5 minutes'
|
||
|
||
- alert: HighMemoryUsage
|
||
expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 90
|
||
for: 5m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: 'High memory usage detected'
|
||
description: 'Memory usage is above 90% for more than 5 minutes'
|
||
|
||
# 应用告警
|
||
- alert: HighErrorRate
|
||
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) * 100 > 5
|
||
for: 2m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: 'High error rate detected'
|
||
description: 'Error rate is above 5% for more than 2 minutes'
|
||
|
||
- alert: SlowResponse
|
||
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: 'Slow response time detected'
|
||
description: '95th percentile response time is above 1 second'
|
||
|
||
# 业务告警
|
||
- alert: MessageSendingFailed
|
||
expr: rate(message_send_failures_total[5m]) > 10
|
||
for: 1m
|
||
labels:
|
||
severity: critical
|
||
annotations:
|
||
summary: 'High message sending failure rate'
|
||
description: 'Message sending failures are above 10 per second'
|
||
```
|
||
|
||
### 日志管理
|
||
|
||
#### ELK Stack配置
|
||
|
||
```yaml
|
||
# docker-compose.elk.yml
|
||
version: '3.8'
|
||
|
||
services:
|
||
elasticsearch:
|
||
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
|
||
environment:
|
||
- discovery.type=single-node
|
||
- 'ES_JAVA_OPTS=-Xms512m -Xmx512m'
|
||
ports:
|
||
- '9200:9200'
|
||
volumes:
|
||
- elasticsearch_data:/usr/share/elasticsearch/data
|
||
|
||
logstash:
|
||
image: docker.elastic.co/logstash/logstash:7.17.0
|
||
volumes:
|
||
- ./logstash/config:/usr/share/logstash/pipeline
|
||
ports:
|
||
- '5044:5044'
|
||
depends_on:
|
||
- elasticsearch
|
||
|
||
kibana:
|
||
image: docker.elastic.co/kibana/kibana:7.17.0
|
||
ports:
|
||
- '5601:5601'
|
||
environment:
|
||
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
|
||
depends_on:
|
||
- elasticsearch
|
||
|
||
volumes:
|
||
elasticsearch_data:
|
||
```
|
||
|
||
```ruby
|
||
# logstash/config/pipeline.conf
|
||
input {
|
||
beats {
|
||
port => 5044
|
||
}
|
||
}
|
||
|
||
filter {
|
||
if [fields][service] == "telegram-system" {
|
||
json {
|
||
source => "message"
|
||
}
|
||
|
||
date {
|
||
match => [ "timestamp", "ISO8601" ]
|
||
}
|
||
|
||
if [level] == "error" {
|
||
mutate {
|
||
add_tag => [ "error" ]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
output {
|
||
elasticsearch {
|
||
hosts => ["elasticsearch:9200"]
|
||
index => "telegram-system-%{+YYYY.MM.dd}"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 扩展性设计
|
||
|
||
### 水平扩展
|
||
|
||
#### 1. 微服务架构
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "API Gateway"
|
||
A1[Kong/Istio Gateway]
|
||
end
|
||
|
||
subgraph "Core Services"
|
||
B1[User Service]
|
||
B2[Auth Service]
|
||
B3[Message Service]
|
||
B4[Task Service]
|
||
B5[Analytics Service]
|
||
end
|
||
|
||
subgraph "Supporting Services"
|
||
C1[Notification Service]
|
||
C2[File Service]
|
||
C3[Config Service]
|
||
C4[Logging Service]
|
||
end
|
||
|
||
subgraph "Data Layer"
|
||
D1[User DB]
|
||
D2[Message DB]
|
||
D3[Analytics DB]
|
||
D4[Shared Cache]
|
||
end
|
||
|
||
A1 --> B1
|
||
A1 --> B2
|
||
A1 --> B3
|
||
A1 --> B4
|
||
A1 --> B5
|
||
|
||
B1 --> D1
|
||
B3 --> D2
|
||
B5 --> D3
|
||
B1 --> D4
|
||
B3 --> D4
|
||
```
|
||
|
||
#### 2. 数据库分片
|
||
|
||
```javascript
|
||
// 数据库分片策略
|
||
class DatabaseSharding {
|
||
constructor() {
|
||
this.shards = [
|
||
{ id: 'shard1', host: 'db1.example.com', range: [0, 1000000] },
|
||
{ id: 'shard2', host: 'db2.example.com', range: [1000001, 2000000] },
|
||
{ id: 'shard3', host: 'db3.example.com', range: [2000001, 3000000] },
|
||
];
|
||
}
|
||
|
||
getShardByUserId(userId) {
|
||
const numericId = parseInt(userId.replace(/\D/g, ''));
|
||
return this.shards.find(
|
||
(shard) => numericId >= shard.range[0] && numericId <= shard.range[1],
|
||
);
|
||
}
|
||
|
||
async queryUser(userId) {
|
||
const shard = this.getShardByUserId(userId);
|
||
const connection = await this.getConnection(shard.host);
|
||
return await connection.query('SELECT * FROM users WHERE id = ?', [userId]);
|
||
}
|
||
|
||
async queryAllShards(query, params) {
|
||
const promises = this.shards.map((shard) => {
|
||
const connection = this.getConnection(shard.host);
|
||
return connection.query(query, params);
|
||
});
|
||
|
||
const results = await Promise.all(promises);
|
||
return results.flat();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 插件化架构
|
||
|
||
#### 1. 插件系统设计
|
||
|
||
```typescript
|
||
// 插件接口定义
|
||
interface Plugin {
|
||
name: string;
|
||
version: string;
|
||
dependencies?: string[];
|
||
|
||
install(app: Application): void;
|
||
uninstall?(app: Application): void;
|
||
configure?(config: PluginConfig): void;
|
||
}
|
||
|
||
// 插件管理器
|
||
class PluginManager {
|
||
private plugins = new Map<string, Plugin>();
|
||
private loadOrder: string[] = [];
|
||
|
||
async loadPlugin(pluginPath: string): Promise<void> {
|
||
const plugin = await import(pluginPath);
|
||
|
||
// 检查依赖
|
||
if (plugin.dependencies) {
|
||
for (const dep of plugin.dependencies) {
|
||
if (!this.plugins.has(dep)) {
|
||
throw new Error(`Plugin dependency not found: ${dep}`);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 安装插件
|
||
plugin.install(this.app);
|
||
this.plugins.set(plugin.name, plugin);
|
||
this.loadOrder.push(plugin.name);
|
||
}
|
||
|
||
unloadPlugin(name: string): void {
|
||
const plugin = this.plugins.get(name);
|
||
if (plugin && plugin.uninstall) {
|
||
plugin.uninstall(this.app);
|
||
}
|
||
|
||
this.plugins.delete(name);
|
||
const index = this.loadOrder.indexOf(name);
|
||
if (index > -1) {
|
||
this.loadOrder.splice(index, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 插件示例:短信发送插件
|
||
export class SmsPlugin implements Plugin {
|
||
name = 'sms-plugin';
|
||
version = '1.0.0';
|
||
|
||
install(app: Application) {
|
||
// 注册短信服务
|
||
app.service('sms', new SmsService());
|
||
|
||
// 注册API路由
|
||
app.router.post('/api/sms/send', this.sendSms.bind(this));
|
||
|
||
// 注册事件监听器
|
||
app.on('user:created', this.sendWelcomeSms.bind(this));
|
||
}
|
||
|
||
private async sendSms(req: Request, res: Response) {
|
||
const { phone, message } = req.body;
|
||
const result = await app.service('sms').send(phone, message);
|
||
res.json(result);
|
||
}
|
||
|
||
private async sendWelcomeSms(user: User) {
|
||
if (user.phone) {
|
||
await app.service('sms').send(user.phone, '欢迎使用我们的服务!');
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 配置管理
|
||
|
||
#### 1. 动态配置系统
|
||
|
||
```typescript
|
||
// 配置中心客户端
|
||
class ConfigCenter {
|
||
private cache = new Map<string, any>();
|
||
private watchers = new Map<string, Function[]>();
|
||
|
||
constructor(private client: ConsulClient) {
|
||
this.startWatching();
|
||
}
|
||
|
||
async get<T>(key: string, defaultValue?: T): Promise<T> {
|
||
// 先从缓存获取
|
||
if (this.cache.has(key)) {
|
||
return this.cache.get(key);
|
||
}
|
||
|
||
// 从配置中心获取
|
||
try {
|
||
const value = await this.client.kv.get(key);
|
||
if (value) {
|
||
const parsed = JSON.parse(value);
|
||
this.cache.set(key, parsed);
|
||
return parsed;
|
||
}
|
||
} catch (error) {
|
||
console.error(`Failed to get config ${key}:`, error);
|
||
}
|
||
|
||
return defaultValue as T;
|
||
}
|
||
|
||
async set(key: string, value: any): Promise<void> {
|
||
await this.client.kv.set(key, JSON.stringify(value));
|
||
this.cache.set(key, value);
|
||
|
||
// 通知观察者
|
||
const watchers = this.watchers.get(key) || [];
|
||
watchers.forEach((callback) => callback(value));
|
||
}
|
||
|
||
watch(key: string, callback: (value: any) => void): void {
|
||
if (!this.watchers.has(key)) {
|
||
this.watchers.set(key, []);
|
||
}
|
||
this.watchers.get(key)!.push(callback);
|
||
}
|
||
|
||
private startWatching(): void {
|
||
// 定期检查配置变更
|
||
setInterval(async () => {
|
||
for (const key of this.cache.keys()) {
|
||
try {
|
||
const newValue = await this.client.kv.get(key);
|
||
const cached = this.cache.get(key);
|
||
|
||
if (JSON.stringify(newValue) !== JSON.stringify(cached)) {
|
||
this.cache.set(key, newValue);
|
||
|
||
const watchers = this.watchers.get(key) || [];
|
||
watchers.forEach((callback) => callback(newValue));
|
||
}
|
||
} catch (error) {
|
||
console.error(`Failed to watch config ${key}:`, error);
|
||
}
|
||
}
|
||
}, 5000);
|
||
}
|
||
}
|
||
|
||
// 使用示例
|
||
const config = new ConfigCenter(consulClient);
|
||
|
||
// 监听配置变更
|
||
config.watch('telegram.api.rate_limit', (newLimit) => {
|
||
console.log('Rate limit updated:', newLimit);
|
||
rateLimiter.updateLimit(newLimit);
|
||
});
|
||
|
||
// 获取配置
|
||
const rateLimit = await config.get('telegram.api.rate_limit', 100);
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 总结
|
||
|
||
本文档详细描述了Telegram管理系统的完整技术架构,涵盖了:
|
||
|
||
### 🏗️ 架构特点
|
||
|
||
- **现代化技术栈**: 采用Vue 3、TypeScript、Node.js等最新技术
|
||
- **微服务架构**: 支持水平扩展和独立部署
|
||
- **安全性**: 完整的认证授权体系和数据加密
|
||
- **高性能**: 多级缓存、数据库优化、资源压缩
|
||
- **可观测性**: 全面的监控、日志、告警体系
|
||
- **可扩展性**: 插件化架构和动态配置管理
|
||
|
||
### 🚀 核心优势
|
||
|
||
1. **开发效率**: 基于Vben Admin框架,快速开发
|
||
2. **用户体验**: 响应式设计,实时通信
|
||
3. **运维友好**: 容器化部署,完善的监控
|
||
4. **业务聚焦**: 专注Telegram营销场景
|
||
5. **技术先进**: 采用最新的前端和后端技术
|
||
|
||
### 📈 未来规划
|
||
|
||
- **AI智能化**: 集成机器学习提升营销效果
|
||
- **多渠道整合**: 支持更多社交媒体平台
|
||
- **边缘计算**: 降低延时,提升用户体验
|
||
- **区块链集成**: 提升数据安全和透明度
|
||
|
||
---
|
||
|
||
**文档维护**: 本文档应随系统演进持续更新,确保架构文档与实际系统保持一致。
|
||
|
||
**版本管理**: 重大架构变更时应更新版本号,并记录变更历史。
|
||
|
||
**团队协作**: 所有开发人员都应熟悉本架构文档,确保开发一致性。
|