本章将通过一个完整的企业级应用项目,整合前面所有章节学到的 TypeScript 知识和最佳实践。我们将构建一个现代化的企业资源管理系统(ERP),涵盖用户管理、权限控制、数据分析、实时通信等核心功能。
15.1 项目概述与架构设计
15.1.1 项目需求分析
// src/types/requirements.ts - 项目需求定义
// 功能需求
interface FunctionalRequirements {
userManagement: {
authentication: boolean;
authorization: boolean;
roleBasedAccess: boolean;
multiTenant: boolean;
};
dataManagement: {
crud: boolean;
search: boolean;
filtering: boolean;
sorting: boolean;
pagination: boolean;
export: boolean;
};
realTimeFeatures: {
notifications: boolean;
chat: boolean;
collaboration: boolean;
liveUpdates: boolean;
};
analytics: {
dashboard: boolean;
reports: boolean;
charts: boolean;
kpi: boolean;
};
}
// 非功能需求
interface NonFunctionalRequirements {
performance: {
responseTime: number; // ms
throughput: number; // requests/second
concurrentUsers: number;
};
scalability: {
horizontal: boolean;
vertical: boolean;
microservices: boolean;
};
security: {
encryption: boolean;
audit: boolean;
compliance: string[];
};
reliability: {
uptime: number; // percentage
backup: boolean;
disaster_recovery: boolean;
};
}
// 技术约束
interface TechnicalConstraints {
frontend: {
framework: 'React' | 'Vue' | 'Angular';
stateManagement: 'Redux' | 'Zustand' | 'MobX';
styling: 'CSS-in-JS' | 'Tailwind' | 'SCSS';
};
backend: {
runtime: 'Node.js' | 'Deno' | 'Bun';
framework: 'Express' | 'Fastify' | 'Koa';
database: 'PostgreSQL' | 'MongoDB' | 'MySQL';
};
infrastructure: {
cloud: 'AWS' | 'Azure' | 'GCP';
containerization: 'Docker' | 'Podman';
orchestration: 'Kubernetes' | 'Docker Swarm';
};
}
15.1.2 系统架构设计
// src/architecture/system-design.ts - 系统架构设计
// 微服务架构
interface MicroserviceArchitecture {
services: {
gateway: APIGatewayService;
auth: AuthenticationService;
user: UserManagementService;
notification: NotificationService;
analytics: AnalyticsService;
file: FileManagementService;
};
communication: {
synchronous: RESTfulAPI | GraphQLAPI;
asynchronous: MessageQueue;
};
dataStorage: {
relational: PostgreSQLDatabase;
document: MongoDBDatabase;
cache: RedisCache;
search: ElasticsearchIndex;
};
}
// API 网关服务
interface APIGatewayService {
routing: {
rules: RoutingRule[];
loadBalancing: LoadBalancingStrategy;
failover: FailoverStrategy;
};
security: {
authentication: AuthenticationMiddleware;
authorization: AuthorizationMiddleware;
rateLimiting: RateLimitingMiddleware;
cors: CORSMiddleware;
};
monitoring: {
logging: LoggingMiddleware;
metrics: MetricsMiddleware;
tracing: TracingMiddleware;
};
}
// 认证服务
interface AuthenticationService {
providers: {
local: LocalAuthProvider;
oauth: OAuthProvider[];
saml: SAMLProvider;
ldap: LDAPProvider;
};
tokens: {
jwt: JWTTokenManager;
refresh: RefreshTokenManager;
session: SessionManager;
};
security: {
encryption: EncryptionService;
hashing: HashingService;
validation: ValidationService;
};
}
// 用户管理服务
interface UserManagementService {
entities: {
user: UserEntity;
role: RoleEntity;
permission: PermissionEntity;
organization: OrganizationEntity;
};
operations: {
crud: CRUDOperations<UserEntity>;
search: SearchOperations<UserEntity>;
bulk: BulkOperations<UserEntity>;
};
policies: {
access: AccessPolicy[];
data: DataPolicy[];
retention: RetentionPolicy[];
};
}
15.1.3 前端架构设计
// src/frontend/architecture.ts - 前端架构设计
// 应用架构
interface FrontendArchitecture {
structure: {
presentation: PresentationLayer;
business: BusinessLogicLayer;
data: DataAccessLayer;
infrastructure: InfrastructureLayer;
};
patterns: {
stateManagement: StateManagementPattern;
routing: RoutingPattern;
componentComposition: ComponentCompositionPattern;
errorHandling: ErrorHandlingPattern;
};
optimization: {
bundling: BundlingStrategy;
caching: CachingStrategy;
lazyLoading: LazyLoadingStrategy;
performance: PerformanceOptimization;
};
}
// 表现层
interface PresentationLayer {
components: {
atoms: AtomicComponent[];
molecules: MolecularComponent[];
organisms: OrganismComponent[];
templates: TemplateComponent[];
pages: PageComponent[];
};
styling: {
system: DesignSystem;
tokens: DesignTokens;
themes: ThemeConfiguration[];
};
accessibility: {
standards: AccessibilityStandard[];
testing: AccessibilityTesting;
automation: AccessibilityAutomation;
};
}
// 业务逻辑层
interface BusinessLogicLayer {
services: {
domain: DomainService[];
application: ApplicationService[];
integration: IntegrationService[];
};
models: {
entities: DomainEntity[];
valueObjects: ValueObject[];
aggregates: Aggregate[];
};
workflows: {
processes: BusinessProcess[];
rules: BusinessRule[];
validations: ValidationRule[];
};
}
// 数据访问层
interface DataAccessLayer {
repositories: {
api: APIRepository[];
cache: CacheRepository[];
local: LocalStorageRepository[];
};
synchronization: {
offline: OfflineSyncStrategy;
conflict: ConflictResolutionStrategy;
merge: DataMergeStrategy;
};
optimization: {
batching: RequestBatching;
deduplication: RequestDeduplication;
prefetching: DataPrefetching;
};
}
15.2 核心模块实现
15.2.1 类型系统设计
// src/types/core.ts - 核心类型系统
// 基础类型
type ID = string;
type Timestamp = number;
type Email = `${string}@${string}.${string}`;
type PhoneNumber = `+${number}`;
type URL = `http${'s' | ''}://${string}`;
// 实体基类
interface BaseEntity {
id: ID;
createdAt: Timestamp;
updatedAt: Timestamp;
version: number;
}
// 软删除支持
interface SoftDeletable {
deletedAt?: Timestamp;
isDeleted: boolean;
}
// 审计支持
interface Auditable {
createdBy: ID;
updatedBy: ID;
deletedBy?: ID;
}
// 多租户支持
interface MultiTenant {
tenantId: ID;
}
// 完整实体类型
type Entity<T = {}> = BaseEntity & SoftDeletable & Auditable & MultiTenant & T;
// 用户实体
interface UserData {
email: Email;
username: string;
firstName: string;
lastName: string;
phoneNumber?: PhoneNumber;
avatar?: URL;
status: 'active' | 'inactive' | 'suspended';
lastLoginAt?: Timestamp;
emailVerifiedAt?: Timestamp;
phoneVerifiedAt?: Timestamp;
}
type User = Entity<UserData>;
// 角色实体
interface RoleData {
name: string;
description: string;
permissions: Permission[];
isSystem: boolean;
priority: number;
}
type Role = Entity<RoleData>;
// 权限实体
interface PermissionData {
resource: string;
action: string;
conditions?: Record<string, any>;
effect: 'allow' | 'deny';
}
type Permission = Entity<PermissionData>;
// 组织实体
interface OrganizationData {
name: string;
description: string;
parentId?: ID;
level: number;
path: string;
settings: OrganizationSettings;
}
type Organization = Entity<OrganizationData>;
interface OrganizationSettings {
timezone: string;
locale: string;
currency: string;
features: string[];
limits: {
users: number;
storage: number;
apiCalls: number;
};
}
15.2.2 状态管理系统
// src/store/index.ts - 状态管理系统
import { create } from 'zustand';
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
// 状态类型定义
interface AppState {
// 认证状态
auth: AuthState;
// 用户状态
user: UserState;
// 应用状态
app: ApplicationState;
// 通知状态
notifications: NotificationState;
// 操作
actions: AppActions;
}
// 认证状态
interface AuthState {
isAuthenticated: boolean;
user: User | null;
token: string | null;
refreshToken: string | null;
permissions: Permission[];
roles: Role[];
expiresAt: Timestamp | null;
}
// 用户状态
interface UserState {
profile: User | null;
preferences: UserPreferences;
organizations: Organization[];
currentOrganization: Organization | null;
}
// 应用状态
interface ApplicationState {
theme: 'light' | 'dark' | 'auto';
locale: string;
timezone: string;
sidebarCollapsed: boolean;
loading: Record<string, boolean>;
errors: Record<string, string | null>;
}
// 通知状态
interface NotificationState {
items: Notification[];
unreadCount: number;
settings: NotificationSettings;
}
// 操作接口
interface AppActions {
// 认证操作
auth: {
login: (credentials: LoginCredentials) => Promise<void>;
logout: () => Promise<void>;
refreshToken: () => Promise<void>;
updatePermissions: (permissions: Permission[]) => void;
};
// 用户操作
user: {
updateProfile: (profile: Partial<UserData>) => Promise<void>;
updatePreferences: (preferences: Partial<UserPreferences>) => void;
switchOrganization: (organizationId: ID) => Promise<void>;
};
// 应用操作
app: {
setTheme: (theme: ApplicationState['theme']) => void;
setLocale: (locale: string) => void;
toggleSidebar: () => void;
setLoading: (key: string, loading: boolean) => void;
setError: (key: string, error: string | null) => void;
};
// 通知操作
notifications: {
add: (notification: Omit<Notification, 'id' | 'createdAt'>) => void;
remove: (id: ID) => void;
markAsRead: (id: ID) => void;
markAllAsRead: () => void;
updateSettings: (settings: Partial<NotificationSettings>) => void;
};
}
// 创建状态存储
export const useAppStore = create<AppState>()()
devtools(
persist(
subscribeWithSelector(
immer((set, get) => ({
// 初始状态
auth: {
isAuthenticated: false,
user: null,
token: null,
refreshToken: null,
permissions: [],
roles: [],
expiresAt: null,
},
user: {
profile: null,
preferences: {
theme: 'auto',
locale: 'en-US',
timezone: 'UTC',
notifications: {
email: true,
push: true,
sms: false,
},
},
organizations: [],
currentOrganization: null,
},
app: {
theme: 'auto',
locale: 'en-US',
timezone: 'UTC',
sidebarCollapsed: false,
loading: {},
errors: {},
},
notifications: {
items: [],
unreadCount: 0,
settings: {
email: true,
push: true,
sms: false,
categories: {
system: true,
security: true,
updates: true,
marketing: false,
},
},
},
// 操作实现
actions: {
auth: {
login: async (credentials) => {
set((state) => {
state.app.loading.auth = true;
state.app.errors.auth = null;
});
try {
const response = await authService.login(credentials);
set((state) => {
state.auth.isAuthenticated = true;
state.auth.user = response.user;
state.auth.token = response.token;
state.auth.refreshToken = response.refreshToken;
state.auth.permissions = response.permissions;
state.auth.roles = response.roles;
state.auth.expiresAt = response.expiresAt;
state.user.profile = response.user;
state.app.loading.auth = false;
});
} catch (error) {
set((state) => {
state.app.loading.auth = false;
state.app.errors.auth = error.message;
});
throw error;
}
},
logout: async () => {
try {
await authService.logout();
} finally {
set((state) => {
state.auth.isAuthenticated = false;
state.auth.user = null;
state.auth.token = null;
state.auth.refreshToken = null;
state.auth.permissions = [];
state.auth.roles = [];
state.auth.expiresAt = null;
state.user.profile = null;
state.user.organizations = [];
state.user.currentOrganization = null;
});
}
},
refreshToken: async () => {
const { refreshToken } = get().auth;
if (!refreshToken) throw new Error('No refresh token available');
try {
const response = await authService.refreshToken(refreshToken);
set((state) => {
state.auth.token = response.token;
state.auth.expiresAt = response.expiresAt;
});
} catch (error) {
// 刷新失败,清除认证状态
get().actions.auth.logout();
throw error;
}
},
updatePermissions: (permissions) => {
set((state) => {
state.auth.permissions = permissions;
});
},
},
user: {
updateProfile: async (profile) => {
set((state) => {
state.app.loading.profile = true;
state.app.errors.profile = null;
});
try {
const updatedProfile = await userService.updateProfile(profile);
set((state) => {
state.user.profile = updatedProfile;
state.auth.user = updatedProfile;
state.app.loading.profile = false;
});
} catch (error) {
set((state) => {
state.app.loading.profile = false;
state.app.errors.profile = error.message;
});
throw error;
}
},
updatePreferences: (preferences) => {
set((state) => {
Object.assign(state.user.preferences, preferences);
});
},
switchOrganization: async (organizationId) => {
const organization = get().user.organizations.find(org => org.id === organizationId);
if (!organization) throw new Error('Organization not found');
set((state) => {
state.user.currentOrganization = organization;
});
// 重新获取权限
const permissions = await authService.getPermissions(organizationId);
get().actions.auth.updatePermissions(permissions);
},
},
app: {
setTheme: (theme) => {
set((state) => {
state.app.theme = theme;
});
},
setLocale: (locale) => {
set((state) => {
state.app.locale = locale;
});
},
toggleSidebar: () => {
set((state) => {
state.app.sidebarCollapsed = !state.app.sidebarCollapsed;
});
},
setLoading: (key, loading) => {
set((state) => {
state.app.loading[key] = loading;
});
},
setError: (key, error) => {
set((state) => {
state.app.errors[key] = error;
});
},
},
notifications: {
add: (notification) => {
const newNotification: Notification = {
...notification,
id: generateId(),
createdAt: Date.now(),
};
set((state) => {
state.notifications.items.unshift(newNotification);
if (!newNotification.read) {
state.notifications.unreadCount++;
}
});
},
remove: (id) => {
set((state) => {
const index = state.notifications.items.findIndex(item => item.id === id);
if (index !== -1) {
const notification = state.notifications.items[index];
if (!notification.read) {
state.notifications.unreadCount--;
}
state.notifications.items.splice(index, 1);
}
});
},
markAsRead: (id) => {
set((state) => {
const notification = state.notifications.items.find(item => item.id === id);
if (notification && !notification.read) {
notification.read = true;
state.notifications.unreadCount--;
}
});
},
markAllAsRead: () => {
set((state) => {
state.notifications.items.forEach(item => {
item.read = true;
});
state.notifications.unreadCount = 0;
});
},
updateSettings: (settings) => {
set((state) => {
Object.assign(state.notifications.settings, settings);
});
},
},
},
}))
),
{
name: 'app-store',
partialize: (state) => ({
user: {
preferences: state.user.preferences,
},
app: {
theme: state.app.theme,
locale: state.app.locale,
sidebarCollapsed: state.app.sidebarCollapsed,
},
notifications: {
settings: state.notifications.settings,
},
}),
}
)
)
);
// 选择器 Hooks
export const useAuth = () => useAppStore((state) => state.auth);
export const useUser = () => useAppStore((state) => state.user);
export const useApp = () => useAppStore((state) => state.app);
export const useNotifications = () => useAppStore((state) => state.notifications);
export const useActions = () => useAppStore((state) => state.actions);
// 类型定义
interface LoginCredentials {
email: string;
password: string;
rememberMe?: boolean;
}
interface UserPreferences {
theme: 'light' | 'dark' | 'auto';
locale: string;
timezone: string;
notifications: {
email: boolean;
push: boolean;
sms: boolean;
};
}
interface Notification {
id: ID;
title: string;
message: string;
type: 'info' | 'success' | 'warning' | 'error';
category: 'system' | 'security' | 'updates' | 'marketing';
read: boolean;
createdAt: Timestamp;
actionUrl?: URL;
actionText?: string;
}
interface NotificationSettings {
email: boolean;
push: boolean;
sms: boolean;
categories: {
system: boolean;
security: boolean;
updates: boolean;
marketing: boolean;
};
}
// 工具函数
function generateId(): ID {
return Math.random().toString(36).substr(2, 9);
}
// 服务接口(需要实现)
declare const authService: {
login: (credentials: LoginCredentials) => Promise<{
user: User;
token: string;
refreshToken: string;
permissions: Permission[];
roles: Role[];
expiresAt: Timestamp;
}>;
logout: () => Promise<void>;
refreshToken: (token: string) => Promise<{
token: string;
expiresAt: Timestamp;
}>;
getPermissions: (organizationId: ID) => Promise<Permission[]>;
};
declare const userService: {
updateProfile: (profile: Partial<UserData>) => Promise<User>;
};
15.2.3 权限控制系统
// src/auth/permission-system.ts - 权限控制系统
// 权限检查器
class PermissionChecker {
private permissions: Permission[];
private roles: Role[];
private context: PermissionContext;
constructor(permissions: Permission[], roles: Role[], context: PermissionContext) {
this.permissions = permissions;
this.roles = roles;
this.context = context;
}
// 检查是否有权限
can(resource: string, action: string, target?: any): boolean {
// 检查直接权限
const directPermission = this.checkDirectPermission(resource, action, target);
if (directPermission !== null) return directPermission;
// 检查角色权限
const rolePermission = this.checkRolePermission(resource, action, target);
if (rolePermission !== null) return rolePermission;
// 默认拒绝
return false;
}
// 检查多个权限(AND 逻辑)
canAll(checks: PermissionCheck[]): boolean {
return checks.every(check => this.can(check.resource, check.action, check.target));
}
// 检查多个权限(OR 逻辑)
canAny(checks: PermissionCheck[]): boolean {
return checks.some(check => this.can(check.resource, check.action, check.target));
}
// 获取资源的可用操作
getAvailableActions(resource: string, target?: any): string[] {
const actions = new Set<string>();
// 从权限中获取
this.permissions
.filter(p => p.resource === resource && this.evaluateConditions(p.conditions, target))
.forEach(p => {
if (p.effect === 'allow') {
actions.add(p.action);
} else {
actions.delete(p.action);
}
});
// 从角色中获取
this.roles.forEach(role => {
role.permissions
.filter(p => p.resource === resource && this.evaluateConditions(p.conditions, target))
.forEach(p => {
if (p.effect === 'allow') {
actions.add(p.action);
} else {
actions.delete(p.action);
}
});
});
return Array.from(actions);
}
// 过滤数据(基于权限)
filterData<T extends Record<string, any>>(data: T[], resource: string, action: string = 'read'): T[] {
return data.filter(item => this.can(resource, action, item));
}
private checkDirectPermission(resource: string, action: string, target?: any): boolean | null {
const relevantPermissions = this.permissions.filter(p =>
p.resource === resource && p.action === action
);
if (relevantPermissions.length === 0) return null;
// 评估条件并应用效果
let result: boolean | null = null;
for (const permission of relevantPermissions) {
if (this.evaluateConditions(permission.conditions, target)) {
result = permission.effect === 'allow';
}
}
return result;
}
private checkRolePermission(resource: string, action: string, target?: any): boolean | null {
let result: boolean | null = null;
for (const role of this.roles) {
const roleResult = this.checkRolePermissions(role.permissions, resource, action, target);
if (roleResult !== null) {
result = roleResult;
}
}
return result;
}
private checkRolePermissions(permissions: Permission[], resource: string, action: string, target?: any): boolean | null {
const relevantPermissions = permissions.filter(p =>
p.resource === resource && p.action === action
);
if (relevantPermissions.length === 0) return null;
let result: boolean | null = null;
for (const permission of relevantPermissions) {
if (this.evaluateConditions(permission.conditions, target)) {
result = permission.effect === 'allow';
}
}
return result;
}
private evaluateConditions(conditions?: Record<string, any>, target?: any): boolean {
if (!conditions) return true;
if (!target) return false;
return Object.entries(conditions).every(([key, expectedValue]) => {
const actualValue = this.getNestedValue(target, key);
return this.compareValues(actualValue, expectedValue);
});
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
private compareValues(actual: any, expected: any): boolean {
if (typeof expected === 'object' && expected !== null) {
// 支持操作符
if ('$eq' in expected) return actual === expected.$eq;
if ('$ne' in expected) return actual !== expected.$ne;
if ('$gt' in expected) return actual > expected.$gt;
if ('$gte' in expected) return actual >= expected.$gte;
if ('$lt' in expected) return actual < expected.$lt;
if ('$lte' in expected) return actual <= expected.$lte;
if ('$in' in expected) return expected.$in.includes(actual);
if ('$nin' in expected) return !expected.$nin.includes(actual);
if ('$regex' in expected) return new RegExp(expected.$regex).test(actual);
}
return actual === expected;
}
}
// 权限上下文
interface PermissionContext {
userId: ID;
organizationId: ID;
tenantId: ID;
ip?: string;
userAgent?: string;
timestamp: Timestamp;
}
// 权限检查项
interface PermissionCheck {
resource: string;
action: string;
target?: any;
}
// 权限装饰器
function RequirePermission(resource: string, action: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const permissionChecker = getPermissionChecker(); // 从上下文获取
if (!permissionChecker.can(resource, action)) {
throw new ForbiddenError(`Permission denied: ${resource}:${action}`);
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
// 权限 Hook
function usePermission() {
const auth = useAuth();
const permissionChecker = useMemo(() => {
if (!auth.isAuthenticated) {
return new PermissionChecker([], [], {
userId: '',
organizationId: '',
tenantId: '',
timestamp: Date.now(),
});
}
return new PermissionChecker(
auth.permissions,
auth.roles,
{
userId: auth.user!.id,
organizationId: auth.user!.tenantId, // 假设 tenantId 就是组织 ID
tenantId: auth.user!.tenantId,
timestamp: Date.now(),
}
);
}, [auth]);
return {
can: permissionChecker.can.bind(permissionChecker),
canAll: permissionChecker.canAll.bind(permissionChecker),
canAny: permissionChecker.canAny.bind(permissionChecker),
getAvailableActions: permissionChecker.getAvailableActions.bind(permissionChecker),
filterData: permissionChecker.filterData.bind(permissionChecker),
};
}
// 权限组件
interface PermissionGuardProps {
resource: string;
action: string;
target?: any;
fallback?: React.ReactNode;
children: React.ReactNode;
}
function PermissionGuard({ resource, action, target, fallback, children }: PermissionGuardProps) {
const { can } = usePermission();
if (!can(resource, action, target)) {
return <>{fallback || null}</>;
}
return <>{children}</>;
}
// 错误类
class ForbiddenError extends Error {
constructor(message: string) {
super(message);
this.name = 'ForbiddenError';
}
}
// 工具函数
function getPermissionChecker(): PermissionChecker {
// 从全局上下文或依赖注入容器获取
throw new Error('Not implemented');
}
export {
PermissionChecker,
RequirePermission,
usePermission,
PermissionGuard,
ForbiddenError,
};
export type {
PermissionContext,
PermissionCheck,
PermissionGuardProps,
};
15.3 高级功能实现
15.3.1 实时通信系统
// src/realtime/websocket-manager.ts - WebSocket 管理器
class WebSocketManager {
private ws: WebSocket | null = null;
private url: string;
private protocols?: string[];
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectDelay = 1000;
private heartbeatInterval = 30000;
private heartbeatTimer: NodeJS.Timeout | null = null;
private messageQueue: QueuedMessage[] = [];
private eventHandlers = new Map<string, Set<EventHandler>>();
private connectionState: ConnectionState = 'disconnected';
constructor(url: string, protocols?: string[]) {
this.url = url;
this.protocols = protocols;
}
// 连接
async connect(): Promise<void> {
if (this.connectionState === 'connected' || this.connectionState === 'connecting') {
return;
}
this.connectionState = 'connecting';
this.emit('connecting');
try {
this.ws = new WebSocket(this.url, this.protocols);
this.ws.onopen = this.handleOpen.bind(this);
this.ws.onmessage = this.handleMessage.bind(this);
this.ws.onclose = this.handleClose.bind(this);
this.ws.onerror = this.handleError.bind(this);
await this.waitForConnection();
} catch (error) {
this.connectionState = 'disconnected';
this.emit('error', error);
throw error;
}
}
// 断开连接
disconnect(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
if (this.ws) {
this.ws.close(1000, 'Client disconnect');
this.ws = null;
}
this.connectionState = 'disconnected';
this.reconnectAttempts = 0;
this.emit('disconnected');
}
// 发送消息
send<T = any>(type: string, data: T): void {
const message: WebSocketMessage<T> = {
id: generateId(),
type,
data,
timestamp: Date.now(),
};
if (this.connectionState === 'connected' && this.ws) {
this.ws.send(JSON.stringify(message));
} else {
// 排队等待连接
this.messageQueue.push({
message,
retries: 0,
maxRetries: 3,
});
}
}
// 订阅事件
on(event: string, handler: EventHandler): () => void {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, new Set());
}
this.eventHandlers.get(event)!.add(handler);
// 返回取消订阅函数
return () => {
this.eventHandlers.get(event)?.delete(handler);
};
}
// 一次性事件监听
once(event: string, handler: EventHandler): () => void {
const wrappedHandler = (...args: any[]) => {
handler(...args);
unsubscribe();
};
const unsubscribe = this.on(event, wrappedHandler);
return unsubscribe;
}
// 获取连接状态
getConnectionState(): ConnectionState {
return this.connectionState;
}
// 获取连接统计
getStats(): ConnectionStats {
return {
connectionState: this.connectionState,
reconnectAttempts: this.reconnectAttempts,
queuedMessages: this.messageQueue.length,
lastConnectedAt: this.lastConnectedAt,
lastDisconnectedAt: this.lastDisconnectedAt,
};
}
private lastConnectedAt: Timestamp | null = null;
private lastDisconnectedAt: Timestamp | null = null;
private async waitForConnection(): Promise<void> {
return new Promise((resolve, reject) => {
if (!this.ws) {
reject(new Error('WebSocket not initialized'));
return;
}
const timeout = setTimeout(() => {
reject(new Error('Connection timeout'));
}, 10000);
this.ws.addEventListener('open', () => {
clearTimeout(timeout);
resolve();
}, { once: true });
this.ws.addEventListener('error', (error) => {
clearTimeout(timeout);
reject(error);
}, { once: true });
});
}
private handleOpen(): void {
this.connectionState = 'connected';
this.lastConnectedAt = Date.now();
this.reconnectAttempts = 0;
this.emit('connected');
this.startHeartbeat();
this.processMessageQueue();
}
private handleMessage(event: MessageEvent): void {
try {
const message = JSON.parse(event.data) as WebSocketMessage;
this.emit('message', message);
this.emit(`message:${message.type}`, message);
} catch (error) {
this.emit('error', new Error('Failed to parse message'));
}
}
private handleClose(event: CloseEvent): void {
this.connectionState = 'disconnected';
this.lastDisconnectedAt = Date.now();
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
this.emit('disconnected', {
code: event.code,
reason: event.reason,
wasClean: event.wasClean,
});
// 自动重连(除非是正常关闭)
if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
this.scheduleReconnect();
}
}
private handleError(event: Event): void {
this.emit('error', new Error('WebSocket error'));
}
private scheduleReconnect(): void {
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
setTimeout(() => {
this.reconnectAttempts++;
this.emit('reconnecting', this.reconnectAttempts);
this.connect().catch(() => {
// 重连失败,会在 handleClose 中再次尝试
});
}, delay);
}
private startHeartbeat(): void {
this.heartbeatTimer = setInterval(() => {
if (this.connectionState === 'connected') {
this.send('ping', { timestamp: Date.now() });
}
}, this.heartbeatInterval);
}
private processMessageQueue(): void {
while (this.messageQueue.length > 0 && this.connectionState === 'connected') {
const queuedMessage = this.messageQueue.shift()!;
try {
this.ws!.send(JSON.stringify(queuedMessage.message));
} catch (error) {
// 发送失败,重新排队
if (queuedMessage.retries < queuedMessage.maxRetries) {
queuedMessage.retries++;
this.messageQueue.unshift(queuedMessage);
}
break;
}
}
}
private emit(event: string, ...args: any[]): void {
const handlers = this.eventHandlers.get(event);
if (handlers) {
handlers.forEach(handler => {
try {
handler(...args);
} catch (error) {
console.error(`Error in event handler for ${event}:`, error);
}
});
}
}
}
// 类型定义
type ConnectionState = 'disconnected' | 'connecting' | 'connected';
interface WebSocketMessage<T = any> {
id: string;
type: string;
data: T;
timestamp: Timestamp;
}
interface QueuedMessage {
message: WebSocketMessage;
retries: number;
maxRetries: number;
}
type EventHandler = (...args: any[]) => void;
interface ConnectionStats {
connectionState: ConnectionState;
reconnectAttempts: number;
queuedMessages: number;
lastConnectedAt: Timestamp | null;
lastDisconnectedAt: Timestamp | null;
}
// React Hook
function useWebSocket(url: string, protocols?: string[]) {
const [manager] = useState(() => new WebSocketManager(url, protocols));
const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');
const [stats, setStats] = useState<ConnectionStats>(manager.getStats());
useEffect(() => {
const unsubscribeConnecting = manager.on('connecting', () => {
setConnectionState('connecting');
});
const unsubscribeConnected = manager.on('connected', () => {
setConnectionState('connected');
setStats(manager.getStats());
});
const unsubscribeDisconnected = manager.on('disconnected', () => {
setConnectionState('disconnected');
setStats(manager.getStats());
});
const unsubscribeReconnecting = manager.on('reconnecting', () => {
setStats(manager.getStats());
});
return () => {
unsubscribeConnecting();
unsubscribeConnected();
unsubscribeDisconnected();
unsubscribeReconnecting();
};
}, [manager]);
useEffect(() => {
return () => {
manager.disconnect();
};
}, [manager]);
const connect = useCallback(() => manager.connect(), [manager]);
const disconnect = useCallback(() => manager.disconnect(), [manager]);
const send = useCallback(<T>(type: string, data: T) => manager.send(type, data), [manager]);
const on = useCallback((event: string, handler: EventHandler) => manager.on(event, handler), [manager]);
const once = useCallback((event: string, handler: EventHandler) => manager.once(event, handler), [manager]);
return {
connectionState,
stats,
connect,
disconnect,
send,
on,
once,
};
}
export {
WebSocketManager,
useWebSocket,
};
export type {
ConnectionState,
WebSocketMessage,
EventHandler,
ConnectionStats,
};
15.3.2 数据分析与可视化
// src/analytics/dashboard-system.ts - 仪表板系统
// 仪表板配置
interface DashboardConfig {
id: ID;
name: string;
description: string;
layout: DashboardLayout;
widgets: WidgetConfig[];
filters: FilterConfig[];
permissions: {
view: string[];
edit: string[];
share: string[];
};
settings: DashboardSettings;
}
// 布局配置
interface DashboardLayout {
type: 'grid' | 'flex' | 'masonry';
columns: number;
gap: number;
responsive: {
breakpoints: Record<string, number>;
columns: Record<string, number>;
};
}
// 组件配置
interface WidgetConfig {
id: ID;
type: WidgetType;
title: string;
position: {
x: number;
y: number;
width: number;
height: number;
};
dataSource: DataSourceConfig;
visualization: VisualizationConfig;
interactions: InteractionConfig[];
styling: StylingConfig;
}
// 组件类型
type WidgetType =
| 'chart'
| 'table'
| 'metric'
| 'text'
| 'image'
| 'map'
| 'calendar'
| 'timeline';
// 数据源配置
interface DataSourceConfig {
type: 'api' | 'websocket' | 'static' | 'computed';
endpoint?: string;
query?: QueryConfig;
transform?: TransformConfig;
cache?: CacheConfig;
refresh?: RefreshConfig;
}
// 查询配置
interface QueryConfig {
sql?: string;
graphql?: string;
rest?: {
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
params?: Record<string, any>;
body?: any;
};
filters?: FilterValue[];
aggregations?: AggregationConfig[];
sorting?: SortConfig[];
pagination?: PaginationConfig;
}
// 可视化配置
interface VisualizationConfig {
chartType?: ChartType;
axes?: AxesConfig;
series?: SeriesConfig[];
colors?: ColorConfig;
legend?: LegendConfig;
tooltip?: TooltipConfig;
animation?: AnimationConfig;
}
// 图表类型
type ChartType =
| 'line'
| 'bar'
| 'pie'
| 'doughnut'
| 'scatter'
| 'bubble'
| 'radar'
| 'polar'
| 'area'
| 'heatmap'
| 'treemap'
| 'sankey'
| 'funnel';
// 仪表板管理器
class DashboardManager {
private dashboards = new Map<ID, DashboardConfig>();
private widgets = new Map<ID, WidgetInstance>();
private dataCache = new Map<string, CachedData>();
private eventBus = new EventEmitter();
// 创建仪表板
async createDashboard(config: Omit<DashboardConfig, 'id'>): Promise<DashboardConfig> {
const dashboard: DashboardConfig = {
...config,
id: generateId(),
};
this.dashboards.set(dashboard.id, dashboard);
this.eventBus.emit('dashboard:created', dashboard);
return dashboard;
}
// 更新仪表板
async updateDashboard(id: ID, updates: Partial<DashboardConfig>): Promise<DashboardConfig> {
const dashboard = this.dashboards.get(id);
if (!dashboard) {
throw new Error(`Dashboard ${id} not found`);
}
const updatedDashboard = { ...dashboard, ...updates };
this.dashboards.set(id, updatedDashboard);
this.eventBus.emit('dashboard:updated', updatedDashboard);
return updatedDashboard;
}
// 删除仪表板
async deleteDashboard(id: ID): Promise<void> {
const dashboard = this.dashboards.get(id);
if (!dashboard) {
throw new Error(`Dashboard ${id} not found`);
}
// 清理相关组件
dashboard.widgets.forEach(widget => {
this.widgets.delete(widget.id);
});
this.dashboards.delete(id);
this.eventBus.emit('dashboard:deleted', { id });
}
// 添加组件
async addWidget(dashboardId: ID, config: Omit<WidgetConfig, 'id'>): Promise<WidgetConfig> {
const dashboard = this.dashboards.get(dashboardId);
if (!dashboard) {
throw new Error(`Dashboard ${dashboardId} not found`);
}
const widget: WidgetConfig = {
...config,
id: generateId(),
};
dashboard.widgets.push(widget);
// 创建组件实例
const instance = await this.createWidgetInstance(widget);
this.widgets.set(widget.id, instance);
this.eventBus.emit('widget:added', { dashboardId, widget });
return widget;
}
// 更新组件
async updateWidget(id: ID, updates: Partial<WidgetConfig>): Promise<WidgetConfig> {
const instance = this.widgets.get(id);
if (!instance) {
throw new Error(`Widget ${id} not found`);
}
const updatedConfig = { ...instance.config, ...updates };
instance.config = updatedConfig;
// 如果数据源配置改变,重新加载数据
if (updates.dataSource) {
await instance.loadData();
}
this.eventBus.emit('widget:updated', { widget: updatedConfig });
return updatedConfig;
}
// 删除组件
async removeWidget(dashboardId: ID, widgetId: ID): Promise<void> {
const dashboard = this.dashboards.get(dashboardId);
if (!dashboard) {
throw new Error(`Dashboard ${dashboardId} not found`);
}
const widgetIndex = dashboard.widgets.findIndex(w => w.id === widgetId);
if (widgetIndex === -1) {
throw new Error(`Widget ${widgetId} not found in dashboard`);
}
dashboard.widgets.splice(widgetIndex, 1);
this.widgets.delete(widgetId);
this.eventBus.emit('widget:removed', { dashboardId, widgetId });
}
// 获取仪表板数据
async getDashboardData(id: ID): Promise<DashboardData> {
const dashboard = this.dashboards.get(id);
if (!dashboard) {
throw new Error(`Dashboard ${id} not found`);
}
const widgetData = await Promise.all(
dashboard.widgets.map(async (widget) => {
const instance = this.widgets.get(widget.id);
if (!instance) {
throw new Error(`Widget instance ${widget.id} not found`);
}
return {
widgetId: widget.id,
data: await instance.getData(),
metadata: instance.getMetadata(),
};
})
);
return {
dashboard,
widgets: widgetData,
timestamp: Date.now(),
};
}
// 刷新仪表板数据
async refreshDashboard(id: ID): Promise<void> {
const dashboard = this.dashboards.get(id);
if (!dashboard) {
throw new Error(`Dashboard ${id} not found`);
}
await Promise.all(
dashboard.widgets.map(async (widget) => {
const instance = this.widgets.get(widget.id);
if (instance) {
await instance.loadData(true); // 强制刷新
}
})
);
this.eventBus.emit('dashboard:refreshed', { id });
}
// 导出仪表板
async exportDashboard(id: ID, format: 'json' | 'pdf' | 'png'): Promise<Blob> {
const dashboardData = await this.getDashboardData(id);
switch (format) {
case 'json':
return new Blob([JSON.stringify(dashboardData, null, 2)], {
type: 'application/json',
});
case 'pdf':
return await this.generatePDF(dashboardData);
case 'png':
return await this.generateImage(dashboardData);
default:
throw new Error(`Unsupported export format: ${format}`);
}
}
// 订阅事件
on(event: string, handler: (...args: any[]) => void): () => void {
this.eventBus.on(event, handler);
return () => this.eventBus.off(event, handler);
}
private async createWidgetInstance(config: WidgetConfig): Promise<WidgetInstance> {
const instance = new WidgetInstance(config, this.dataCache);
await instance.initialize();
return instance;
}
private async generatePDF(data: DashboardData): Promise<Blob> {
// 实现 PDF 生成逻辑
throw new Error('PDF generation not implemented');
}
private async generateImage(data: DashboardData): Promise<Blob> {
// 实现图片生成逻辑
throw new Error('Image generation not implemented');
}
}
// 组件实例
class WidgetInstance {
public config: WidgetConfig;
private dataCache: Map<string, CachedData>;
private data: any = null;
private metadata: WidgetMetadata = {
lastUpdated: null,
dataPoints: 0,
status: 'idle',
};
constructor(config: WidgetConfig, dataCache: Map<string, CachedData>) {
this.config = config;
this.dataCache = dataCache;
}
async initialize(): Promise<void> {
await this.loadData();
}
async loadData(forceRefresh = false): Promise<void> {
this.metadata.status = 'loading';
try {
const cacheKey = this.generateCacheKey();
if (!forceRefresh && this.dataCache.has(cacheKey)) {
const cached = this.dataCache.get(cacheKey)!;
if (Date.now() - cached.timestamp < (this.config.dataSource.cache?.ttl || 300000)) {
this.data = cached.data;
this.metadata.status = 'success';
this.metadata.lastUpdated = cached.timestamp;
return;
}
}
const rawData = await this.fetchData();
const transformedData = await this.transformData(rawData);
this.data = transformedData;
this.metadata.status = 'success';
this.metadata.lastUpdated = Date.now();
this.metadata.dataPoints = Array.isArray(transformedData) ? transformedData.length : 1;
// 缓存数据
this.dataCache.set(cacheKey, {
data: transformedData,
timestamp: Date.now(),
});
} catch (error) {
this.metadata.status = 'error';
throw error;
}
}
async getData(): Promise<any> {
return this.data;
}
getMetadata(): WidgetMetadata {
return { ...this.metadata };
}
private generateCacheKey(): string {
return `widget:${this.config.id}:${JSON.stringify(this.config.dataSource)}`;
}
private async fetchData(): Promise<any> {
const { dataSource } = this.config;
switch (dataSource.type) {
case 'api':
return await this.fetchFromAPI(dataSource);
case 'websocket':
return await this.fetchFromWebSocket(dataSource);
case 'static':
return dataSource.query?.rest?.body || [];
case 'computed':
return await this.computeData(dataSource);
default:
throw new Error(`Unsupported data source type: ${dataSource.type}`);
}
}
private async fetchFromAPI(dataSource: DataSourceConfig): Promise<any> {
if (!dataSource.endpoint) {
throw new Error('API endpoint is required');
}
const { query } = dataSource;
const response = await fetch(dataSource.endpoint, {
method: query?.rest?.method || 'GET',
headers: {
'Content-Type': 'application/json',
...query?.rest?.headers,
},
body: query?.rest?.body ? JSON.stringify(query.rest.body) : undefined,
});
if (!response.ok) {
throw new Error(`API request failed: ${response.statusText}`);
}
return await response.json();
}
private async fetchFromWebSocket(dataSource: DataSourceConfig): Promise<any> {
// WebSocket 数据获取实现
throw new Error('WebSocket data source not implemented');
}
private async computeData(dataSource: DataSourceConfig): Promise<any> {
// 计算数据实现
throw new Error('Computed data source not implemented');
}
private async transformData(data: any): Promise<any> {
const { transform } = this.config.dataSource;
if (!transform) return data;
// 应用数据转换
let result = data;
if (transform.filter) {
result = this.applyFilter(result, transform.filter);
}
if (transform.map) {
result = this.applyMapping(result, transform.map);
}
if (transform.aggregate) {
result = this.applyAggregation(result, transform.aggregate);
}
if (transform.sort) {
result = this.applySort(result, transform.sort);
}
return result;
}
private applyFilter(data: any[], filter: FilterConfig): any[] {
return data.filter(item => {
return filter.conditions.every(condition => {
const value = this.getNestedValue(item, condition.field);
return this.evaluateCondition(value, condition.operator, condition.value);
});
});
}
private applyMapping(data: any[], mapping: MappingConfig): any[] {
return data.map(item => {
const mapped: any = {};
Object.entries(mapping.fields).forEach(([targetField, sourceField]) => {
mapped[targetField] = this.getNestedValue(item, sourceField);
});
return mapped;
});
}
private applyAggregation(data: any[], aggregation: AggregationConfig): any {
const grouped = this.groupBy(data, aggregation.groupBy);
return Object.entries(grouped).map(([key, items]) => {
const result: any = { [aggregation.groupBy]: key };
aggregation.metrics.forEach(metric => {
const values = items.map(item => this.getNestedValue(item, metric.field));
result[metric.alias || metric.field] = this.calculateMetric(values, metric.function);
});
return result;
});
}
private applySort(data: any[], sort: SortConfig[]): any[] {
return data.sort((a, b) => {
for (const sortRule of sort) {
const aValue = this.getNestedValue(a, sortRule.field);
const bValue = this.getNestedValue(b, sortRule.field);
const comparison = this.compareValues(aValue, bValue);
if (comparison !== 0) {
return sortRule.direction === 'desc' ? -comparison : comparison;
}
}
return 0;
});
}
private getNestedValue(obj: any, path: string): any {
return path.split('.').reduce((current, key) => current?.[key], obj);
}
private evaluateCondition(value: any, operator: string, expected: any): boolean {
switch (operator) {
case 'eq': return value === expected;
case 'ne': return value !== expected;
case 'gt': return value > expected;
case 'gte': return value >= expected;
case 'lt': return value < expected;
case 'lte': return value <= expected;
case 'in': return Array.isArray(expected) && expected.includes(value);
case 'nin': return Array.isArray(expected) && !expected.includes(value);
case 'contains': return String(value).includes(String(expected));
case 'startsWith': return String(value).startsWith(String(expected));
case 'endsWith': return String(value).endsWith(String(expected));
default: return false;
}
}
private groupBy(data: any[], field: string): Record<string, any[]> {
return data.reduce((groups, item) => {
const key = this.getNestedValue(item, field);
if (!groups[key]) groups[key] = [];
groups[key].push(item);
return groups;
}, {});
}
private calculateMetric(values: any[], func: string): any {
const numbers = values.filter(v => typeof v === 'number');
switch (func) {
case 'sum': return numbers.reduce((sum, val) => sum + val, 0);
case 'avg': return numbers.length > 0 ? numbers.reduce((sum, val) => sum + val, 0) / numbers.length : 0;
case 'min': return numbers.length > 0 ? Math.min(...numbers) : null;
case 'max': return numbers.length > 0 ? Math.max(...numbers) : null;
case 'count': return values.length;
case 'countDistinct': return new Set(values).size;
default: return null;
}
}
private compareValues(a: any, b: any): number {
if (a === b) return 0;
if (a == null) return -1;
if (b == null) return 1;
if (typeof a === 'number' && typeof b === 'number') {
return a - b;
}
return String(a).localeCompare(String(b));
}
}
// 类型定义
interface DashboardData {
dashboard: DashboardConfig;
widgets: {
widgetId: ID;
data: any;
metadata: WidgetMetadata;
}[];
timestamp: Timestamp;
}
interface WidgetMetadata {
lastUpdated: Timestamp | null;
dataPoints: number;
status: 'idle' | 'loading' | 'success' | 'error';
}
interface CachedData {
data: any;
timestamp: Timestamp;
}
interface TransformConfig {
filter?: FilterConfig;
map?: MappingConfig;
aggregate?: AggregationConfig;
sort?: SortConfig[];
}
interface FilterConfig {
conditions: {
field: string;
operator: string;
value: any;
}[];
}
interface MappingConfig {
fields: Record<string, string>;
}
interface AggregationConfig {
groupBy: string;
metrics: {
field: string;
function: string;
alias?: string;
}[];
}
interface SortConfig {
field: string;
direction: 'asc' | 'desc';
}
interface CacheConfig {
ttl: number; // Time to live in milliseconds
maxSize?: number;
}
interface RefreshConfig {
interval?: number; // Auto refresh interval in milliseconds
onFocus?: boolean; // Refresh when window gains focus
onVisible?: boolean; // Refresh when widget becomes visible
}
interface FilterValue {
field: string;
operator: string;
value: any;
}
interface PaginationConfig {
page: number;
size: number;
}
interface AxesConfig {
x?: AxisConfig;
y?: AxisConfig;
}
interface AxisConfig {
label?: string;
min?: number;
max?: number;
type?: 'linear' | 'logarithmic' | 'category' | 'time';
}
interface SeriesConfig {
name: string;
data: string; // Field name
color?: string;
type?: ChartType;
}
interface ColorConfig {
scheme?: string;
custom?: string[];
}
interface LegendConfig {
show: boolean;
position: 'top' | 'bottom' | 'left' | 'right';
}
interface TooltipConfig {
show: boolean;
format?: string;
}
interface AnimationConfig {
enabled: boolean;
duration?: number;
easing?: string;
}
interface InteractionConfig {
type: 'click' | 'hover' | 'select';
action: 'filter' | 'navigate' | 'highlight' | 'custom';
target?: string;
params?: Record<string, any>;
}
interface StylingConfig {
theme?: string;
colors?: ColorConfig;
fonts?: FontConfig;
spacing?: SpacingConfig;
}
interface FontConfig {
family?: string;
size?: number;
weight?: string;
}
interface SpacingConfig {
padding?: number;
margin?: number;
}
interface DashboardSettings {
autoRefresh?: boolean;
refreshInterval?: number;
theme?: string;
timezone?: string;
locale?: string;
}
// React Hook
function useDashboard(dashboardId: ID) {
const [dashboard, setDashboard] = useState<DashboardConfig | null>(null);
const [data, setData] = useState<DashboardData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const manager = useMemo(() => new DashboardManager(), []);
const loadDashboard = useCallback(async () => {
setLoading(true);
setError(null);
try {
const dashboardData = await manager.getDashboardData(dashboardId);
setDashboard(dashboardData.dashboard);
setData(dashboardData);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
}, [manager, dashboardId]);
const refreshDashboard = useCallback(async () => {
try {
await manager.refreshDashboard(dashboardId);
await loadDashboard();
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
}
}, [manager, dashboardId, loadDashboard]);
useEffect(() => {
loadDashboard();
}, [loadDashboard]);
return {
dashboard,
data,
loading,
error,
refresh: refreshDashboard,
manager,
};
}
export {
DashboardManager,
WidgetInstance,
useDashboard,
};
export type {
DashboardConfig,
WidgetConfig,
DashboardData,
WidgetMetadata,
};
6.6 API 网关实现
// src/gateway/types.ts
interface RouteConfig {
id: ID;
path: string;
method: HttpMethod;
target: {
service: string;
path: string;
timeout?: number;
};
middleware?: MiddlewareConfig[];
rateLimit?: RateLimitConfig;
auth?: AuthConfig;
cache?: CacheConfig;
transform?: {
request?: TransformConfig;
response?: TransformConfig;
};
}
interface MiddlewareConfig {
name: string;
config?: Record<string, any>;
order: number;
}
interface RateLimitConfig {
windowMs: number;
maxRequests: number;
keyGenerator?: (req: Request) => string;
}
interface AuthConfig {
required: boolean;
roles?: string[];
permissions?: string[];
skipPaths?: string[];
}
interface TransformConfig {
headers?: Record<string, string>;
body?: {
add?: Record<string, any>;
remove?: string[];
transform?: Record<string, string>;
};
}
interface ServiceRegistry {
services: Map<string, ServiceInstance[]>;
healthChecks: Map<string, HealthCheckConfig>;
}
interface ServiceInstance {
id: ID;
name: string;
host: string;
port: number;
protocol: 'http' | 'https';
weight: number;
status: 'healthy' | 'unhealthy' | 'unknown';
metadata: Record<string, any>;
lastHealthCheck: Timestamp;
}
interface HealthCheckConfig {
path: string;
interval: number;
timeout: number;
retries: number;
successThreshold: number;
failureThreshold: number;
}
// API 网关核心类
class APIGateway {
private routes: Map<string, RouteConfig> = new Map();
private serviceRegistry: ServiceRegistry;
private loadBalancer: LoadBalancer;
private circuitBreaker: CircuitBreaker;
private rateLimiter: RateLimiter;
private cache: CacheManager;
private metrics: MetricsCollector;
private logger: Logger;
constructor(config: GatewayConfig) {
this.serviceRegistry = new ServiceRegistryImpl(config.registry);
this.loadBalancer = new LoadBalancer(config.loadBalancing);
this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
this.rateLimiter = new RateLimiter(config.rateLimit);
this.cache = new CacheManager(config.cache);
this.metrics = new MetricsCollector();
this.logger = new Logger('APIGateway');
}
async handleRequest(req: Request, res: Response): Promise<void> {
const startTime = Date.now();
const requestId = this.generateRequestId();
try {
// 1. 路由匹配
const route = this.matchRoute(req);
if (!route) {
return this.sendError(res, 404, 'Route not found');
}
// 2. 中间件执行
const middlewareResult = await this.executeMiddleware(req, res, route);
if (!middlewareResult.continue) {
return;
}
// 3. 认证授权
if (route.auth?.required) {
const authResult = await this.authenticate(req, route.auth);
if (!authResult.success) {
return this.sendError(res, 401, authResult.message);
}
req.user = authResult.user;
}
// 4. 限流检查
if (route.rateLimit) {
const rateLimitResult = await this.rateLimiter.check(req, route.rateLimit);
if (!rateLimitResult.allowed) {
return this.sendError(res, 429, 'Rate limit exceeded');
}
}
// 5. 缓存检查
if (route.cache && req.method === 'GET') {
const cachedResponse = await this.cache.get(this.generateCacheKey(req));
if (cachedResponse) {
return this.sendCachedResponse(res, cachedResponse);
}
}
// 6. 服务发现
const serviceInstance = await this.discoverService(route.target.service);
if (!serviceInstance) {
return this.sendError(res, 503, 'Service unavailable');
}
// 7. 请求转发
const response = await this.forwardRequest(req, route, serviceInstance);
// 8. 响应缓存
if (route.cache && req.method === 'GET' && response.status === 200) {
await this.cache.set(
this.generateCacheKey(req),
response,
route.cache.ttl
);
}
// 9. 响应转换
const transformedResponse = await this.transformResponse(response, route);
// 10. 发送响应
this.sendResponse(res, transformedResponse);
// 11. 记录指标
this.metrics.recordRequest({
route: route.id,
method: req.method,
status: response.status,
duration: Date.now() - startTime,
service: serviceInstance.name,
});
} catch (error) {
this.logger.error('Request handling failed', {
requestId,
error: error.message,
stack: error.stack,
});
this.sendError(res, 500, 'Internal server error');
}
}
private matchRoute(req: Request): RouteConfig | null {
const path = req.path;
const method = req.method.toLowerCase() as HttpMethod;
for (const [routeKey, route] of this.routes) {
if (route.method === method && this.pathMatches(path, route.path)) {
return route;
}
}
return null;
}
private pathMatches(requestPath: string, routePath: string): boolean {
// 支持路径参数和通配符
const routeRegex = routePath
.replace(/:[^/]+/g, '([^/]+)')
.replace(/\*/g, '.*');
return new RegExp(`^${routeRegex}$`).test(requestPath);
}
private async executeMiddleware(
req: Request,
res: Response,
route: RouteConfig
): Promise<{ continue: boolean }> {
if (!route.middleware) {
return { continue: true };
}
const sortedMiddleware = route.middleware.sort((a, b) => a.order - b.order);
for (const middleware of sortedMiddleware) {
const handler = this.getMiddlewareHandler(middleware.name);
if (!handler) {
this.logger.warn(`Middleware not found: ${middleware.name}`);
continue;
}
const result = await handler(req, res, middleware.config);
if (!result.continue) {
return { continue: false };
}
}
return { continue: true };
}
private async authenticate(
req: Request,
authConfig: AuthConfig
): Promise<{ success: boolean; user?: User; message?: string }> {
const token = this.extractToken(req);
if (!token) {
return { success: false, message: 'No token provided' };
}
try {
const user = await this.verifyToken(token);
// 角色检查
if (authConfig.roles && !this.hasRole(user, authConfig.roles)) {
return { success: false, message: 'Insufficient role' };
}
// 权限检查
if (authConfig.permissions && !this.hasPermission(user, authConfig.permissions)) {
return { success: false, message: 'Insufficient permissions' };
}
return { success: true, user };
} catch (error) {
return { success: false, message: 'Invalid token' };
}
}
private async discoverService(serviceName: string): Promise<ServiceInstance | null> {
const instances = this.serviceRegistry.services.get(serviceName);
if (!instances || instances.length === 0) {
return null;
}
const healthyInstances = instances.filter(instance => instance.status === 'healthy');
if (healthyInstances.length === 0) {
return null;
}
return this.loadBalancer.selectInstance(healthyInstances);
}
private async forwardRequest(
req: Request,
route: RouteConfig,
serviceInstance: ServiceInstance
): Promise<any> {
const targetUrl = `${serviceInstance.protocol}://${serviceInstance.host}:${serviceInstance.port}${route.target.path}`;
// 请求转换
const transformedReq = await this.transformRequest(req, route);
// 熔断器检查
const circuitBreakerKey = `${serviceInstance.name}:${route.target.path}`;
if (this.circuitBreaker.isOpen(circuitBreakerKey)) {
throw new Error('Circuit breaker is open');
}
try {
const response = await fetch(targetUrl, {
method: transformedReq.method,
headers: transformedReq.headers,
body: transformedReq.body,
timeout: route.target.timeout || 30000,
});
this.circuitBreaker.recordSuccess(circuitBreakerKey);
return response;
} catch (error) {
this.circuitBreaker.recordFailure(circuitBreakerKey);
throw error;
}
}
private async transformRequest(req: Request, route: RouteConfig): Promise<any> {
const transform = route.transform?.request;
if (!transform) return req;
const headers = { ...req.headers };
const body = req.body;
// 头部转换
if (transform.headers) {
Object.assign(headers, transform.headers);
}
// 请求体转换
let transformedBody = body;
if (transform.body) {
if (transform.body.add) {
transformedBody = { ...body, ...transform.body.add };
}
if (transform.body.remove) {
transform.body.remove.forEach(key => {
delete transformedBody[key];
});
}
if (transform.body.transform) {
Object.entries(transform.body.transform).forEach(([from, to]) => {
if (transformedBody[from] !== undefined) {
transformedBody[to] = transformedBody[from];
delete transformedBody[from];
}
});
}
}
return {
...req,
headers,
body: transformedBody,
};
}
private async transformResponse(response: any, route: RouteConfig): Promise<any> {
const transform = route.transform?.response;
if (!transform) return response;
// 响应转换逻辑
return response;
}
private generateRequestId(): string {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
private generateCacheKey(req: Request): string {
return `${req.method}:${req.path}:${JSON.stringify(req.query)}`;
}
private extractToken(req: Request): string | null {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
return null;
}
private async verifyToken(token: string): Promise<User> {
// JWT 验证逻辑
throw new Error('Not implemented');
}
private hasRole(user: User, requiredRoles: string[]): boolean {
return requiredRoles.some(role => user.roles.includes(role));
}
private hasPermission(user: User, requiredPermissions: string[]): boolean {
return requiredPermissions.every(permission =>
user.permissions.includes(permission)
);
}
private getMiddlewareHandler(name: string): MiddlewareHandler | null {
// 中间件处理器注册表
const handlers: Record<string, MiddlewareHandler> = {
cors: this.corsMiddleware,
logging: this.loggingMiddleware,
compression: this.compressionMiddleware,
};
return handlers[name] || null;
}
private corsMiddleware: MiddlewareHandler = async (req, res, config) => {
res.setHeader('Access-Control-Allow-Origin', config?.origin || '*');
res.setHeader('Access-Control-Allow-Methods', config?.methods || 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', config?.headers || 'Content-Type,Authorization');
if (req.method === 'OPTIONS') {
res.status(200).end();
return { continue: false };
}
return { continue: true };
};
private loggingMiddleware: MiddlewareHandler = async (req, res, config) => {
this.logger.info('Request received', {
method: req.method,
path: req.path,
userAgent: req.headers['user-agent'],
ip: req.ip,
});
return { continue: true };
};
private compressionMiddleware: MiddlewareHandler = async (req, res, config) => {
const acceptEncoding = req.headers['accept-encoding'] || '';
if (acceptEncoding.includes('gzip')) {
res.setHeader('Content-Encoding', 'gzip');
} else if (acceptEncoding.includes('deflate')) {
res.setHeader('Content-Encoding', 'deflate');
}
return { continue: true };
};
private sendResponse(res: Response, response: any): void {
res.status(response.status);
Object.entries(response.headers || {}).forEach(([key, value]) => {
res.setHeader(key, value as string);
});
res.json(response.body);
}
private sendCachedResponse(res: Response, cachedResponse: any): void {
res.setHeader('X-Cache', 'HIT');
this.sendResponse(res, cachedResponse);
}
private sendError(res: Response, status: number, message: string): void {
res.status(status).json({
error: {
code: status,
message,
timestamp: new Date().toISOString(),
},
});
}
}
// 负载均衡器
class LoadBalancer {
private strategy: LoadBalancingStrategy;
private roundRobinIndex: Map<string, number> = new Map();
constructor(config: LoadBalancingConfig) {
this.strategy = config.strategy || 'round-robin';
}
selectInstance(instances: ServiceInstance[]): ServiceInstance {
switch (this.strategy) {
case 'round-robin':
return this.roundRobin(instances);
case 'weighted':
return this.weighted(instances);
case 'least-connections':
return this.leastConnections(instances);
case 'random':
return this.random(instances);
default:
return instances[0];
}
}
private roundRobin(instances: ServiceInstance[]): ServiceInstance {
const key = instances.map(i => i.id).join(',');
const currentIndex = this.roundRobinIndex.get(key) || 0;
const nextIndex = (currentIndex + 1) % instances.length;
this.roundRobinIndex.set(key, nextIndex);
return instances[currentIndex];
}
private weighted(instances: ServiceInstance[]): ServiceInstance {
const totalWeight = instances.reduce((sum, instance) => sum + instance.weight, 0);
let random = Math.random() * totalWeight;
for (const instance of instances) {
random -= instance.weight;
if (random <= 0) {
return instance;
}
}
return instances[instances.length - 1];
}
private leastConnections(instances: ServiceInstance[]): ServiceInstance {
// 简化实现,实际应该跟踪连接数
return instances[0];
}
private random(instances: ServiceInstance[]): ServiceInstance {
const randomIndex = Math.floor(Math.random() * instances.length);
return instances[randomIndex];
}
}
// 熔断器
class CircuitBreaker {
private circuits: Map<string, CircuitState> = new Map();
private config: CircuitBreakerConfig;
constructor(config: CircuitBreakerConfig) {
this.config = config;
}
isOpen(key: string): boolean {
const circuit = this.getCircuit(key);
if (circuit.state === 'open') {
if (Date.now() - circuit.lastFailureTime > this.config.timeout) {
circuit.state = 'half-open';
return false;
}
return true;
}
return false;
}
recordSuccess(key: string): void {
const circuit = this.getCircuit(key);
circuit.successCount++;
circuit.failureCount = 0;
if (circuit.state === 'half-open' && circuit.successCount >= this.config.successThreshold) {
circuit.state = 'closed';
}
}
recordFailure(key: string): void {
const circuit = this.getCircuit(key);
circuit.failureCount++;
circuit.lastFailureTime = Date.now();
if (circuit.failureCount >= this.config.failureThreshold) {
circuit.state = 'open';
}
}
private getCircuit(key: string): CircuitState {
if (!this.circuits.has(key)) {
this.circuits.set(key, {
state: 'closed',
failureCount: 0,
successCount: 0,
lastFailureTime: 0,
});
}
return this.circuits.get(key)!;
}
}
// 类型定义
type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head';
type LoadBalancingStrategy = 'round-robin' | 'weighted' | 'least-connections' | 'random';
type CircuitState = 'closed' | 'open' | 'half-open';
interface GatewayConfig {
registry: ServiceRegistryConfig;
loadBalancing: LoadBalancingConfig;
circuitBreaker: CircuitBreakerConfig;
rateLimit: RateLimitConfig;
cache: CacheConfig;
}
interface ServiceRegistryConfig {
type: 'consul' | 'etcd' | 'zookeeper' | 'static';
endpoints: string[];
healthCheckInterval: number;
}
interface LoadBalancingConfig {
strategy: LoadBalancingStrategy;
}
interface CircuitBreakerConfig {
failureThreshold: number;
successThreshold: number;
timeout: number;
}
interface CircuitState {
state: 'closed' | 'open' | 'half-open';
failureCount: number;
successCount: number;
lastFailureTime: number;
}
type MiddlewareHandler = (
req: Request,
res: Response,
config?: Record<string, any>
) => Promise<{ continue: boolean }>;
export {
APIGateway,
LoadBalancer,
CircuitBreaker,
};
export type {
RouteConfig,
ServiceInstance,
GatewayConfig,
};
6.7 监控与日志系统
// src/monitoring/metrics.ts
interface MetricData {
name: string;
value: number;
timestamp: Timestamp;
labels: Record<string, string>;
type: MetricType;
}
type MetricType = 'counter' | 'gauge' | 'histogram' | 'summary';
class MetricsCollector {
private metrics: Map<string, MetricData[]> = new Map();
private exporters: MetricExporter[] = [];
private aggregationInterval: number = 60000; // 1 minute
constructor(config: MetricsConfig) {
this.aggregationInterval = config.aggregationInterval || 60000;
this.setupExporters(config.exporters);
this.startAggregation();
}
// 计数器指标
incrementCounter(name: string, labels: Record<string, string> = {}, value: number = 1): void {
this.recordMetric({
name,
value,
timestamp: Date.now(),
labels,
type: 'counter',
});
}
// 仪表盘指标
setGauge(name: string, value: number, labels: Record<string, string> = {}): void {
this.recordMetric({
name,
value,
timestamp: Date.now(),
labels,
type: 'gauge',
});
}
// 直方图指标
recordHistogram(name: string, value: number, labels: Record<string, string> = {}): void {
this.recordMetric({
name,
value,
timestamp: Date.now(),
labels,
type: 'histogram',
});
}
// 记录请求指标
recordRequest(data: {
route: string;
method: string;
status: number;
duration: number;
service: string;
}): void {
const labels = {
route: data.route,
method: data.method,
status: data.status.toString(),
service: data.service,
};
// 请求计数
this.incrementCounter('http_requests_total', labels);
// 请求持续时间
this.recordHistogram('http_request_duration_ms', data.duration, labels);
// 错误率
if (data.status >= 400) {
this.incrementCounter('http_requests_errors_total', labels);
}
}
// 记录业务指标
recordBusinessMetric(name: string, value: number, labels: Record<string, string> = {}): void {
this.recordMetric({
name: `business_${name}`,
value,
timestamp: Date.now(),
labels,
type: 'gauge',
});
}
private recordMetric(metric: MetricData): void {
const key = this.generateMetricKey(metric);
if (!this.metrics.has(key)) {
this.metrics.set(key, []);
}
this.metrics.get(key)!.push(metric);
}
private generateMetricKey(metric: MetricData): string {
const labelString = Object.entries(metric.labels)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => `${key}=${value}`)
.join(',');
return `${metric.name}{${labelString}}`;
}
private setupExporters(exporterConfigs: ExporterConfig[]): void {
exporterConfigs.forEach(config => {
switch (config.type) {
case 'prometheus':
this.exporters.push(new PrometheusExporter(config));
break;
case 'influxdb':
this.exporters.push(new InfluxDBExporter(config));
break;
case 'console':
this.exporters.push(new ConsoleExporter(config));
break;
}
});
}
private startAggregation(): void {
setInterval(() => {
this.aggregateAndExport();
}, this.aggregationInterval);
}
private async aggregateAndExport(): Promise<void> {
const aggregatedMetrics = this.aggregateMetrics();
for (const exporter of this.exporters) {
try {
await exporter.export(aggregatedMetrics);
} catch (error) {
console.error(`Failed to export metrics to ${exporter.constructor.name}:`, error);
}
}
// 清理旧数据
this.cleanupOldMetrics();
}
private aggregateMetrics(): AggregatedMetric[] {
const aggregated: AggregatedMetric[] = [];
for (const [key, metrics] of this.metrics) {
if (metrics.length === 0) continue;
const firstMetric = metrics[0];
const values = metrics.map(m => m.value);
switch (firstMetric.type) {
case 'counter':
aggregated.push({
name: firstMetric.name,
labels: firstMetric.labels,
type: 'counter',
value: values.reduce((sum, val) => sum + val, 0),
timestamp: Date.now(),
});
break;
case 'gauge':
aggregated.push({
name: firstMetric.name,
labels: firstMetric.labels,
type: 'gauge',
value: values[values.length - 1], // 最新值
timestamp: Date.now(),
});
break;
case 'histogram':
aggregated.push({
name: firstMetric.name,
labels: firstMetric.labels,
type: 'histogram',
value: this.calculateHistogramStats(values),
timestamp: Date.now(),
});
break;
}
}
return aggregated;
}
private calculateHistogramStats(values: number[]): HistogramStats {
const sorted = values.sort((a, b) => a - b);
const count = values.length;
const sum = values.reduce((acc, val) => acc + val, 0);
return {
count,
sum,
min: sorted[0],
max: sorted[count - 1],
avg: sum / count,
p50: this.percentile(sorted, 0.5),
p90: this.percentile(sorted, 0.9),
p95: this.percentile(sorted, 0.95),
p99: this.percentile(sorted, 0.99),
};
}
private percentile(sortedValues: number[], p: number): number {
const index = Math.ceil(sortedValues.length * p) - 1;
return sortedValues[Math.max(0, index)];
}
private cleanupOldMetrics(): void {
const cutoff = Date.now() - this.aggregationInterval * 2;
for (const [key, metrics] of this.metrics) {
const filtered = metrics.filter(m => m.timestamp > cutoff);
if (filtered.length === 0) {
this.metrics.delete(key);
} else {
this.metrics.set(key, filtered);
}
}
}
}
// 日志系统
class Logger {
private context: string;
private level: LogLevel;
private appenders: LogAppender[] = [];
constructor(context: string, config: LoggerConfig = {}) {
this.context = context;
this.level = config.level || 'info';
this.setupAppenders(config.appenders || []);
}
debug(message: string, meta?: Record<string, any>): void {
this.log('debug', message, meta);
}
info(message: string, meta?: Record<string, any>): void {
this.log('info', message, meta);
}
warn(message: string, meta?: Record<string, any>): void {
this.log('warn', message, meta);
}
error(message: string, meta?: Record<string, any>): void {
this.log('error', message, meta);
}
fatal(message: string, meta?: Record<string, any>): void {
this.log('fatal', message, meta);
}
private log(level: LogLevel, message: string, meta?: Record<string, any>): void {
if (!this.shouldLog(level)) {
return;
}
const logEntry: LogEntry = {
timestamp: new Date().toISOString(),
level,
context: this.context,
message,
meta: meta || {},
traceId: this.getTraceId(),
};
this.appenders.forEach(appender => {
try {
appender.append(logEntry);
} catch (error) {
console.error('Failed to append log:', error);
}
});
}
private shouldLog(level: LogLevel): boolean {
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'fatal'];
const currentLevelIndex = levels.indexOf(this.level);
const messageLevelIndex = levels.indexOf(level);
return messageLevelIndex >= currentLevelIndex;
}
private setupAppenders(appenderConfigs: AppenderConfig[]): void {
appenderConfigs.forEach(config => {
switch (config.type) {
case 'console':
this.appenders.push(new ConsoleAppender(config));
break;
case 'file':
this.appenders.push(new FileAppender(config));
break;
case 'elasticsearch':
this.appenders.push(new ElasticsearchAppender(config));
break;
case 'syslog':
this.appenders.push(new SyslogAppender(config));
break;
}
});
}
private getTraceId(): string {
// 从异步上下文获取追踪ID
return 'trace-id-placeholder';
}
}
// 健康检查系统
class HealthChecker {
private checks: Map<string, HealthCheck> = new Map();
private results: Map<string, HealthCheckResult> = new Map();
private interval: number = 30000; // 30 seconds
constructor(config: HealthCheckConfig) {
this.interval = config.interval || 30000;
this.setupChecks(config.checks);
this.startChecking();
}
registerCheck(name: string, check: HealthCheck): void {
this.checks.set(name, check);
}
async getHealth(): Promise<HealthStatus> {
const results = Array.from(this.results.values());
const healthy = results.every(result => result.status === 'healthy');
return {
status: healthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
checks: Object.fromEntries(this.results),
uptime: process.uptime(),
version: process.env.APP_VERSION || 'unknown',
};
}
private setupChecks(checkConfigs: HealthCheckConfig[]): void {
checkConfigs.forEach(config => {
const check = this.createCheck(config);
if (check) {
this.checks.set(config.name, check);
}
});
}
private createCheck(config: HealthCheckConfig): HealthCheck | null {
switch (config.type) {
case 'database':
return new DatabaseHealthCheck(config);
case 'redis':
return new RedisHealthCheck(config);
case 'http':
return new HttpHealthCheck(config);
case 'memory':
return new MemoryHealthCheck(config);
case 'disk':
return new DiskHealthCheck(config);
default:
return null;
}
}
private startChecking(): void {
setInterval(() => {
this.runAllChecks();
}, this.interval);
// 立即运行一次
this.runAllChecks();
}
private async runAllChecks(): Promise<void> {
const promises = Array.from(this.checks.entries()).map(async ([name, check]) => {
try {
const result = await Promise.race([
check.check(),
this.timeout(check.timeout || 5000),
]);
this.results.set(name, {
name,
status: 'healthy',
message: result.message || 'OK',
timestamp: new Date().toISOString(),
duration: result.duration,
});
} catch (error) {
this.results.set(name, {
name,
status: 'unhealthy',
message: error.message,
timestamp: new Date().toISOString(),
duration: 0,
});
}
});
await Promise.allSettled(promises);
}
private timeout(ms: number): Promise<never> {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Health check timeout')), ms);
});
}
}
// 类型定义
type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';
interface LogEntry {
timestamp: string;
level: LogLevel;
context: string;
message: string;
meta: Record<string, any>;
traceId: string;
}
interface LoggerConfig {
level?: LogLevel;
appenders?: AppenderConfig[];
}
interface AppenderConfig {
type: 'console' | 'file' | 'elasticsearch' | 'syslog';
config?: Record<string, any>;
}
interface LogAppender {
append(entry: LogEntry): void;
}
interface MetricsConfig {
aggregationInterval?: number;
exporters: ExporterConfig[];
}
interface ExporterConfig {
type: 'prometheus' | 'influxdb' | 'console';
config?: Record<string, any>;
}
interface MetricExporter {
export(metrics: AggregatedMetric[]): Promise<void>;
}
interface AggregatedMetric {
name: string;
labels: Record<string, string>;
type: MetricType;
value: number | HistogramStats;
timestamp: Timestamp;
}
interface HistogramStats {
count: number;
sum: number;
min: number;
max: number;
avg: number;
p50: number;
p90: number;
p95: number;
p99: number;
}
interface HealthCheck {
check(): Promise<{ message?: string; duration: number }>;
timeout?: number;
}
interface HealthCheckResult {
name: string;
status: 'healthy' | 'unhealthy';
message: string;
timestamp: string;
duration: number;
}
interface HealthStatus {
status: 'healthy' | 'unhealthy';
timestamp: string;
checks: Record<string, HealthCheckResult>;
uptime: number;
version: string;
}
export {
MetricsCollector,
Logger,
HealthChecker,
};
export type {
MetricData,
LogEntry,
HealthStatus,
};
6.8 部署配置
# docker-compose.yml
version: '3.8'
services:
# API 网关
api-gateway:
build:
context: ./gateway
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- NODE_ENV=production
- REDIS_URL=redis://redis:6379
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=erp_gateway
- DB_USER=gateway_user
- DB_PASSWORD=gateway_pass
depends_on:
- redis
- postgres
networks:
- erp-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
# 认证服务
auth-service:
build:
context: ./services/auth
dockerfile: Dockerfile
ports:
- "8081:8080"
environment:
- NODE_ENV=production
- JWT_SECRET=${JWT_SECRET}
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=erp_auth
- DB_USER=auth_user
- DB_PASSWORD=auth_pass
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
networks:
- erp-network
restart: unless-stopped
# 用户管理服务
user-service:
build:
context: ./services/user
dockerfile: Dockerfile
ports:
- "8082:8080"
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=erp_users
- DB_USER=user_user
- DB_PASSWORD=user_pass
- AUTH_SERVICE_URL=http://auth-service:8080
depends_on:
- postgres
- auth-service
networks:
- erp-network
restart: unless-stopped
# 前端应用
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:80"
environment:
- REACT_APP_API_URL=http://api-gateway:8080
depends_on:
- api-gateway
networks:
- erp-network
restart: unless-stopped
# PostgreSQL 数据库
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_DB=erp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
- ./database/init:/docker-entrypoint-initdb.d
networks:
- erp-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 10s
retries: 5
# Redis 缓存
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- erp-network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
# Elasticsearch (日志存储)
elasticsearch:
image: elasticsearch:8.8.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
networks:
- erp-network
restart: unless-stopped
# Kibana (日志可视化)
kibana:
image: kibana:8.8.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- erp-network
restart: unless-stopped
# Prometheus (指标收集)
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- erp-network
restart: unless-stopped
# Grafana (指标可视化)
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources
depends_on:
- prometheus
networks:
- erp-network
restart: unless-stopped
volumes:
postgres_data:
redis_data:
elasticsearch_data:
prometheus_data:
grafana_data:
networks:
erp-network:
driver: bridge
# kubernetes/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: erp-system
labels:
name: erp-system
---
# kubernetes/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: erp-config
namespace: erp-system
data:
NODE_ENV: "production"
LOG_LEVEL: "info"
METRICS_ENABLED: "true"
HEALTH_CHECK_INTERVAL: "30000"
---
# kubernetes/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: erp-secrets
namespace: erp-system
type: Opaque
data:
JWT_SECRET: <base64-encoded-jwt-secret>
DB_PASSWORD: <base64-encoded-db-password>
REDIS_PASSWORD: <base64-encoded-redis-password>
---
# kubernetes/api-gateway-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: erp-system
labels:
app: api-gateway
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
containers:
- name: api-gateway
image: erp/api-gateway:latest
ports:
- containerPort: 8080
env:
- name: NODE_ENV
valueFrom:
configMapKeyRef:
name: erp-config
key: NODE_ENV
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: erp-secrets
key: JWT_SECRET
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
# kubernetes/api-gateway-service.yaml
apiVersion: v1
kind: Service
metadata:
name: api-gateway-service
namespace: erp-system
spec:
selector:
app: api-gateway
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
# kubernetes/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
namespace: erp-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
本章练习
练习1:扩展权限系统
- 实现基于资源的权限控制(RBAC)
- 添加动态权限分配功能
- 实现权限继承机制
- 创建权限审计日志
练习2:优化API网关
- 实现请求限流算法(令牌桶、滑动窗口)
- 添加API版本管理
- 实现请求/响应缓存策略
- 添加API文档自动生成
练习3:完善监控系统
- 实现分布式链路追踪
- 添加业务指标监控
- 创建告警规则和通知系统
- 实现性能基线和异常检测
练习4:数据库优化
- 实现读写分离
- 添加数据库连接池管理
- 实现数据分片策略
- 创建数据备份和恢复机制
练习5:部署优化
- 实现蓝绿部署策略
- 添加滚动更新配置
- 创建灾难恢复方案
- 实现多环境配置管理
本章总结
本章通过一个完整的企业级ERP系统项目,展示了如何运用TypeScript构建大型应用架构。我们涵盖了:
核心架构设计
- 微服务架构:服务拆分、通信机制、服务发现
- 领域驱动设计:实体建模、聚合根、值对象
- 分层架构:表现层、业务层、数据访问层
技术实现要点
- 类型系统设计:基础类型、实体定义、业务模型
- 状态管理:Zustand集成、异步状态处理
- 权限控制:RBAC模型、装饰器模式、组件级权限
- API网关:路由管理、负载均衡、熔断机制
工程化实践
- 监控系统:指标收集、日志管理、健康检查
- 部署策略:容器化、Kubernetes、自动扩缩容
- 质量保证:类型检查、单元测试、集成测试
最佳实践总结
- 类型安全优先:充分利用TypeScript的类型系统
- 模块化设计:清晰的模块边界和依赖关系
- 可观测性:完善的监控、日志和追踪
- 可扩展性:支持水平扩展和垂直扩展
- 可维护性:代码组织、文档、测试覆盖
通过本章的学习,你应该能够: - 设计和实现企业级TypeScript应用架构 - 运用现代化的开发工具和最佳实践 - 构建可扩展、可维护的大型系统 - 实现完整的DevOps流程
这个综合项目展示了TypeScript在企业级应用开发中的强大能力,为你在实际工作中应用这些技术奠定了坚实的基础。