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>
51 KiB
51 KiB
Telegram 管理系统架构文档
版本: v2.0.0
更新时间: 2024-01-20
维护者: 开发团队
📋 文档目录
🏗️ 系统概述
系统简介
Telegram 管理系统是一个基于现代Web技术栈构建的企业级营销管理平台,专注于Telegram渠道的用户管理、消息群发、营销活动等核心功能。
核心特性
- 🚀 高性能: 基于Vue 3 + Vite构建,支持秒级响应
- 🔒 安全可靠: 完整的权限控制体系,支持多角色管理
- 🌐 国际化: 支持中英文双语,可扩展更多语言
- 📱 响应式设计: 完美适配桌面端、平板和移动端
- ⚡ 实时通信: 基于WebSocket的实时状态同步
- 🔧 高度可配置: 支持灵活的系统配置和个性化定制
业务模块
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[数据统计]
🏛️ 技术架构
整体架构图
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/ # 全局类型定义
组件架构
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 进行状态管理,按模块划分:
// 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,
};
});
路由架构
采用动态路由 + 权限控制的方式:
// 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请求层:
// 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),
};
⚙️ 后端架构
分层架构
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. 用户认证模块
// 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. 权限控制模块
// 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. 消息处理模块
// 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);
}
}
数据模型
用户模型
// 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 },
});
消息任务模型
// 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 },
});
🗄️ 数据库设计
数据库架构
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
}
数据表设计
核心业务表
- 用户表 (users)
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)
);
- Telegram账号表 (telegram_accounts)
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)
);
- 消息任务表 (message_tasks)
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 数据结构设计
// 缓存键命名规范
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认证流程
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) 模型:
// 权限定义
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;
});
}
}
数据安全
敏感数据加密
// 数据加密服务
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安全防护
// 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.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.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配置
# 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部署
部署清单
# 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配置
# .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. 构建优化
// 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. 资源优化
// 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. 运行时优化
<!-- 组件懒加载 -->
<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. 数据库优化
-- 索引优化
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. 缓存策略
// 多级缓存
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. 消息队列优化
// 批量处理消息发送
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. 前端监控
// 性能监控 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. 后端监控
// 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. 基础设施监控
# 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. 告警规则
# 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配置
# 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:
# 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. 微服务架构
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. 数据库分片
// 数据库分片策略
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. 插件系统设计
// 插件接口定义
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. 动态配置系统
// 配置中心客户端
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等最新技术
- 微服务架构: 支持水平扩展和独立部署
- 安全性: 完整的认证授权体系和数据加密
- 高性能: 多级缓存、数据库优化、资源压缩
- 可观测性: 全面的监控、日志、告警体系
- 可扩展性: 插件化架构和动态配置管理
🚀 核心优势
- 开发效率: 基于Vben Admin框架,快速开发
- 用户体验: 响应式设计,实时通信
- 运维友好: 容器化部署,完善的监控
- 业务聚焦: 专注Telegram营销场景
- 技术先进: 采用最新的前端和后端技术
📈 未来规划
- AI智能化: 集成机器学习提升营销效果
- 多渠道整合: 支持更多社交媒体平台
- 边缘计算: 降低延时,提升用户体验
- 区块链集成: 提升数据安全和透明度
文档维护: 本文档应随系统演进持续更新,确保架构文档与实际系统保持一致。
版本管理: 重大架构变更时应更新版本号,并记录变更历史。
团队协作: 所有开发人员都应熟悉本架构文档,确保开发一致性。