feat: 添加前端页面和业务说明书
refactor(server): 重构服务层代码结构 feat(server): 添加基础设施、跨境电商、AI决策等核心服务 docs: 完善前端业务说明书和开发进度文档 style: 格式化代码和文档
This commit is contained in:
402
docs/02_Backend/Config_Driven.md
Normal file
402
docs/02_Backend/Config_Driven.md
Normal file
@@ -0,0 +1,402 @@
|
||||
# 配置驱动文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 配置驱动设计文档 - 说明如何实现可配置的系统参数,避免硬编码。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Service_Design.md](./Service_Design.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 配置驱动概述
|
||||
|
||||
### 1.1 定义
|
||||
|
||||
配置驱动是指将系统参数(如 ROI 阈值、库存预警值等)从代码中分离出来,通过配置文件或数据库进行管理,实现系统的灵活性和可维护性。
|
||||
|
||||
### 1.2 重要性
|
||||
|
||||
配置驱动的好处:
|
||||
- **灵活性**:无需修改代码即可调整系统行为
|
||||
- **可维护性**:集中管理配置,便于维护
|
||||
- **安全性**:敏感配置可以单独管理
|
||||
- **可扩展性**:易于添加新的配置项
|
||||
- **可监控性**:可以监控配置的使用情况
|
||||
|
||||
---
|
||||
|
||||
## 2. 配置管理
|
||||
|
||||
### 2.1 配置存储
|
||||
|
||||
#### 2.1.1 配置文件
|
||||
|
||||
**适用场景**:静态配置,如数据库连接信息、API 密钥等
|
||||
|
||||
**实现**:
|
||||
- 使用 YAML、JSON、ENV 等格式
|
||||
- 按环境分离配置文件(开发、测试、生产)
|
||||
- 使用配置文件加载工具
|
||||
|
||||
**示例**:
|
||||
```yaml
|
||||
# config.yaml
|
||||
database:
|
||||
host: localhost
|
||||
port: 3306
|
||||
username: root
|
||||
password: password
|
||||
database: crawlful_hub
|
||||
|
||||
api:
|
||||
timeout: 30000
|
||||
retry: 3
|
||||
|
||||
business:
|
||||
roi_threshold: 1.5
|
||||
inventory_alert: 10
|
||||
max_order_quantity: 100
|
||||
```
|
||||
|
||||
#### 2.1.2 数据库
|
||||
|
||||
**适用场景**:动态配置,如业务规则、阈值等
|
||||
|
||||
**实现**:
|
||||
- 创建配置表
|
||||
- 支持配置的版本管理
|
||||
- 支持配置的生效时间
|
||||
|
||||
**示例**:
|
||||
```sql
|
||||
CREATE TABLE cf_config (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
key VARCHAR(100) NOT NULL UNIQUE,
|
||||
value JSON NOT NULL,
|
||||
description VARCHAR(255),
|
||||
tenant_id VARCHAR(50),
|
||||
shop_id VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 插入配置
|
||||
INSERT INTO cf_config (key, value, description) VALUES
|
||||
('roi_threshold', '1.5', 'ROI 阈值'),
|
||||
('inventory_alert', '10', '库存预警值'),
|
||||
('max_order_quantity', '100', '最大订单数量');
|
||||
```
|
||||
|
||||
#### 2.1.3 缓存
|
||||
|
||||
**适用场景**:频繁访问的配置
|
||||
|
||||
**实现**:
|
||||
- 使用 Redis 等缓存工具
|
||||
- 配置变更时自动更新缓存
|
||||
- 设置合理的缓存过期时间
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 配置服务
|
||||
class ConfigService {
|
||||
private cache: Map<string, any> = new Map();
|
||||
|
||||
async getConfig(key: string): Promise<any> {
|
||||
// 先从缓存获取
|
||||
if (this.cache.has(key)) {
|
||||
return this.cache.get(key);
|
||||
}
|
||||
|
||||
// 从数据库获取
|
||||
const config = await this.configRepository.findByKey(key);
|
||||
if (config) {
|
||||
this.cache.set(key, config.value);
|
||||
return config.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async updateConfig(key: string, value: any): Promise<void> {
|
||||
// 更新数据库
|
||||
await this.configRepository.update(key, value);
|
||||
|
||||
// 更新缓存
|
||||
this.cache.set(key, value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 配置加载
|
||||
|
||||
#### 2.2.1 启动时加载
|
||||
|
||||
**实现**:
|
||||
- 在应用启动时加载所有配置
|
||||
- 缓存到内存中
|
||||
- 提供配置访问接口
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 配置加载
|
||||
class ConfigLoader {
|
||||
private config: any = {};
|
||||
|
||||
async load(): Promise<void> {
|
||||
// 加载配置文件
|
||||
const fileConfig = await this.loadFromFile();
|
||||
|
||||
// 加载数据库配置
|
||||
const dbConfig = await this.loadFromDatabase();
|
||||
|
||||
// 合并配置
|
||||
this.config = { ...fileConfig, ...dbConfig };
|
||||
}
|
||||
|
||||
get(key: string, defaultValue?: any): any {
|
||||
const keys = key.split('.');
|
||||
let value = this.config;
|
||||
|
||||
for (const k of keys) {
|
||||
if (value[k] === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
value = value[k];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
const configLoader = new ConfigLoader();
|
||||
await configLoader.load();
|
||||
const roiThreshold = configLoader.get('business.roi_threshold', 1.5);
|
||||
```
|
||||
|
||||
#### 2.2.2 动态加载
|
||||
|
||||
**实现**:
|
||||
- 配置变更时自动加载
|
||||
- 支持热更新
|
||||
- 无需重启应用
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 配置监听器
|
||||
class ConfigListener {
|
||||
constructor(private configService: ConfigService) {
|
||||
// 监听配置变更
|
||||
this.listenForChanges();
|
||||
}
|
||||
|
||||
private listenForChanges(): void {
|
||||
// 监听数据库变更
|
||||
this.configService.on('configChanged', (key: string, value: any) => {
|
||||
console.log(`Config changed: ${key} = ${value}`);
|
||||
// 处理配置变更
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 配置分类
|
||||
|
||||
### 3.1 系统配置
|
||||
|
||||
- **数据库配置**:连接信息、池大小等
|
||||
- **API 配置**:超时时间、重试次数等
|
||||
- **缓存配置**:缓存大小、过期时间等
|
||||
- **安全配置**:密钥、加密算法等
|
||||
|
||||
### 3.2 业务配置
|
||||
|
||||
- **定价配置**:ROI 阈值、定价策略等
|
||||
- **库存配置**:预警值、安全库存等
|
||||
- **订单配置**:最大数量、处理规则等
|
||||
- **物流配置**:物流渠道、运费规则等
|
||||
|
||||
### 3.3 租户配置
|
||||
|
||||
- **租户特定配置**:每个租户的个性化配置
|
||||
- **店铺配置**:每个店铺的特定配置
|
||||
|
||||
---
|
||||
|
||||
## 4. 配置管理界面
|
||||
|
||||
### 4.1 配置列表
|
||||
|
||||
- **展示所有配置**:按分类展示配置项
|
||||
- **搜索配置**:支持按关键字搜索
|
||||
- **过滤配置**:按类型、租户、店铺等过滤
|
||||
|
||||
### 4.2 配置编辑
|
||||
|
||||
- **编辑配置值**:修改配置的当前值
|
||||
- **配置历史**:查看配置的修改历史
|
||||
- **配置生效**:设置配置的生效时间
|
||||
|
||||
### 4.3 配置验证
|
||||
|
||||
- **格式验证**:验证配置值的格式
|
||||
- **范围验证**:验证配置值的范围
|
||||
- **依赖验证**:验证配置之间的依赖关系
|
||||
|
||||
---
|
||||
|
||||
## 5. 最佳实践
|
||||
|
||||
### 5.1 配置命名规范
|
||||
|
||||
- **使用点号分隔**:如 `business.roi_threshold`
|
||||
- **使用小写字母**:避免大小写混淆
|
||||
- **使用下划线**:代替空格
|
||||
- **语义清晰**:配置名称应清晰表达其用途
|
||||
|
||||
### 5.2 配置默认值
|
||||
|
||||
- **设置合理的默认值**:确保系统在没有配置时也能正常运行
|
||||
- **文档化默认值**:在文档中说明默认值的含义
|
||||
- **环境特定默认值**:为不同环境设置不同的默认值
|
||||
|
||||
### 5.3 配置安全
|
||||
|
||||
- **敏感配置加密**:加密存储敏感配置
|
||||
- **配置访问控制**:限制配置的访问权限
|
||||
- **配置审计**:记录配置的修改历史
|
||||
|
||||
### 5.4 配置版本管理
|
||||
|
||||
- **版本控制**:记录配置的版本
|
||||
- **回滚机制**:支持配置的回滚
|
||||
- **变更通知**:通知相关人员配置变更
|
||||
|
||||
---
|
||||
|
||||
## 6. 实现示例
|
||||
|
||||
### 6.1 配置服务
|
||||
|
||||
```typescript
|
||||
// 配置服务
|
||||
class ConfigService {
|
||||
private config: Map<string, any> = new Map();
|
||||
private listeners: Map<string, Function[]> = new Map();
|
||||
|
||||
constructor(private configRepository: ConfigRepository) {
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
private async loadConfig(): Promise<void> {
|
||||
const configs = await this.configRepository.findAll();
|
||||
configs.forEach(config => {
|
||||
this.config.set(config.key, config.value);
|
||||
});
|
||||
}
|
||||
|
||||
async get(key: string, defaultValue?: any): Promise<any> {
|
||||
if (this.config.has(key)) {
|
||||
return this.config.get(key);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
async set(key: string, value: any): Promise<void> {
|
||||
await this.configRepository.upsert(key, value);
|
||||
this.config.set(key, value);
|
||||
this.notifyListeners(key, value);
|
||||
}
|
||||
|
||||
private notifyListeners(key: string, value: any): void {
|
||||
if (this.listeners.has(key)) {
|
||||
this.listeners.get(key)?.forEach(listener => {
|
||||
listener(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
on(key: string, listener: Function): void {
|
||||
if (!this.listeners.has(key)) {
|
||||
this.listeners.set(key, []);
|
||||
}
|
||||
this.listeners.get(key)?.push(listener);
|
||||
}
|
||||
|
||||
off(key: string, listener: Function): void {
|
||||
if (this.listeners.has(key)) {
|
||||
const listeners = this.listeners.get(key)?.filter(l => l !== listener);
|
||||
this.listeners.set(key, listeners || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 配置使用示例
|
||||
|
||||
```typescript
|
||||
// 定价服务
|
||||
class PricingService {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
async calculateROI(product: Product, price: number): Promise<number> {
|
||||
// 获取 ROI 阈值配置
|
||||
const roiThreshold = await this.configService.get('business.roi_threshold', 1.5);
|
||||
|
||||
// 计算成本
|
||||
const cost = await this.calculateCost(product);
|
||||
|
||||
// 计算 ROI
|
||||
const roi = (price - cost) / cost;
|
||||
|
||||
// 检查是否达到阈值
|
||||
if (roi < roiThreshold) {
|
||||
this.logger.warn(`ROI below threshold: ${roi} < ${roiThreshold}`, {
|
||||
productId: product.id,
|
||||
price,
|
||||
cost
|
||||
});
|
||||
}
|
||||
|
||||
return roi;
|
||||
}
|
||||
}
|
||||
|
||||
// 库存服务
|
||||
class InventoryService {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
async checkInventory(productId: string): Promise<void> {
|
||||
// 获取库存预警配置
|
||||
const inventoryAlert = await this.configService.get('business.inventory_alert', 10);
|
||||
|
||||
// 获取库存
|
||||
const inventory = await this.inventoryRepository.findByProductId(productId);
|
||||
|
||||
// 检查库存是否低于预警值
|
||||
if (inventory.quantity < inventoryAlert) {
|
||||
this.logger.warn(`Inventory below alert level: ${inventory.quantity} < ${inventoryAlert}`, {
|
||||
productId,
|
||||
quantity: inventory.quantity,
|
||||
alert: inventoryAlert
|
||||
});
|
||||
|
||||
// 发送库存预警
|
||||
await this.notificationService.sendInventoryAlert(productId, inventory.quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 相关文档
|
||||
|
||||
- [Service_Design.md](./Service_Design.md)
|
||||
- [Data_Consistency.md](./Data_Consistency.md)
|
||||
- [Observability.md](./Observability.md)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于服务设计文档,最后更新: 2026-03-18*
|
||||
442
docs/02_Backend/Data_Consistency.md
Normal file
442
docs/02_Backend/Data_Consistency.md
Normal file
@@ -0,0 +1,442 @@
|
||||
# 数据一致性文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 数据一致性设计文档 - 确保系统数据的准确性和可靠性。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Service_Design.md](./Service_Design.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据一致性概述
|
||||
|
||||
### 1.1 重要性
|
||||
|
||||
数据一致性是系统的核心要求,特别是对于涉及资金、库存、订单等关键业务数据的系统。数据不一致可能导致:
|
||||
- 财务损失
|
||||
- 库存管理混乱
|
||||
- 订单处理错误
|
||||
- 商户信任度下降
|
||||
- 系统不可用
|
||||
|
||||
### 1.2 核心原则
|
||||
|
||||
- **原子性**:操作要么全部成功,要么全部失败
|
||||
- **一致性**:操作前后数据状态保持一致
|
||||
- **隔离性**:并发操作互不干扰
|
||||
- **持久性**:数据一旦提交,就永久保存
|
||||
|
||||
---
|
||||
|
||||
## 2. 事务边界
|
||||
|
||||
### 2.1 定义
|
||||
|
||||
事务边界是指一组操作的范围,这些操作必须作为一个整体执行,要么全部成功,要么全部失败。
|
||||
|
||||
### 2.2 实现方法
|
||||
|
||||
#### 2.2.1 数据库事务
|
||||
|
||||
**适用场景**:涉及数据库操作的业务流程
|
||||
|
||||
**实现**:
|
||||
- 使用 `@Transactional` 注解(Java/Spring)
|
||||
- 使用 `transaction` 方法(Node.js/Sequelize)
|
||||
- 使用数据库原生事务
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// Node.js/Sequelize 示例
|
||||
async createOrder(orderData: OrderCreateDto): Promise<Order> {
|
||||
return await this.sequelize.transaction(async (t) => {
|
||||
// 1. 创建订单
|
||||
const order = await Order.create(orderData, { transaction: t });
|
||||
|
||||
// 2. 扣减库存
|
||||
await Inventory.decrement('quantity', {
|
||||
by: orderData.quantity,
|
||||
where: { productId: orderData.productId },
|
||||
transaction: t
|
||||
});
|
||||
|
||||
// 3. 记录交易
|
||||
await Transaction.create({
|
||||
orderId: order.id,
|
||||
amount: order.totalAmount,
|
||||
type: 'ORDER_CREATED'
|
||||
}, { transaction: t });
|
||||
|
||||
return order;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.2 分布式事务
|
||||
|
||||
**适用场景**:跨多个服务或数据源的业务流程
|
||||
|
||||
**实现**:
|
||||
- 两阶段提交(2PC)
|
||||
- 补偿事务(TCC)
|
||||
- 消息队列 + 最终一致性
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 补偿事务示例
|
||||
async processOrder(orderId: string): Promise<void> {
|
||||
// 1. 尝试处理订单
|
||||
const order = await this.orderRepository.findById(orderId);
|
||||
|
||||
try {
|
||||
// 2. 扣减库存
|
||||
await this.inventoryService.deductInventory(
|
||||
order.productId,
|
||||
order.quantity
|
||||
);
|
||||
|
||||
// 3. 安排物流
|
||||
const shippingId = await this.logisticsService.createShipping(order);
|
||||
order.shippingId = shippingId;
|
||||
|
||||
// 4. 标记订单为已处理
|
||||
order.status = 'PROCESSED';
|
||||
await this.orderRepository.save(order);
|
||||
} catch (error) {
|
||||
// 5. 补偿操作
|
||||
await this.compensateOrder(order);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async compensateOrder(order: Order): Promise<void> {
|
||||
// 恢复库存
|
||||
await this.inventoryService.restoreInventory(
|
||||
order.productId,
|
||||
order.quantity
|
||||
);
|
||||
|
||||
// 取消物流
|
||||
if (order.shippingId) {
|
||||
await this.logisticsService.cancelShipping(order.shippingId);
|
||||
}
|
||||
|
||||
// 标记订单为失败
|
||||
order.status = 'FAILED';
|
||||
await this.orderRepository.save(order);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 最佳实践
|
||||
|
||||
- **明确事务边界**:只包含必要的操作
|
||||
- **保持事务简短**:减少锁持有时间
|
||||
- **合理设置隔离级别**:根据业务需求选择适当的隔离级别
|
||||
- **处理事务异常**:确保异常情况下能够正确回滚
|
||||
|
||||
---
|
||||
|
||||
## 3. 幂等性
|
||||
|
||||
### 3.1 定义
|
||||
|
||||
幂等性是指同一个请求执行多次,结果应该相同。
|
||||
|
||||
### 3.2 实现方法
|
||||
|
||||
#### 3.2.1 请求ID
|
||||
|
||||
**适用场景**:所有外部 API 调用、支付回调等
|
||||
|
||||
**实现**:
|
||||
- 生成唯一的 `requestId`
|
||||
- 记录已处理的 `requestId`
|
||||
- 检查重复请求
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async processPayment(paymentData: PaymentDto): Promise<Payment> {
|
||||
// 检查是否已处理
|
||||
const existingPayment = await this.paymentRepository.findByRequestId(paymentData.requestId);
|
||||
if (existingPayment) {
|
||||
return existingPayment;
|
||||
}
|
||||
|
||||
// 处理支付
|
||||
const payment = await this.paymentRepository.create({
|
||||
...paymentData,
|
||||
status: 'PROCESSING'
|
||||
});
|
||||
|
||||
try {
|
||||
// 调用支付网关
|
||||
const result = await this.paymentGateway.process(paymentData);
|
||||
|
||||
// 更新支付状态
|
||||
payment.status = result.status;
|
||||
payment.transactionId = result.transactionId;
|
||||
await this.paymentRepository.save(payment);
|
||||
} catch (error) {
|
||||
// 更新支付状态为失败
|
||||
payment.status = 'FAILED';
|
||||
await this.paymentRepository.save(payment);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return payment;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2 乐观锁
|
||||
|
||||
**适用场景**:并发更新操作
|
||||
|
||||
**实现**:
|
||||
- 使用版本号或时间戳
|
||||
- 更新时检查版本号
|
||||
- 版本号不匹配则重试或失败
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async updateProduct(productId: string, updates: Partial<Product>): Promise<Product> {
|
||||
const product = await this.productRepository.findById(productId);
|
||||
const currentVersion = product.version;
|
||||
|
||||
// 尝试更新
|
||||
const updated = await this.productRepository.update(
|
||||
{
|
||||
...updates,
|
||||
version: currentVersion + 1
|
||||
},
|
||||
{
|
||||
where: {
|
||||
id: productId,
|
||||
version: currentVersion
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (updated[0] === 0) {
|
||||
// 版本不匹配,说明被其他线程修改
|
||||
throw new ConflictException('Product has been updated by another process');
|
||||
}
|
||||
|
||||
return await this.productRepository.findById(productId);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 最佳实践
|
||||
|
||||
- **为所有外部请求生成唯一ID**
|
||||
- **存储请求ID和处理结果**
|
||||
- **设置合理的过期时间**
|
||||
- **处理重复请求时返回相同的结果**
|
||||
|
||||
---
|
||||
|
||||
## 4. 状态机
|
||||
|
||||
### 4.1 定义
|
||||
|
||||
状态机是一种用于描述对象状态及其转换规则的模型。
|
||||
|
||||
### 4.2 实现方法
|
||||
|
||||
#### 4.2.1 枚举状态
|
||||
|
||||
**适用场景**:简单的状态流转
|
||||
|
||||
**实现**:
|
||||
- 使用枚举定义状态
|
||||
- 使用条件判断处理状态转换
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
enum OrderStatus {
|
||||
PENDING = 'PENDING',
|
||||
PROCESSING = 'PROCESSING',
|
||||
SHIPPED = 'SHIPPED',
|
||||
DELIVERED = 'DELIVERED',
|
||||
CANCELLED = 'CANCELLED',
|
||||
FAILED = 'FAILED'
|
||||
}
|
||||
|
||||
class OrderStateMachine {
|
||||
static canTransition(from: OrderStatus, to: OrderStatus): boolean {
|
||||
const transitions = {
|
||||
[OrderStatus.PENDING]: [OrderStatus.PROCESSING, OrderStatus.CANCELLED],
|
||||
[OrderStatus.PROCESSING]: [OrderStatus.SHIPPED, OrderStatus.FAILED, OrderStatus.CANCELLED],
|
||||
[OrderStatus.SHIPPED]: [OrderStatus.DELIVERED, OrderStatus.FAILED],
|
||||
[OrderStatus.DELIVERED]: [],
|
||||
[OrderStatus.CANCELLED]: [],
|
||||
[OrderStatus.FAILED]: []
|
||||
};
|
||||
|
||||
return transitions[from].includes(to);
|
||||
}
|
||||
}
|
||||
|
||||
async updateOrderStatus(orderId: string, newStatus: OrderStatus): Promise<Order> {
|
||||
const order = await this.orderRepository.findById(orderId);
|
||||
|
||||
if (!OrderStateMachine.canTransition(order.status, newStatus)) {
|
||||
throw new BadRequestException(`Cannot transition from ${order.status} to ${newStatus}`);
|
||||
}
|
||||
|
||||
order.status = newStatus;
|
||||
return await this.orderRepository.save(order);
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2.2 状态机库
|
||||
|
||||
**适用场景**:复杂的状态流转
|
||||
|
||||
**实现**:
|
||||
- 使用专门的状态机库
|
||||
- 定义状态、事件和转换
|
||||
- 处理副作用
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 使用 xstate 库
|
||||
import { createMachine, interpret } from 'xstate';
|
||||
|
||||
const orderMachine = createMachine({
|
||||
id: 'order',
|
||||
initial: 'PENDING',
|
||||
states: {
|
||||
PENDING: {
|
||||
on: {
|
||||
PROCESS: 'PROCESSING',
|
||||
CANCEL: 'CANCELLED'
|
||||
}
|
||||
},
|
||||
PROCESSING: {
|
||||
on: {
|
||||
SHIP: 'SHIPPED',
|
||||
FAIL: 'FAILED',
|
||||
CANCEL: 'CANCELLED'
|
||||
}
|
||||
},
|
||||
SHIPPED: {
|
||||
on: {
|
||||
DELIVER: 'DELIVERED',
|
||||
FAIL: 'FAILED'
|
||||
}
|
||||
},
|
||||
DELIVERED: {
|
||||
type: 'final'
|
||||
},
|
||||
CANCELLED: {
|
||||
type: 'final'
|
||||
},
|
||||
FAILED: {
|
||||
type: 'final'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const orderService = interpret(orderMachine)
|
||||
.onTransition(state => console.log('Order state:', state.value));
|
||||
|
||||
orderService.start();
|
||||
orderService.send('PROCESS'); // 从 PENDING 到 PROCESSING
|
||||
orderService.send('SHIP'); // 从 PROCESSING 到 SHIPPED
|
||||
orderService.send('DELIVER'); // 从 SHIPPED 到 DELIVERED
|
||||
```
|
||||
|
||||
### 4.3 最佳实践
|
||||
|
||||
- **明确定义状态和转换规则**
|
||||
- **使用状态机管理所有状态流转**
|
||||
- **记录状态转换历史**
|
||||
- **处理状态转换的副作用**
|
||||
- **验证状态转换的合法性**
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据一致性保障措施
|
||||
|
||||
### 5.1 数据库层面
|
||||
|
||||
- **使用事务**:确保数据操作的原子性
|
||||
- **设置约束**:使用唯一约束、外键约束等
|
||||
- **合理索引**:提高查询性能,减少锁竞争
|
||||
- **定期备份**:防止数据丢失
|
||||
|
||||
### 5.2 应用层面
|
||||
|
||||
- **实现幂等性**:处理重复请求
|
||||
- **使用状态机**:管理状态流转
|
||||
- **异步处理**:使用消息队列处理非实时操作
|
||||
- **补偿机制**:处理失败的操作
|
||||
|
||||
### 5.3 监控和告警
|
||||
|
||||
- **数据一致性检查**:定期检查数据一致性
|
||||
- **异常监控**:监控数据操作异常
|
||||
- **告警机制**:及时发现和处理数据不一致问题
|
||||
|
||||
---
|
||||
|
||||
## 6. 常见问题及解决方案
|
||||
|
||||
### 6.1 并发更新冲突
|
||||
|
||||
**问题**:多个用户同时更新同一条数据
|
||||
|
||||
**解决方案**:
|
||||
- 使用乐观锁
|
||||
- 使用悲观锁
|
||||
- 实现队列机制
|
||||
|
||||
### 6.2 分布式事务
|
||||
|
||||
**问题**:跨多个服务或数据源的事务
|
||||
|
||||
**解决方案**:
|
||||
- 使用消息队列 + 最终一致性
|
||||
- 使用 Saga 模式
|
||||
- 使用 TCC 模式
|
||||
|
||||
### 6.3 数据同步延迟
|
||||
|
||||
**问题**:不同系统之间的数据同步存在延迟
|
||||
|
||||
**解决方案**:
|
||||
- 使用事件驱动架构
|
||||
- 实现增量同步
|
||||
- 设置合理的同步频率
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
### 7.1 单元测试
|
||||
|
||||
- 测试单个组件的逻辑
|
||||
- 模拟依赖
|
||||
- 验证边界情况
|
||||
|
||||
### 7.2 集成测试
|
||||
|
||||
- 测试多个组件的交互
|
||||
- 测试事务边界
|
||||
- 测试状态转换
|
||||
|
||||
### 7.3 压力测试
|
||||
|
||||
- 测试并发场景
|
||||
- 测试系统稳定性
|
||||
- 测试数据一致性
|
||||
|
||||
---
|
||||
|
||||
## 8. 相关文档
|
||||
|
||||
- [Service_Design.md](./Service_Design.md)
|
||||
- [Database_Design.md](./Database_Design.md)
|
||||
- [API_Specs](./API_Specs/)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于服务设计文档,最后更新: 2026-03-18*
|
||||
492
docs/02_Backend/Event_Driven.md
Normal file
492
docs/02_Backend/Event_Driven.md
Normal file
@@ -0,0 +1,492 @@
|
||||
# 事件驱动文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 事件驱动设计文档 - 定义核心事件和处理流程,实现系统解耦和可扩展性。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Service_Design.md](./Service_Design.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 事件驱动概述
|
||||
|
||||
### 1.1 定义
|
||||
|
||||
事件驱动是一种架构模式,通过事件的产生、发布、订阅和处理来实现系统组件之间的通信和协作。
|
||||
|
||||
### 1.2 重要性
|
||||
|
||||
事件驱动架构的好处:
|
||||
- **解耦**:组件之间通过事件通信,减少直接依赖
|
||||
- **可扩展性**:可以轻松添加新的事件处理逻辑
|
||||
- **异步处理**:事件处理可以异步执行,提高系统性能
|
||||
- **可靠性**:事件可以持久化,确保消息不丢失
|
||||
- **可观测性**:可以追踪事件的产生和处理
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心事件
|
||||
|
||||
### 2.1 事件定义
|
||||
|
||||
| 事件名称 | 描述 | 触发条件 | 相关服务 |
|
||||
|----------|------|----------|----------|
|
||||
| **OrderCreated** | 订单创建 | 用户提交订单 | 订单服务、库存服务、通知服务 |
|
||||
| **OrderUpdated** | 订单更新 | 订单状态变更 | 订单服务、通知服务 |
|
||||
| **OrderCompleted** | 订单完成 | 订单状态变为已完成 | 订单服务、结算服务、通知服务 |
|
||||
| **OrderCancelled** | 订单取消 | 订单状态变为已取消 | 订单服务、库存服务、通知服务 |
|
||||
| **ProductCreated** | 商品创建 | 创建新商品 | 商品服务、搜索服务 |
|
||||
| **ProductUpdated** | 商品更新 | 更新商品信息 | 商品服务、搜索服务 |
|
||||
| **ProductPriceChanged** | 商品价格变更 | 商品价格更新 | 商品服务、定价服务、搜索服务 |
|
||||
| **InventoryLow** | 库存不足 | 库存低于预警值 | 库存服务、通知服务 |
|
||||
| **InventoryUpdated** | 库存更新 | 库存数量变更 | 库存服务、商品服务 |
|
||||
| **PaymentProcessed** | 支付处理 | 支付完成 | 支付服务、订单服务 |
|
||||
| **SettlementCreated** | 结算创建 | 创建结算单 | 结算服务、通知服务 |
|
||||
| **MerchantRegistered** | 商户注册 | 新商户注册 | 商户服务、通知服务 |
|
||||
| **MerchantVerified** | 商户验证 | 商户资质验证通过 | 商户服务、通知服务 |
|
||||
|
||||
### 2.2 事件结构
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
interface Event {
|
||||
id: string; // 事件ID
|
||||
type: string; // 事件类型
|
||||
timestamp: number; // 事件时间戳
|
||||
data: any; // 事件数据
|
||||
metadata: {
|
||||
tenantId: string; // 租户ID
|
||||
shopId: string; // 店铺ID
|
||||
traceId: string; // 追踪ID
|
||||
source: string; // 事件源
|
||||
};
|
||||
}
|
||||
|
||||
// 订单创建事件示例
|
||||
const orderCreatedEvent: Event = {
|
||||
id: 'event-001',
|
||||
type: 'OrderCreated',
|
||||
timestamp: Date.now(),
|
||||
data: {
|
||||
orderId: 'order-001',
|
||||
userId: 'user-001',
|
||||
items: [
|
||||
{
|
||||
productId: 'product-001',
|
||||
quantity: 2,
|
||||
price: 99.99
|
||||
}
|
||||
],
|
||||
totalAmount: 199.98,
|
||||
status: 'PENDING'
|
||||
},
|
||||
metadata: {
|
||||
tenantId: 'tenant-001',
|
||||
shopId: 'shop-001',
|
||||
traceId: 'trace-001',
|
||||
source: 'OrderService'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 事件处理流程
|
||||
|
||||
### 3.1 事件发布
|
||||
|
||||
**实现**:
|
||||
- 使用事件总线或消息队列
|
||||
- 事件发布者将事件发送到事件总线
|
||||
- 事件总线确保事件的传递
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 事件总线
|
||||
class EventBus {
|
||||
private listeners: Map<string, Function[]> = new Map();
|
||||
|
||||
publish(event: Event): void {
|
||||
console.log(`Publishing event: ${event.type}`, event);
|
||||
|
||||
if (this.listeners.has(event.type)) {
|
||||
this.listeners.get(event.type)?.forEach(listener => {
|
||||
try {
|
||||
listener(event);
|
||||
} catch (error) {
|
||||
console.error(`Error handling event ${event.type}:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(eventType: string, listener: Function): void {
|
||||
if (!this.listeners.has(eventType)) {
|
||||
this.listeners.set(eventType, []);
|
||||
}
|
||||
this.listeners.get(eventType)?.push(listener);
|
||||
}
|
||||
|
||||
unsubscribe(eventType: string, listener: Function): void {
|
||||
if (this.listeners.has(eventType)) {
|
||||
const listeners = this.listeners.get(eventType)?.filter(l => l !== listener);
|
||||
this.listeners.set(eventType, listeners || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 订单服务发布事件
|
||||
class OrderService {
|
||||
constructor(private eventBus: EventBus) {}
|
||||
|
||||
async createOrder(orderData: OrderCreateDto): Promise<Order> {
|
||||
// 创建订单
|
||||
const order = await this.orderRepository.create(orderData);
|
||||
|
||||
// 发布订单创建事件
|
||||
const event: Event = {
|
||||
id: uuidv4(),
|
||||
type: 'OrderCreated',
|
||||
timestamp: Date.now(),
|
||||
data: order,
|
||||
metadata: {
|
||||
tenantId: order.tenantId,
|
||||
shopId: order.shopId,
|
||||
traceId: orderData.traceId,
|
||||
source: 'OrderService'
|
||||
}
|
||||
};
|
||||
|
||||
this.eventBus.publish(event);
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 事件订阅
|
||||
|
||||
**实现**:
|
||||
- 服务订阅感兴趣的事件
|
||||
- 当事件发生时,执行相应的处理逻辑
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 库存服务订阅事件
|
||||
class InventoryService {
|
||||
constructor(private eventBus: EventBus) {
|
||||
// 订阅订单创建事件
|
||||
this.eventBus.subscribe('OrderCreated', this.handleOrderCreated.bind(this));
|
||||
|
||||
// 订阅订单取消事件
|
||||
this.eventBus.subscribe('OrderCancelled', this.handleOrderCancelled.bind(this));
|
||||
}
|
||||
|
||||
private async handleOrderCreated(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
|
||||
// 扣减库存
|
||||
for (const item of order.items) {
|
||||
await this.deductInventory(item.productId, item.quantity);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleOrderCancelled(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
|
||||
// 恢复库存
|
||||
for (const item of order.items) {
|
||||
await this.restoreInventory(item.productId, item.quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通知服务订阅事件
|
||||
class NotificationService {
|
||||
constructor(private eventBus: EventBus) {
|
||||
// 订阅订单相关事件
|
||||
this.eventBus.subscribe('OrderCreated', this.sendOrderConfirmation.bind(this));
|
||||
this.eventBus.subscribe('OrderUpdated', this.sendOrderUpdate.bind(this));
|
||||
this.eventBus.subscribe('OrderCompleted', this.sendOrderCompletion.bind(this));
|
||||
this.eventBus.subscribe('OrderCancelled', this.sendOrderCancellation.bind(this));
|
||||
}
|
||||
|
||||
private async sendOrderConfirmation(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
// 发送订单确认通知
|
||||
await this.sendEmail(order.userId, 'Order Confirmation', `Your order ${order.id} has been created`);
|
||||
}
|
||||
|
||||
private async sendOrderUpdate(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
// 发送订单更新通知
|
||||
await this.sendEmail(order.userId, 'Order Update', `Your order ${order.id} has been updated`);
|
||||
}
|
||||
|
||||
private async sendOrderCompletion(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
// 发送订单完成通知
|
||||
await this.sendEmail(order.userId, 'Order Completed', `Your order ${order.id} has been completed`);
|
||||
}
|
||||
|
||||
private async sendOrderCancellation(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
// 发送订单取消通知
|
||||
await this.sendEmail(order.userId, 'Order Cancelled', `Your order ${order.id} has been cancelled`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 事件持久化
|
||||
|
||||
**实现**:
|
||||
- 使用消息队列(如 RabbitMQ、Kafka)持久化事件
|
||||
- 确保事件不丢失
|
||||
- 支持事件的重试和重放
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 使用 Kafka
|
||||
import { Kafka } from 'kafkajs';
|
||||
|
||||
const kafka = new Kafka({
|
||||
clientId: 'crawlful-hub',
|
||||
brokers: ['localhost:9092']
|
||||
});
|
||||
|
||||
const producer = kafka.producer();
|
||||
const consumer = kafka.consumer({ groupId: 'inventory-service' });
|
||||
|
||||
// 发布事件
|
||||
async function publishEvent(event: Event): Promise<void> {
|
||||
await producer.connect();
|
||||
await producer.send({
|
||||
topic: 'events',
|
||||
messages: [
|
||||
{
|
||||
key: event.type,
|
||||
value: JSON.stringify(event)
|
||||
}
|
||||
]
|
||||
});
|
||||
await producer.disconnect();
|
||||
}
|
||||
|
||||
// 订阅事件
|
||||
async function subscribeEvents(): Promise<void> {
|
||||
await consumer.connect();
|
||||
await consumer.subscribe({ topic: 'events', fromBeginning: false });
|
||||
|
||||
await consumer.run({
|
||||
eachMessage: async ({ message }) => {
|
||||
const event = JSON.parse(message.value?.toString() || '{}');
|
||||
console.log('Received event:', event);
|
||||
|
||||
// 处理事件
|
||||
if (event.type === 'OrderCreated') {
|
||||
await handleOrderCreated(event);
|
||||
} else if (event.type === 'OrderCancelled') {
|
||||
await handleOrderCancelled(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 事件驱动架构
|
||||
|
||||
### 4.1 架构图
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ │ │ │ │ │
|
||||
│ 服务 A │────>│ 事件总线 │────>│ 服务 B │
|
||||
│ │ │ │ │ │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
^ │ ^
|
||||
│ │ │
|
||||
│ v │
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ │ │ │ │ │
|
||||
│ 服务 D │<────│ 消息队列 │<────│ 服务 C │
|
||||
│ │ │ │ │ │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
### 4.2 核心组件
|
||||
|
||||
- **事件发布者**:产生事件的服务
|
||||
- **事件总线**:传递事件的中间件
|
||||
- **消息队列**:持久化事件的存储
|
||||
- **事件订阅者**:处理事件的服务
|
||||
- **事件处理器**:执行具体的事件处理逻辑
|
||||
|
||||
### 4.3 优势
|
||||
|
||||
- **松耦合**:服务之间通过事件通信,减少直接依赖
|
||||
- **可扩展性**:可以轻松添加新的服务和事件处理逻辑
|
||||
- **可靠性**:事件可以持久化,确保消息不丢失
|
||||
- **灵活性**:事件处理可以异步执行,提高系统性能
|
||||
- **可观测性**:可以追踪事件的产生和处理
|
||||
|
||||
---
|
||||
|
||||
## 5. 最佳实践
|
||||
|
||||
### 5.1 事件设计
|
||||
|
||||
- **事件命名**:使用清晰、语义化的事件名称
|
||||
- **事件结构**:统一事件结构,包含必要的字段
|
||||
- **事件数据**:只包含必要的数据,避免过大
|
||||
- **事件版本**:考虑事件的版本管理
|
||||
|
||||
### 5.2 事件处理
|
||||
|
||||
- **幂等性**:确保事件处理的幂等性
|
||||
- **错误处理**:妥善处理事件处理过程中的错误
|
||||
- **重试机制**:实现事件处理的重试机制
|
||||
- **死信队列**:处理无法正常处理的事件
|
||||
|
||||
### 5.3 性能优化
|
||||
|
||||
- **批量处理**:批量处理事件,减少网络开销
|
||||
- **异步处理**:使用异步处理提高性能
|
||||
- **缓存**:合理使用缓存,减少重复计算
|
||||
- **限流**:实现事件处理的限流机制
|
||||
|
||||
### 5.4 监控和告警
|
||||
|
||||
- **事件监控**:监控事件的产生和处理
|
||||
- **事件延迟**:监控事件处理的延迟
|
||||
- **错误率**:监控事件处理的错误率
|
||||
- **告警机制**:设置合理的告警阈值
|
||||
|
||||
---
|
||||
|
||||
## 6. 实现示例
|
||||
|
||||
### 6.1 事件总线
|
||||
|
||||
```typescript
|
||||
// 事件总线实现
|
||||
class EventBus {
|
||||
private listeners: Map<string, Function[]> = new Map();
|
||||
private queue: Event[] = [];
|
||||
private processing = false;
|
||||
|
||||
publish(event: Event): void {
|
||||
this.queue.push(event);
|
||||
if (!this.processing) {
|
||||
this.processQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private async processQueue(): Promise<void> {
|
||||
this.processing = true;
|
||||
|
||||
while (this.queue.length > 0) {
|
||||
const event = this.queue.shift();
|
||||
if (event) {
|
||||
await this.processEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
private async processEvent(event: Event): Promise<void> {
|
||||
console.log(`Processing event: ${event.type}`, event);
|
||||
|
||||
if (this.listeners.has(event.type)) {
|
||||
const listeners = this.listeners.get(event.type) || [];
|
||||
|
||||
for (const listener of listeners) {
|
||||
try {
|
||||
await listener(event);
|
||||
} catch (error) {
|
||||
console.error(`Error handling event ${event.type}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(eventType: string, listener: Function): void {
|
||||
if (!this.listeners.has(eventType)) {
|
||||
this.listeners.set(eventType, []);
|
||||
}
|
||||
this.listeners.get(eventType)?.push(listener);
|
||||
}
|
||||
|
||||
unsubscribe(eventType: string, listener: Function): void {
|
||||
if (this.listeners.has(eventType)) {
|
||||
const listeners = this.listeners.get(eventType)?.filter(l => l !== listener);
|
||||
this.listeners.set(eventType, listeners || []);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 全局事件总线
|
||||
export const eventBus = new EventBus();
|
||||
```
|
||||
|
||||
### 6.2 事件处理器
|
||||
|
||||
```typescript
|
||||
// 订单事件处理器
|
||||
class OrderEventHandler {
|
||||
constructor(private inventoryService: InventoryService, private notificationService: NotificationService) {
|
||||
eventBus.subscribe('OrderCreated', this.handleOrderCreated.bind(this));
|
||||
eventBus.subscribe('OrderCancelled', this.handleOrderCancelled.bind(this));
|
||||
}
|
||||
|
||||
private async handleOrderCreated(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
|
||||
// 扣减库存
|
||||
for (const item of order.items) {
|
||||
await this.inventoryService.deductInventory(item.productId, item.quantity);
|
||||
}
|
||||
|
||||
// 发送通知
|
||||
await this.notificationService.sendOrderConfirmation(order);
|
||||
}
|
||||
|
||||
private async handleOrderCancelled(event: Event): Promise<void> {
|
||||
const order = event.data;
|
||||
|
||||
// 恢复库存
|
||||
for (const item of order.items) {
|
||||
await this.inventoryService.restoreInventory(item.productId, item.quantity);
|
||||
}
|
||||
|
||||
// 发送通知
|
||||
await this.notificationService.sendOrderCancellation(order);
|
||||
}
|
||||
}
|
||||
|
||||
// 库存事件处理器
|
||||
class InventoryEventHandler {
|
||||
constructor(private notificationService: NotificationService) {
|
||||
eventBus.subscribe('InventoryLow', this.handleInventoryLow.bind(this));
|
||||
}
|
||||
|
||||
private async handleInventoryLow(event: Event): Promise<void> {
|
||||
const { productId, quantity, alertLevel } = event.data;
|
||||
|
||||
// 发送库存预警通知
|
||||
await this.notificationService.sendInventoryAlert(productId, quantity, alertLevel);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 相关文档
|
||||
|
||||
- [Service_Design.md](./Service_Design.md)
|
||||
- [Data_Consistency.md](./Data_Consistency.md)
|
||||
- [Observability.md](./Observability.md)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于服务设计文档,最后更新: 2026-03-18*
|
||||
349
docs/02_Backend/Observability.md
Normal file
349
docs/02_Backend/Observability.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# 可观测性文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 可观测性设计文档 - 确保系统的可观测性,便于问题排查和系统优化。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Service_Design.md](./Service_Design.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 可观测性概述
|
||||
|
||||
### 1.1 定义
|
||||
|
||||
可观测性是指通过系统的外部输出(如日志、指标、追踪)来了解系统内部状态的能力。
|
||||
|
||||
### 1.2 重要性
|
||||
|
||||
良好的可观测性可以:
|
||||
- 快速定位和解决问题
|
||||
- 预测和预防系统故障
|
||||
- 优化系统性能
|
||||
- 提高系统可靠性
|
||||
- 降低运维成本
|
||||
|
||||
### 1.3 核心组成
|
||||
|
||||
- **业务日志**:记录业务操作的详细信息
|
||||
- **链路追踪**:追踪请求在系统中的完整路径
|
||||
- **指标监控**:监控系统的各种指标
|
||||
|
||||
---
|
||||
|
||||
## 2. 业务日志
|
||||
|
||||
### 2.1 定义
|
||||
|
||||
业务日志是指记录业务操作的详细信息,包括操作人、操作时间、操作类型、操作对象、操作结果等。
|
||||
|
||||
### 2.2 实现方法
|
||||
|
||||
#### 2.2.1 日志级别
|
||||
|
||||
- **DEBUG**:详细的调试信息
|
||||
- **INFO**:一般的信息
|
||||
- **WARN**:警告信息
|
||||
- **ERROR**:错误信息
|
||||
- **FATAL**:致命错误信息
|
||||
|
||||
#### 2.2.2 日志格式
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-03-18T10:00:00Z",
|
||||
"level": "INFO",
|
||||
"service": "ProductService",
|
||||
"method": "updatePrice",
|
||||
"traceId": "1234567890",
|
||||
"tenantId": "tenant-001",
|
||||
"shopId": "shop-001",
|
||||
"businessType": "TOC",
|
||||
"message": "Updating price for product 123 to 99.99",
|
||||
"data": {
|
||||
"productId": "123",
|
||||
"oldPrice": 89.99,
|
||||
"newPrice": 99.99,
|
||||
"roi": 1.5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2.3 日志框架
|
||||
|
||||
**推荐**:
|
||||
- Node.js:winston, bunyan
|
||||
- Java:log4j, logback
|
||||
- Python:logging, structlog
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// Node.js/winston 示例
|
||||
import winston from 'winston';
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: winston.format.json(),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
||||
new winston.transports.File({ filename: 'combined.log' })
|
||||
]
|
||||
});
|
||||
|
||||
// 使用
|
||||
logger.info('Updating price for product', {
|
||||
productId: '123',
|
||||
oldPrice: 89.99,
|
||||
newPrice: 99.99,
|
||||
traceId: '1234567890',
|
||||
tenantId: 'tenant-001',
|
||||
shopId: 'shop-001',
|
||||
businessType: 'TOC'
|
||||
});
|
||||
```
|
||||
|
||||
### 2.3 最佳实践
|
||||
|
||||
- **结构化日志**:使用 JSON 格式,便于分析和查询
|
||||
- **统一日志格式**:所有服务使用相同的日志格式
|
||||
- **包含必要字段**:时间戳、级别、服务名、方法名、追踪ID、租户ID、店铺ID、业务类型等
|
||||
- **适当的日志级别**:根据信息的重要性选择合适的级别
|
||||
- **日志轮转**:定期轮转日志文件,防止日志文件过大
|
||||
- **日志存储**:使用 ELK Stack、Splunk 等工具存储和分析日志
|
||||
|
||||
---
|
||||
|
||||
## 3. 链路追踪
|
||||
|
||||
### 3.1 定义
|
||||
|
||||
链路追踪是指追踪请求在系统中的完整路径,包括请求经过的所有服务和操作。
|
||||
|
||||
### 3.2 实现方法
|
||||
|
||||
#### 3.2.1 追踪ID
|
||||
|
||||
**定义**:一个唯一的标识符,用于关联同一个请求的所有操作
|
||||
|
||||
**实现**:
|
||||
- 生成唯一的 `traceId`
|
||||
- 在请求开始时创建 `traceId`
|
||||
- 在所有相关操作中传递 `traceId`
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 中间件生成 traceId
|
||||
const traceMiddleware = (req, res, next) => {
|
||||
const traceId = req.headers['x-trace-id'] || uuidv4();
|
||||
req.traceId = traceId;
|
||||
res.setHeader('x-trace-id', traceId);
|
||||
next();
|
||||
};
|
||||
|
||||
// 使用
|
||||
app.use(traceMiddleware);
|
||||
|
||||
// 在服务中使用
|
||||
async updatePrice(productId: string, price: number, traceId: string): Promise<Product> {
|
||||
logger.info('Updating price', { traceId, productId, price });
|
||||
// 业务逻辑
|
||||
return product;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2 分布式追踪系统
|
||||
|
||||
**推荐**:
|
||||
- Jaeger
|
||||
- Zipkin
|
||||
- OpenTelemetry
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 使用 OpenTelemetry
|
||||
import { trace } from '@opentelemetry/api';
|
||||
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
||||
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
|
||||
|
||||
// 初始化追踪器
|
||||
const provider = new NodeTracerProvider();
|
||||
const exporter = new JaegerExporter({
|
||||
serviceName: 'product-service'
|
||||
});
|
||||
|
||||
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
||||
provider.register();
|
||||
|
||||
// 创建 span
|
||||
const tracer = trace.getTracer('product-service');
|
||||
|
||||
async updatePrice(productId: string, price: number): Promise<Product> {
|
||||
const span = tracer.startSpan('updatePrice');
|
||||
|
||||
try {
|
||||
// 业务逻辑
|
||||
const product = await this.productRepository.findById(productId);
|
||||
|
||||
// 创建子 span
|
||||
const calculateRoiSpan = tracer.startSpan('calculateROI', {
|
||||
parent: span
|
||||
});
|
||||
const roi = await this.pricingService.calculateROI(product, price);
|
||||
calculateRoiSpan.end();
|
||||
|
||||
product.price = price;
|
||||
product.roi = roi;
|
||||
const updatedProduct = await this.productRepository.save(product);
|
||||
|
||||
return updatedProduct;
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 最佳实践
|
||||
|
||||
- **统一的追踪ID**:所有服务使用相同的追踪ID格式
|
||||
- **完整的链路**:追踪请求的完整路径,包括所有服务和操作
|
||||
- **适当的 span**:为重要操作创建 span
|
||||
- **添加上下文信息**:在 span 中添加业务相关的上下文信息
|
||||
- **集成监控系统**:将追踪数据与监控系统集成
|
||||
|
||||
---
|
||||
|
||||
## 4. 指标监控
|
||||
|
||||
### 4.1 定义
|
||||
|
||||
指标监控是指监控系统的各种指标,如响应时间、调用次数、错误率等。
|
||||
|
||||
### 4.2 实现方法
|
||||
|
||||
#### 4.2.1 核心指标
|
||||
|
||||
- **响应时间**:请求的处理时间
|
||||
- **调用次数**:服务的调用次数
|
||||
- **错误率**:错误请求的比例
|
||||
- **资源使用**:CPU、内存、磁盘、网络等资源的使用情况
|
||||
- **业务指标**:订单量、销售额、利润率等
|
||||
|
||||
#### 4.2.2 监控系统
|
||||
|
||||
**推荐**:
|
||||
- Prometheus
|
||||
- Grafana
|
||||
- Datadog
|
||||
- New Relic
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 使用 Prometheus
|
||||
import prometheus from 'prom-client';
|
||||
|
||||
// 定义指标
|
||||
const requestCounter = new prometheus.Counter({
|
||||
name: 'http_requests_total',
|
||||
help: 'Total number of HTTP requests',
|
||||
labelNames: ['method', 'endpoint', 'status']
|
||||
});
|
||||
|
||||
const requestDuration = new prometheus.Histogram({
|
||||
name: 'http_request_duration_seconds',
|
||||
help: 'HTTP request duration in seconds',
|
||||
labelNames: ['method', 'endpoint'],
|
||||
buckets: [0.1, 0.5, 1, 2, 5]
|
||||
});
|
||||
|
||||
// 中间件
|
||||
const metricsMiddleware = (req, res, next) => {
|
||||
const start = process.hrtime();
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = process.hrtime(start);
|
||||
const seconds = duration[0] + duration[1] / 1e9;
|
||||
|
||||
requestCounter.inc({
|
||||
method: req.method,
|
||||
endpoint: req.path,
|
||||
status: res.statusCode
|
||||
});
|
||||
|
||||
requestDuration.observe({
|
||||
method: req.method,
|
||||
endpoint: req.path
|
||||
}, seconds);
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// 使用
|
||||
app.use(metricsMiddleware);
|
||||
app.get('/metrics', (req, res) => {
|
||||
res.set('Content-Type', prometheus.register.contentType);
|
||||
res.end(prometheus.register.metrics());
|
||||
});
|
||||
```
|
||||
|
||||
### 4.3 最佳实践
|
||||
|
||||
- **定义关键指标**:根据业务需求定义关键指标
|
||||
- **设置合理的告警**:为重要指标设置告警
|
||||
- **可视化**:使用 Grafana 等工具可视化指标
|
||||
- **定期分析**:定期分析指标数据,发现问题和优化机会
|
||||
- **集成业务指标**:将业务指标与技术指标结合
|
||||
|
||||
---
|
||||
|
||||
## 5. 可观测性集成
|
||||
|
||||
### 5.1 日志、追踪和指标的关联
|
||||
|
||||
- **使用相同的 traceId**:将日志、追踪和指标关联起来
|
||||
- **统一的上下文**:在所有可观测性数据中包含相同的上下文信息
|
||||
- **集成平台**:使用 ELK Stack、Grafana 等平台集成所有可观测性数据
|
||||
|
||||
### 5.2 监控仪表盘
|
||||
|
||||
**推荐仪表盘**:
|
||||
- **系统健康仪表盘**:显示系统的整体健康状态
|
||||
- **服务性能仪表盘**:显示各个服务的性能指标
|
||||
- **业务指标仪表盘**:显示业务相关的指标
|
||||
- **告警仪表盘**:显示当前的告警状态
|
||||
|
||||
### 5.3 告警策略
|
||||
|
||||
- **设置合理的阈值**:根据业务需求设置合理的告警阈值
|
||||
- **分级告警**:根据问题的严重程度设置不同级别的告警
|
||||
- **告警通知**:通过邮件、短信、Slack 等渠道发送告警通知
|
||||
- **告警抑制**:避免告警风暴
|
||||
|
||||
---
|
||||
|
||||
## 6. 最佳实践
|
||||
|
||||
### 6.1 设计原则
|
||||
|
||||
- **可观测性优先**:在系统设计阶段就考虑可观测性
|
||||
- **统一标准**:所有服务使用相同的可观测性标准
|
||||
- **适度采集**:采集足够的数据,但不过度采集
|
||||
- **数据保留**:设置合理的数据保留策略
|
||||
|
||||
### 6.2 实现建议
|
||||
|
||||
- **使用开源工具**:如 ELK Stack、Prometheus、Grafana、Jaeger 等
|
||||
- **自动化部署**:自动化可观测性工具的部署和配置
|
||||
- **定期演练**:定期演练故障排查和恢复流程
|
||||
- **持续优化**:根据实际情况持续优化可观测性方案
|
||||
|
||||
---
|
||||
|
||||
## 7. 相关文档
|
||||
|
||||
- [Service_Design.md](./Service_Design.md)
|
||||
- [Data_Consistency.md](./Data_Consistency.md)
|
||||
- [Architecture_Overview.md](./Architecture_Overview.md)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于服务设计文档,最后更新: 2026-03-18*
|
||||
383
docs/02_Backend/RBAC_Design.md
Normal file
383
docs/02_Backend/RBAC_Design.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# 权限系统设计文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 权限系统设计文档 - 定义基于角色的访问控制(RBAC)和数据级权限。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Service_Design.md](./Service_Design.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 权限系统概述
|
||||
|
||||
### 1.1 核心概念
|
||||
|
||||
- **角色(Role)**:一组权限的集合
|
||||
- **权限(Permission)**:对资源的操作许可
|
||||
- **用户(User)**:系统的使用者
|
||||
- **资源(Resource)**:系统中的对象,如商品、订单、用户等
|
||||
- **数据级权限**:基于数据属性的访问控制
|
||||
|
||||
### 1.2 重要性
|
||||
|
||||
良好的权限系统可以:
|
||||
- 保护系统安全
|
||||
- 确保数据隐私
|
||||
- 实现职责分离
|
||||
- 满足合规要求
|
||||
- 提供可审计性
|
||||
|
||||
---
|
||||
|
||||
## 2. RBAC 模型
|
||||
|
||||
### 2.1 角色定义
|
||||
|
||||
| 角色 | 描述 | 权限范围 |
|
||||
|------|------|----------|
|
||||
| **ADMIN** | 系统管理员 | 所有资源的所有操作 |
|
||||
| **MANAGER** | 运营主管 | 大部分资源的管理操作 |
|
||||
| **OPERATOR** | 运营专员 | 基础运营操作 |
|
||||
| **FINANCE** | 财务主管 | 财务相关操作 |
|
||||
| **SOURCING** | 采购专家 | 采购相关操作 |
|
||||
| **LOGISTICS** | 物流专家 | 物流相关操作 |
|
||||
| **ANALYST** | 数据分析师 | 数据分析相关操作 |
|
||||
|
||||
### 2.2 权限定义
|
||||
|
||||
| 权限 | 描述 | 适用资源 |
|
||||
|------|------|----------|
|
||||
| **CREATE** | 创建资源 | 所有资源 |
|
||||
| **READ** | 读取资源 | 所有资源 |
|
||||
| **UPDATE** | 更新资源 | 所有资源 |
|
||||
| **DELETE** | 删除资源 | 所有资源 |
|
||||
| **APPROVE** | 审批操作 | 订单、结算等 |
|
||||
| **EXPORT** | 导出数据 | 报表、数据等 |
|
||||
| **IMPORT** | 导入数据 | 商品、用户等 |
|
||||
|
||||
### 2.3 权限分配
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 角色权限映射
|
||||
const rolePermissions = {
|
||||
ADMIN: ['*'], // 所有权限
|
||||
MANAGER: [
|
||||
'CREATE:product', 'READ:product', 'UPDATE:product', 'DELETE:product',
|
||||
'CREATE:order', 'READ:order', 'UPDATE:order', 'APPROVE:order',
|
||||
'READ:report', 'EXPORT:report'
|
||||
],
|
||||
OPERATOR: [
|
||||
'READ:product', 'UPDATE:product',
|
||||
'READ:order', 'UPDATE:order'
|
||||
],
|
||||
FINANCE: [
|
||||
'READ:order', 'READ:settlement', 'APPROVE:settlement',
|
||||
'READ:report', 'EXPORT:report'
|
||||
],
|
||||
SOURCING: [
|
||||
'CREATE:product', 'READ:product', 'UPDATE:product',
|
||||
'READ:supplier', 'CREATE:supplier', 'UPDATE:supplier'
|
||||
],
|
||||
LOGISTICS: [
|
||||
'READ:order', 'UPDATE:order',
|
||||
'READ:inventory', 'UPDATE:inventory'
|
||||
],
|
||||
ANALYST: [
|
||||
'READ:report', 'EXPORT:report'
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据级权限
|
||||
|
||||
### 3.1 定义
|
||||
|
||||
数据级权限是指基于数据属性的访问控制,确保用户只能访问和操作与自己相关的数据。
|
||||
|
||||
### 3.2 实现方法
|
||||
|
||||
#### 3.2.1 租户隔离
|
||||
|
||||
**定义**:不同租户的数据相互隔离
|
||||
|
||||
**实现**:
|
||||
- 在所有数据表中添加 `tenantId` 字段
|
||||
- 在所有查询中默认带上 `tenantId` 过滤
|
||||
- 确保跨租户操作的安全性
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 数据访问层
|
||||
class ProductRepository {
|
||||
async findAll(tenantId: string, params?: any): Promise<Product[]> {
|
||||
return await Product.findAll({
|
||||
where: {
|
||||
tenantId,
|
||||
...params
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id: string, tenantId: string): Promise<Product | null> {
|
||||
return await Product.findOne({
|
||||
where: {
|
||||
id,
|
||||
tenantId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2 店铺隔离
|
||||
|
||||
**定义**:同一租户下不同店铺的数据相互隔离
|
||||
|
||||
**实现**:
|
||||
- 在相关数据表中添加 `shopId` 字段
|
||||
- 在查询中根据用户的店铺权限过滤
|
||||
- 确保用户只能访问自己有权限的店铺数据
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 权限服务
|
||||
class RBACService {
|
||||
async checkShopAccess(userId: string, shopId: string): Promise<boolean> {
|
||||
const user = await this.userRepository.findById(userId);
|
||||
const userShops = await this.userShopRepository.findByUserId(userId);
|
||||
return userShops.some(us => us.shopId === shopId) || user.role === 'ADMIN';
|
||||
}
|
||||
}
|
||||
|
||||
// 订单服务
|
||||
class OrderService {
|
||||
async getOrders(userId: string, params?: any): Promise<Order[]> {
|
||||
const user = await this.userRepository.findById(userId);
|
||||
|
||||
if (user.role === 'ADMIN') {
|
||||
return await this.orderRepository.findAll(params);
|
||||
}
|
||||
|
||||
const userShops = await this.userShopRepository.findByUserId(userId);
|
||||
const shopIds = userShops.map(us => us.shopId);
|
||||
|
||||
return await this.orderRepository.findAll({
|
||||
...params,
|
||||
shopId: shopIds
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.3 字段级权限
|
||||
|
||||
**定义**:基于字段的访问控制
|
||||
|
||||
**实现**:
|
||||
- 定义字段级权限规则
|
||||
- 在返回数据时过滤敏感字段
|
||||
- 确保用户只能看到和修改自己有权限的字段
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 字段级权限
|
||||
const fieldPermissions = {
|
||||
ADMIN: {
|
||||
product: ['*'], // 所有字段
|
||||
user: ['*']
|
||||
},
|
||||
MANAGER: {
|
||||
product: ['id', 'name', 'price', 'stock'],
|
||||
user: ['id', 'name', 'email']
|
||||
},
|
||||
OPERATOR: {
|
||||
product: ['id', 'name', 'price'],
|
||||
user: ['id', 'name']
|
||||
}
|
||||
};
|
||||
|
||||
// 数据过滤
|
||||
function filterFields(data: any, fields: string[]): any {
|
||||
if (fields.includes('*')) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const filtered: any = {};
|
||||
fields.forEach(field => {
|
||||
if (data[field] !== undefined) {
|
||||
filtered[field] = data[field];
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 最佳实践
|
||||
|
||||
- **默认带租户ID**:所有查询默认带 `tenantId` 过滤
|
||||
- **权限检查前置**:在业务逻辑开始前检查权限
|
||||
- **统一权限服务**:使用统一的权限服务处理权限检查
|
||||
- **缓存权限**:缓存用户权限,提高性能
|
||||
- **审计日志**:记录权限相关操作的审计日志
|
||||
|
||||
---
|
||||
|
||||
## 4. 权限验证
|
||||
|
||||
### 4.1 实现方法
|
||||
|
||||
#### 4.1.1 中间件验证
|
||||
|
||||
**适用场景**:API 路由权限验证
|
||||
|
||||
**实现**:
|
||||
- 创建权限验证中间件
|
||||
- 在路由中应用中间件
|
||||
- 验证用户是否有权限访问资源
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// 权限中间件
|
||||
const authorize = (requiredPermission: string) => {
|
||||
return (req, res, next) => {
|
||||
const user = req.user;
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
if (!user.permissions.includes(requiredPermission) && user.role !== 'ADMIN') {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// 使用
|
||||
app.get('/products', authorize('READ:product'), productController.getProducts);
|
||||
app.post('/products', authorize('CREATE:product'), productController.createProduct);
|
||||
```
|
||||
|
||||
#### 4.1.2 服务层验证
|
||||
|
||||
**适用场景**:业务逻辑权限验证
|
||||
|
||||
**实现**:
|
||||
- 在服务层检查权限
|
||||
- 验证用户是否有权限操作资源
|
||||
- 验证用户是否有权限访问数据
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async updateProduct(productId: string, updates: Partial<Product>, userId: string): Promise<Product> {
|
||||
// 检查操作权限
|
||||
if (!this.rbacService.hasPermission(userId, 'UPDATE:product')) {
|
||||
throw new ForbiddenException('You do not have permission to update products');
|
||||
}
|
||||
|
||||
// 获取商品
|
||||
const product = await this.productRepository.findById(productId);
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
|
||||
// 检查数据权限
|
||||
if (!this.rbacService.hasDataAccess(userId, product.tenantId, product.shopId)) {
|
||||
throw new ForbiddenException('You do not have access to this product');
|
||||
}
|
||||
|
||||
// 更新商品
|
||||
Object.assign(product, updates);
|
||||
return await this.productRepository.save(product);
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 最佳实践
|
||||
|
||||
- **多层验证**:在中间件和服务层都进行权限验证
|
||||
- **明确的错误信息**:返回清晰的权限错误信息
|
||||
- **权限缓存**:缓存用户权限,提高验证性能
|
||||
- **定期权限检查**:定期检查权限配置的正确性
|
||||
|
||||
---
|
||||
|
||||
## 5. 权限管理
|
||||
|
||||
### 5.1 角色管理
|
||||
|
||||
- **创建角色**:定义新角色
|
||||
- **修改角色**:修改角色的权限
|
||||
- **删除角色**:删除不需要的角色
|
||||
- **分配角色**:为用户分配角色
|
||||
|
||||
### 5.2 权限管理
|
||||
|
||||
- **定义权限**:定义新的权限
|
||||
- **分配权限**:为角色分配权限
|
||||
- **回收权限**:从角色中回收权限
|
||||
|
||||
### 5.3 用户管理
|
||||
|
||||
- **创建用户**:创建新用户并分配角色
|
||||
- **修改用户**:修改用户的角色和权限
|
||||
- **删除用户**:删除用户
|
||||
- **禁用用户**:暂时禁用用户
|
||||
|
||||
---
|
||||
|
||||
## 6. 安全考虑
|
||||
|
||||
### 6.1 防止权限提升
|
||||
|
||||
- **最小权限原则**:用户只获得必要的权限
|
||||
- **权限审计**:定期审计权限配置
|
||||
- **权限边界**:明确权限的边界
|
||||
|
||||
### 6.2 防止数据泄露
|
||||
|
||||
- **数据加密**:加密敏感数据
|
||||
- **访问控制**:严格控制数据访问
|
||||
- **审计日志**:记录数据访问日志
|
||||
|
||||
### 6.3 防止暴力破解
|
||||
|
||||
- **密码策略**:强密码要求
|
||||
- **登录限制**:限制登录尝试次数
|
||||
- **验证码**:使用验证码防止暴力破解
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试策略
|
||||
|
||||
### 7.1 单元测试
|
||||
|
||||
- 测试权限验证逻辑
|
||||
- 测试角色权限映射
|
||||
- 测试数据级权限过滤
|
||||
|
||||
### 7.2 集成测试
|
||||
|
||||
- 测试完整的权限流程
|
||||
- 测试不同角色的访问控制
|
||||
- 测试数据级权限的有效性
|
||||
|
||||
### 7.3 安全测试
|
||||
|
||||
- 测试权限提升攻击
|
||||
- 测试数据泄露风险
|
||||
- 测试暴力破解防护
|
||||
|
||||
---
|
||||
|
||||
## 8. 相关文档
|
||||
|
||||
- [Service_Design.md](./Service_Design.md)
|
||||
- [Data_Consistency.md](./Data_Consistency.md)
|
||||
- [API_Specs](./API_Specs/)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于服务设计文档,最后更新: 2026-03-18*
|
||||
433
docs/02_Backend/Service_Design.md
Normal file
433
docs/02_Backend/Service_Design.md
Normal file
@@ -0,0 +1,433 @@
|
||||
# 服务设计文档 (Crawlful Hub)
|
||||
|
||||
> **定位**:Crawlful Hub 后端服务设计文档 - 定义服务层架构、服务职责和实现规范。
|
||||
> **更新日期**: 2026-03-18
|
||||
> **最高优先级参考**: [Business_ClosedLoops.md](../00_Business/Business_ClosedLoops.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 服务层架构
|
||||
|
||||
### 1.1 核心原则
|
||||
|
||||
- **每个业务操作对应一个 Service**:前端的每个用户操作都必须有对应的后端 Service 处理
|
||||
- **服务编排**:通过 Service 层串联多个模块,实现业务流程的完整执行
|
||||
- **单一职责**:每个 Service 只负责一个核心业务领域
|
||||
- **依赖注入**:通过依赖注入实现服务间的解耦
|
||||
- **事务管理**:重要操作必须在事务中执行,确保数据一致性
|
||||
|
||||
### 1.2 服务层级结构
|
||||
|
||||
```
|
||||
Controller → Service (核心编排) → Repository / External API → 数据库/外部系统
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心服务列表
|
||||
|
||||
### 2.1 商品服务 (ProductService)
|
||||
|
||||
**职责**:管理商品全生命周期,包括创建、更新、查询、删除等操作
|
||||
|
||||
**核心方法**:
|
||||
- `createProduct()`: 创建商品
|
||||
- `updateProduct()`: 更新商品信息
|
||||
- `updatePrice()`: 更新商品价格,同时计算 ROI
|
||||
- `getProductById()`: 根据 ID 获取商品详情
|
||||
- `getProducts()`: 获取商品列表
|
||||
- `deleteProduct()`: 删除商品
|
||||
- `publishProduct()`: 发布商品到平台
|
||||
- `unpublishProduct()`: 从平台下架商品
|
||||
|
||||
**依赖**:
|
||||
- `PricingService`: 计算价格和 ROI
|
||||
- `InventoryService`: 管理商品库存
|
||||
- `MediaService`: 管理商品媒体资源
|
||||
|
||||
### 2.2 订单服务 (OrderService)
|
||||
|
||||
**职责**:管理订单全生命周期,包括创建、更新、查询、处理等操作
|
||||
|
||||
**核心方法**:
|
||||
- `createOrder()`: 创建订单
|
||||
- `updateOrderStatus()`: 更新订单状态
|
||||
- `getOrderById()`: 根据 ID 获取订单详情
|
||||
- `getOrders()`: 获取订单列表
|
||||
- `processOrder()`: 处理订单(包括库存扣减、物流安排等)
|
||||
- `splitOrder()`: 拆分订单
|
||||
- `mergeOrders()`: 合并订单
|
||||
- `handleExceptionOrder()`: 处理异常订单
|
||||
|
||||
**依赖**:
|
||||
- `InventoryService`: 管理库存
|
||||
- `LogisticsService`: 安排物流
|
||||
- `SettlementService`: 处理结算
|
||||
|
||||
### 2.3 定价服务 (PricingService)
|
||||
|
||||
**职责**:负责商品定价和利润计算
|
||||
|
||||
**核心方法**:
|
||||
- `calculatePrice()`: 计算商品价格
|
||||
- `calculateROI()`: 计算投资回报率
|
||||
- `calculateProfit()`: 计算利润
|
||||
- `getPricingStrategy()`: 获取定价策略
|
||||
- `updatePricingStrategy()`: 更新定价策略
|
||||
|
||||
**依赖**:
|
||||
- `CostService`: 计算成本
|
||||
- `MarketService`: 分析市场价格
|
||||
|
||||
### 2.4 库存服务 (InventoryService)
|
||||
|
||||
**职责**:管理库存,包括库存查询、扣减、预警等
|
||||
|
||||
**核心方法**:
|
||||
- `getInventory()`: 获取库存信息
|
||||
- `updateInventory()`: 更新库存
|
||||
- `deductInventory()`: 扣减库存
|
||||
- `reserveInventory()`: 预留库存
|
||||
- `releaseInventory()`: 释放预留库存
|
||||
- `getInventoryAlert()`: 获取库存预警
|
||||
|
||||
**依赖**:
|
||||
- `WarehouseService`: 管理仓库
|
||||
|
||||
### 2.5 物流服务 (LogisticsService)
|
||||
|
||||
**职责**:管理物流,包括物流选择、追踪、费用计算等
|
||||
|
||||
**核心方法**:
|
||||
- `selectLogisticsChannel()`: 选择物流渠道
|
||||
- `calculateFreight()`: 计算运费
|
||||
- `trackLogistics()`: 追踪物流状态
|
||||
- `createShippingLabel()`: 创建 shipping label
|
||||
|
||||
**依赖**:
|
||||
- `ThirdPartyLogisticsAPI`: 对接第三方物流 API
|
||||
|
||||
### 2.6 结算服务 (SettlementService)
|
||||
|
||||
**职责**:管理结算,包括收入计算、佣金扣除、打款等
|
||||
|
||||
**核心方法**:
|
||||
- `calculateSettlement()`: 计算结算金额
|
||||
- `processSettlement()`: 处理结算
|
||||
- `createSettlementBill()`: 创建结算账单
|
||||
- `processPayment()`: 处理付款
|
||||
|
||||
**依赖**:
|
||||
- `FinanceService`: 财务管理
|
||||
- `PaymentService`: 支付处理
|
||||
|
||||
### 2.7 商户服务 (MerchantService)
|
||||
|
||||
**职责**:管理商户,包括商户注册、认证、管理等
|
||||
|
||||
**核心方法**:
|
||||
- `registerMerchant()`: 注册商户
|
||||
- `verifyMerchant()`: 验证商户资质
|
||||
- `getMerchantById()`: 获取商户详情
|
||||
- `updateMerchant()`: 更新商户信息
|
||||
- `suspendMerchant()`: 暂停商户
|
||||
- `activateMerchant()`: 激活商户
|
||||
|
||||
**依赖**:
|
||||
- `VerificationService`: 资质验证
|
||||
- `RBACService`: 权限管理
|
||||
|
||||
### 2.8 权限服务 (RBACService)
|
||||
|
||||
**职责**:管理权限,包括角色定义、权限分配等
|
||||
|
||||
**核心方法**:
|
||||
- `createRole()`: 创建角色
|
||||
- `assignPermission()`: 分配权限
|
||||
- `checkPermission()`: 检查权限
|
||||
- `getUserRoles()`: 获取用户角色
|
||||
|
||||
**依赖**:
|
||||
- `UserService`: 用户管理
|
||||
|
||||
### 2.9 数据服务 (DataPipelineService)
|
||||
|
||||
**职责**:管理数据采集、清洗、处理等
|
||||
|
||||
**核心方法**:
|
||||
- `collectData()`: 采集数据
|
||||
- `cleanData()`: 清洗数据
|
||||
- `processData()`: 处理数据
|
||||
- `analyzeData()`: 分析数据
|
||||
|
||||
**依赖**:
|
||||
- `PlatformApiService`: 对接平台 API
|
||||
- `CrawlerService`: 网页爬虫
|
||||
|
||||
### 2.10 AI 服务 (AIService)
|
||||
|
||||
**职责**:提供 AI 相关功能,如智能定价、智能分析等
|
||||
|
||||
**核心方法**:
|
||||
- `predictPrice()`: 预测价格
|
||||
- `analyzeMarket()`: 分析市场
|
||||
- `optimizeStrategy()`: 优化策略
|
||||
- `detectAnomaly()`: 检测异常
|
||||
|
||||
**依赖**:
|
||||
- `MachineLearningModel`: 机器学习模型
|
||||
|
||||
---
|
||||
|
||||
## 3. 服务间调用规范
|
||||
|
||||
### 3.1 同步调用
|
||||
|
||||
**适用场景**:需要立即获取结果的操作
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// ProductService 调用 PricingService
|
||||
async updatePrice(productId: string, price: number): Promise<Product> {
|
||||
const product = await this.productRepository.findById(productId);
|
||||
const roi = await this.pricingService.calculateROI(product, price);
|
||||
product.price = price;
|
||||
product.roi = roi;
|
||||
return await this.productRepository.save(product);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 异步调用
|
||||
|
||||
**适用场景**:不需要立即获取结果的操作,如发送通知、数据处理等
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
// OrderService 调用 NotificationService
|
||||
async processOrder(orderId: string): Promise<void> {
|
||||
const order = await this.orderRepository.findById(orderId);
|
||||
// 处理订单
|
||||
await this.orderRepository.save(order);
|
||||
// 异步发送通知
|
||||
this.notificationService.sendOrderConfirmation(order).catch(err => {
|
||||
this.logger.error('Failed to send order confirmation', err);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 事务管理
|
||||
|
||||
### 4.1 事务边界
|
||||
|
||||
**定义**:一组操作要么全部成功,要么全部失败
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
@Transactional()
|
||||
async createOrder(orderData: OrderCreateDto): Promise<Order> {
|
||||
// 1. 创建订单
|
||||
const order = await this.orderRepository.create(orderData);
|
||||
|
||||
// 2. 扣减库存
|
||||
await this.inventoryService.deductInventory(
|
||||
orderData.productId,
|
||||
orderData.quantity
|
||||
);
|
||||
|
||||
// 3. 记录交易
|
||||
await this.transactionRepository.create({
|
||||
orderId: order.id,
|
||||
amount: order.totalAmount,
|
||||
type: 'ORDER_CREATED'
|
||||
});
|
||||
|
||||
return order;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 幂等性
|
||||
|
||||
**定义**:同一个请求执行多次,结果应该相同
|
||||
|
||||
**实现**:
|
||||
- 使用唯一的 `requestId`
|
||||
- 记录已处理的请求
|
||||
- 检查重复请求
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async processPayment(paymentData: PaymentDto): Promise<Payment> {
|
||||
// 检查是否已处理
|
||||
const existingPayment = await this.paymentRepository.findByRequestId(paymentData.requestId);
|
||||
if (existingPayment) {
|
||||
return existingPayment;
|
||||
}
|
||||
|
||||
// 处理支付
|
||||
const payment = await this.paymentRepository.create(paymentData);
|
||||
return payment;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 错误处理
|
||||
|
||||
### 5.1 业务错误
|
||||
|
||||
**定义**:业务逻辑错误,如库存不足、权限不足等
|
||||
|
||||
**处理**:
|
||||
- 抛出业务异常
|
||||
- 上层捕获并返回适当的错误信息
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async deductInventory(productId: string, quantity: number): Promise<void> {
|
||||
const inventory = await this.inventoryRepository.findByProductId(productId);
|
||||
if (inventory.quantity < quantity) {
|
||||
throw new BusinessException('Insufficient inventory');
|
||||
}
|
||||
|
||||
inventory.quantity -= quantity;
|
||||
await this.inventoryRepository.save(inventory);
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 系统错误
|
||||
|
||||
**定义**:系统级错误,如数据库连接失败、API 调用失败等
|
||||
|
||||
**处理**:
|
||||
- 捕获系统异常
|
||||
- 记录错误日志
|
||||
- 尝试重试或降级策略
|
||||
- 向上层返回适当的错误信息
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async callExternalApi(endpoint: string, data: any): Promise<any> {
|
||||
try {
|
||||
const response = await this.httpClient.post(endpoint, data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to call external API: ${endpoint}`, error);
|
||||
throw new SystemException('External API error');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 日志和监控
|
||||
|
||||
### 6.1 业务日志
|
||||
|
||||
**定义**:记录业务操作的日志
|
||||
|
||||
**内容**:
|
||||
- 操作人
|
||||
- 操作时间
|
||||
- 操作类型
|
||||
- 操作对象
|
||||
- 操作结果
|
||||
|
||||
**示例**:
|
||||
```typescript
|
||||
async updatePrice(productId: string, price: number): Promise<Product> {
|
||||
this.logger.info(`Updating price for product ${productId} to ${price}`);
|
||||
|
||||
try {
|
||||
const product = await this.productRepository.findById(productId);
|
||||
const roi = await this.pricingService.calculateROI(product, price);
|
||||
product.price = price;
|
||||
product.roi = roi;
|
||||
const updatedProduct = await this.productRepository.save(product);
|
||||
|
||||
this.logger.info(`Successfully updated price for product ${productId}`, {
|
||||
oldPrice: product.oldPrice,
|
||||
newPrice: price,
|
||||
roi: roi
|
||||
});
|
||||
|
||||
return updatedProduct;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to update price for product ${productId}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 性能监控
|
||||
|
||||
**定义**:监控服务性能
|
||||
|
||||
**指标**:
|
||||
- 响应时间
|
||||
- 调用次数
|
||||
- 错误率
|
||||
- 资源使用情况
|
||||
|
||||
**实现**:
|
||||
- 使用 Prometheus 等监控工具
|
||||
- 定期分析性能数据
|
||||
- 优化性能瓶颈
|
||||
|
||||
---
|
||||
|
||||
## 7. 服务版本管理
|
||||
|
||||
### 7.1 版本控制
|
||||
|
||||
**定义**:管理服务的版本
|
||||
|
||||
**策略**:
|
||||
- 使用语义化版本号
|
||||
- 向后兼容
|
||||
- 版本迁移计划
|
||||
|
||||
### 7.2 API 版本控制
|
||||
|
||||
**定义**:管理 API 的版本
|
||||
|
||||
**实现**:
|
||||
- 在 URL 中包含版本号
|
||||
- 支持多个版本并存
|
||||
- 逐步废弃旧版本
|
||||
|
||||
---
|
||||
|
||||
## 8. 部署和扩展性
|
||||
|
||||
### 8.1 部署策略
|
||||
|
||||
**定义**:服务的部署方式
|
||||
|
||||
**选项**:
|
||||
- 单体应用
|
||||
- 微服务
|
||||
- 容器化部署
|
||||
|
||||
### 8.2 扩展性
|
||||
|
||||
**定义**:服务的扩展能力
|
||||
|
||||
**实现**:
|
||||
- 水平扩展
|
||||
- 负载均衡
|
||||
- 缓存策略
|
||||
- 异步处理
|
||||
|
||||
---
|
||||
|
||||
## 9. 相关文档
|
||||
|
||||
- [API_Specs](./API_Specs/)
|
||||
- [Database_Design](./Database_Design.md)
|
||||
- [Architecture_Overview](./Architecture_Overview.md)
|
||||
|
||||
---
|
||||
|
||||
*本文档基于业务闭环设计,最后更新: 2026-03-18*
|
||||
Reference in New Issue
Block a user