主要更新: 1. 后端核心底座完成 (M1-M6): - 健康检查、指标监控、分布式锁 - 事件总线、队列系统、事务管理 - 安全守卫、多租户隔离、存储适配器 - 审计日志、配置管理、多语言支持 2. 前端迁移到 Ant Design Vue: - 从 Element Plus 迁移到 Ant Design Vue - 完善 system 模块 (role/menu/dept) - 修复依赖和配置问题 3. 文档完善: - AI 开发工作流文档 - 架构约束和开发规范 - 项目进度跟踪 4. 其他改进: - 修复编译错误和类型问题 - 完善测试用例 - 优化项目结构
6.6 KiB
6.6 KiB
基础能力集成(Kafka / Redis / 队列 / 事务)
总览
- 目标: 将事件、任务、缓存、事务能力以统一规范接入 App/Core/Infrastructure 三层,替代“散落式调用”。
- 约束: 由 Application 发起流程;Core 编排业务规则且不直接依赖外设;Infrastructure 提供具体实现。
1) 事务(TypeORM)
- 发起层: Application(用例级事务边界)
- 使用方式:
// application/xxx.app.service.ts
constructor(private readonly dataSource: DataSource, private readonly core: XxxCoreService) {}
async runUseCase(dto: Dto) {
return await this.dataSource.transaction(async (manager) => {
// 将 manager 注入到仓储实现(通过请求域注入或方法透传)
await this.core.handle(dto); // Core 内仅调用仓储接口
});
}
- 规范:
- 事务只在 Application 层开启;Core 不直接操作事务对象
- 多仓储参与时基于同一
EntityManager
2) 队列(Bull/BullMQ 或 DB 队列)
- 发起层: Application(用例结束后入队)
- 接入点:
UnifiedQueueService或具体 Provider(如BullQueueProvider/DatabaseQueueProvider)
// application/xxx.app.service.ts
constructor(private readonly queue: UnifiedQueueService) {}
await this.queue.addJob('media', 'generateThumbnail', { attId }, { attempts: 3, delay: 0 });
- 处理器建议放置:
infrastructure/queues/xxx.processor.ts或独立消费模块
- 规范:
- 入队数据为最小必要字段(ID/键);大对象存储DB再查
3) 事件(Kafka / DB Outbox)
- 发起层: Application(领域事件在用例完成后发布)
- 接入点:
DomainEventService(绑定IEventBus,默认 DB Outbox,可切 Kafka)
// application/xxx.app.service.ts
constructor(private readonly events: DomainEventService) {}
await this.events.publishEvent(
'system.settings.storage.updated',
String(siteId),
String(siteId),
{ storageType },
);
- 配置切换:
- 通过
EventBusModule的 provider 切换DatabaseEventBusProvider⇄KafkaEventBusProvider
- 通过
- 规范:
- 事件名格式:
domain.aggregate.action - 载荷仅含必要业务字段,带上
tenantId/siteId
- 事件名格式:
4) Redis(缓存/限流/幂等)
- 发起层: Application(流程性控制)或 Infrastructure(技术性实现)
- 接入点:
RedisProvider(vendor/redis) - 常见场景:
- 读多写少配置缓存:
sys_config读取后短缓存 - 上传限流/防刷:基于 IP/UID 的计数器
- 幂等:
SETNX+ 过期控制
- 读多写少配置缓存:
5) 存储(本地/云)
- 发起层: Application 调用 Core 规则后,委托 Infrastructure Provider 落地
- 接入点:
infrastructure/providers/*或vendor/storage/*(如LocalStorageAdapter) - 规范:
- Provider 通过接口注入,便于切换 OSS/COS/Qiniu
6) 在三层中的放置原则
- Application: 事务、入队、发事件、协调多 Core 服务
- Core: 纯业务规则/策略与仓储接口;不直接依赖 Kafka/Redis/队列
- Infrastructure: 队列消费者、存储/HTTP/Redis 具体实现、TypeORM 仓储实现
7) 示例:Upload 模块接入
- 用例: 上传完成 → 入库附件 → 入队生成缩略图
// application/upload.app.service.ts
await this.dataSource.transaction(async (manager) => {
const att = await this.core.validateAndPlan(fileMeta);
await this.attachmentRepo.withManager(manager).save(att);
});
await this.queue.addJob('media', 'generateThumbnail', { attId: att.id });
8) 示例:Settings.Storage 接入
- 用例: 切换默认存储 → 写
sys_config→ 发布事件 → 入队校验可用性
// application/storage-settings.app.service.ts
await this.dataSource.transaction(async (manager) => {
await this.core.ensureExclusiveDefault(type);
await this.sysConfigRepo.withManager(manager).setValue(key, value);
});
await this.events.publishEvent('system.settings.storage.updated', String(siteId), String(siteId), { type });
await this.queue.addJob('ops', 'validateStorage', { type, siteId });
9) 配置与健康
- 配置在
VendorModule/EventBusModule注入;通过ConfigService读取连接信息 - 健康检查:将 Redis/队列/事件写入健康聚合输出
10) 方案B:vendor/storage 标准结构(平台外设)
vendor/storage/
├── index.ts # 统一导出(接口、Token、模块、适配器)
├── storage.module.ts # 可配置模块(选择具体实现)
├── tokens.ts # 注入Token常量(如 STORAGE_ADAPTER)
├── interfaces/
│ ├── storage-adapter.ts # 适配器接口(平台标准)
│ └── types.ts # 通用类型(上传结果、签名参数等)
├── adapters/
│ ├── local.adapter.ts # 本地实现
│ ├── aliyun-oss.adapter.ts # 阿里云实现
│ ├── qcloud-cos.adapter.ts # 腾讯云实现
│ └── qiniu.adapter.ts # 七牛云实现
├── providers/
│ ├── storage.provider.ts # 工厂: 按配置/站点解析适配器
│ └── registry.ts # 多实例注册表 Map<siteId, adapter> + TTL
├── health/storage.health.ts # 健康检查(各实现可选实现)
├── config/schema.ts # 配置Schema(必需项校验)
└── __tests__/
├── storage.contract.spec.ts # 契约测试(接口一致性)
└── adapters/*.spec.ts # 各实现最小测试
- Token
export const STORAGE_ADAPTER = 'STORAGE_ADAPTER';
- 接口
export interface StorageAdapter {
upload(params: { key: string; content: Buffer | NodeJS.ReadableStream; mime?: string }): Promise<{ url: string; key: string }>;
delete(key: string): Promise<void>;
signUpload?(params: { key: string; expiresSec?: number; mime?: string }): Promise<{ url: string; headers?: Record<string,string>; fields?: Record<string,string> }>;
healthCheck?(): Promise<boolean>;
}
- 按 site_id 解析(站点启用 > 跟随系统 > local 兜底)
function resolveAdapter(siteId: number): StorageAdapter {
const enabled = readSiteEnabled(siteId); // storage_xxx with is_use
const type = enabled?.type ?? readPlatformDefault();
const options = enabled?.options ?? readPlatformOptions(type) ?? {};
return registry.getOrCreate(siteId, type, options);
}
- 本地隔离路径:
upload/site_{siteId}/... - 健康检查:对活跃站点适配器定期
healthCheck()并聚合到 Health
说明:方案B 将“三方存储”视为外设,接口与实现均在 vendor;业务通过 Token 注入与按站点解析工厂使用,core 无需暴露存储端口。