架构设计文档
1. 概述
Reporting Service 采用 领域驱动设计 (DDD) 结合 六边形架构 (Hexagonal Architecture) 模式构建,实现业务逻辑与技术实现的解耦。
2. 架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ API Layer (端口) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ ReportController│ │ ExportController│ │ HealthController │ │
│ └────────┬────────┘ └────────┬────────┘ └─────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Application Layer ││
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────────────┐ ││
│ │ │ Commands/Queries │ │ Schedulers │ │ Application Services │ ││
│ │ │ - GenerateReport │ │ - CronJobs │ │ - ReportingAppService │ ││
│ │ │ - ExportReport │ │ - Periodic Tasks │ │ │ ││
│ │ └────────┬─────────┘ └──────────────────┘ └────────────────────────┘ ││
│ └───────────┼──────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Domain Layer (核心) ││
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────────────┐ ││
│ │ │ Aggregates │ │ Entities │ │ Value Objects │ ││
│ │ │ - ReportDefinit. │ │ - ReportFile │ │ - DateRange │ ││
│ │ │ - ReportSnapshot │ │ - AnalyticsMetric│ │ - ReportPeriod │ ││
│ │ └──────────────────┘ └──────────────────┘ │ - SnapshotData │ ││
│ │ └────────────────────────┘ ││
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────────────┐ ││
│ │ │ Domain Events │ │ Domain Services │ │ Repository Ports │ ││
│ │ │ - SnapshotCreated│ │ - ReportGenSvc │ │ (Interfaces Only) │ ││
│ │ │ - ReportExported │ │ │ │ │ ││
│ │ └──────────────────┘ └──────────────────┘ └────────────────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ ▲ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Infrastructure Layer (适配器) ││
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────────────┐ ││
│ │ │ Persistence │ │ External APIs │ │ Export Services │ ││
│ │ │ - Prisma/PG │ │ - LeaderboardSvc │ │ - ExcelExport │ ││
│ │ │ - Repositories │ │ - PlantingSvc │ │ - CSVExport │ ││
│ │ │ - Mappers │ │ │ │ - PDFExport │ ││
│ │ └──────────────────┘ └──────────────────┘ └────────────────────────┘ ││
│ │ ┌──────────────────┐ ┌──────────────────┐ ││
│ │ │ Redis Cache │ │ Kafka │ ││
│ │ │ - ReportCache │ │ - EventPublisher │ ││
│ │ └──────────────────┘ └──────────────────┘ ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
3. 目录结构
src/
├── api/ # API层 - 入站端口
│ ├── controllers/ # HTTP控制器
│ │ ├── report.controller.ts # 报表API
│ │ ├── export.controller.ts # 导出API
│ │ └── health.controller.ts # 健康检查
│ └── dto/ # 数据传输对象
│ ├── request/ # 请求DTO
│ └── response/ # 响应DTO
│
├── application/ # 应用层 - 用例编排
│ ├── commands/ # 命令处理器
│ │ ├── generate-report/ # 生成报表命令
│ │ └── export-report/ # 导出报表命令
│ ├── queries/ # 查询处理器
│ │ └── get-report-snapshot/ # 获取快照查询
│ ├── schedulers/ # 定时任务
│ │ └── report-generation.scheduler.ts
│ └── services/ # 应用服务
│ └── reporting-application.service.ts
│
├── domain/ # 领域层 - 业务核心
│ ├── aggregates/ # 聚合根
│ │ ├── report-definition/ # 报表定义聚合
│ │ └── report-snapshot/ # 报表快照聚合
│ ├── entities/ # 实体
│ │ ├── report-file.entity.ts
│ │ └── analytics-metric.entity.ts
│ ├── value-objects/ # 值对象
│ │ ├── date-range.vo.ts
│ │ ├── report-period.enum.ts
│ │ ├── report-type.enum.ts
│ │ └── snapshot-data.vo.ts
│ ├── events/ # 领域事件
│ │ ├── snapshot-created.event.ts
│ │ └── report-exported.event.ts
│ ├── repositories/ # 仓储接口
│ │ ├── report-definition.repository.interface.ts
│ │ └── report-snapshot.repository.interface.ts
│ └── services/ # 领域服务
│ └── report-generation.service.ts
│
├── infrastructure/ # 基础设施层 - 出站适配器
│ ├── persistence/ # 持久化
│ │ ├── prisma/ # Prisma配置
│ │ ├── repositories/ # 仓储实现
│ │ └── mappers/ # 对象映射器
│ ├── external/ # 外部服务客户端
│ │ ├── leaderboard-service/
│ │ └── planting-service/
│ ├── export/ # 导出服务实现
│ │ ├── excel-export.service.ts
│ │ ├── csv-export.service.ts
│ │ └── pdf-export.service.ts
│ └── redis/ # Redis缓存
│ └── report-cache.service.ts
│
├── shared/ # 共享模块
│ ├── decorators/ # 自定义装饰器
│ ├── filters/ # 异常过滤器
│ ├── guards/ # 守卫
│ ├── interceptors/ # 拦截器
│ └── strategies/ # Passport策略
│
└── config/ # 配置
├── app.config.ts
├── database.config.ts
└── redis.config.ts
4. 核心设计原则
4.1 依赖倒置原则 (DIP)
// 领域层定义接口 (端口)
export interface IReportSnapshotRepository {
save(snapshot: ReportSnapshot): Promise<ReportSnapshot>;
findById(id: bigint): Promise<ReportSnapshot | null>;
findByCodeAndPeriodKey(code: string, periodKey: string): Promise<ReportSnapshot | null>;
}
// 基础设施层实现接口 (适配器)
@Injectable()
export class ReportSnapshotRepository implements IReportSnapshotRepository {
constructor(private readonly prisma: PrismaService) {}
async save(snapshot: ReportSnapshot): Promise<ReportSnapshot> {
// Prisma实现细节
}
}
4.2 聚合根设计
// ReportSnapshot 聚合根
export class ReportSnapshot extends AggregateRoot {
private readonly _id: bigint;
private _reportType: ReportType;
private _reportCode: string;
private _reportPeriod: ReportPeriod;
private _snapshotData: SnapshotData;
private _events: DomainEvent[] = [];
// 工厂方法 - 创建新快照
public static create(props: CreateSnapshotProps): ReportSnapshot {
const snapshot = new ReportSnapshot(props);
snapshot.addDomainEvent(new SnapshotCreatedEvent(snapshot));
return snapshot;
}
// 从持久化重建 - 不触发事件
public static reconstitute(props: ReconstitutionProps): ReportSnapshot {
return new ReportSnapshot(props);
}
// 业务行为
public updateData(newData: SnapshotData): void {
this.validateDataUpdate(newData);
this._snapshotData = newData;
this._updatedAt = new Date();
}
}
4.3 值对象不可变性
// DateRange 值对象
export class DateRange {
private readonly _startDate: Date;
private readonly _endDate: Date;
private constructor(startDate: Date, endDate: Date) {
this._startDate = startDate;
this._endDate = endDate;
Object.freeze(this);
}
public static create(startDate: Date, endDate: Date): DateRange {
if (startDate > endDate) {
throw new DomainException('Start date must be before end date');
}
return new DateRange(startDate, endDate);
}
public equals(other: DateRange): boolean {
return this._startDate.getTime() === other._startDate.getTime() &&
this._endDate.getTime() === other._endDate.getTime();
}
}
5. 数据流
5.1 报表生成流程
┌─────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Client │───▶│ ReportCtrl │───▶│ GenerateHandler │───▶│ DomainSvc │
└─────────┘ └──────────────┘ └─────────────────┘ └──────┬───────┘
│
┌─────────────────────────────────────────────┘
▼
┌──────────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ External Services │───▶│ ReportSnapshot │───▶│ Repository │
│ (Leaderboard/Plant.) │ │ (Aggregate) │ │ (Save to DB) │
└──────────────────────┘ └──────────────────┘ └──────────────────┘
5.2 报表导出流程
┌─────────┐ ┌──────────────┐ ┌─────────────────┐
│ Client │───▶│ ExportCtrl │───▶│ ExportHandler │
└─────────┘ └──────────────┘ └────────┬────────┘
│
┌───────────────────────────────────┴───────────────────────────┐
▼ ▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ ExcelExportSvc │ │ CSVExportSvc │ │ PDFExportSvc │
│ (ExcelJS) │ │ (csv-stringify) │ │ (PDFKit) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│ │ │
└───────────────────────┴────────────────────────┘
▼
┌──────────────────────┐
│ ReportFile Entity │
│ (Save to Storage) │
└──────────────────────┘
6. 关键组件
6.1 报表类型 (ReportType)
| 类型 |
描述 |
数据来源 |
| LEADERBOARD_REPORT |
排行榜报表 |
Leaderboard Service |
| PLANTING_REPORT |
种植报表 |
Planting Service |
| COMMUNITY_REPORT |
社区报表 |
Community Service |
| SYSTEM_ACCOUNT_REPORT |
系统账户报表 |
Account Service |
| ANALYTICS_DASHBOARD |
分析仪表板 |
多数据源聚合 |
6.2 报表周期 (ReportPeriod)
| 周期 |
描述 |
用途 |
| DAILY |
日报 |
每日运营数据 |
| WEEKLY |
周报 |
周度趋势分析 |
| MONTHLY |
月报 |
月度业绩汇总 |
| QUARTERLY |
季报 |
季度财务报表 |
| YEARLY |
年报 |
年度总结 |
| CUSTOM |
自定义 |
灵活时间范围 |
6.3 导出格式 (OutputFormat)
| 格式 |
实现库 |
特点 |
| EXCEL |
ExcelJS |
支持样式、图表、多Sheet |
| CSV |
csv-stringify |
轻量、通用 |
| PDF |
PDFKit |
适合打印、分发 |
| JSON |
内置 |
API集成、数据交换 |
7. 扩展点
7.1 添加新报表类型
- 在
ReportType 枚举中添加新类型
- 创建对应的外部服务客户端 (如需要)
- 在
GenerateReportHandler 中添加数据获取逻辑
- 更新
ReportDefinition 种子数据
7.2 添加新导出格式
- 在
OutputFormat 枚举中添加新格式
- 创建新的导出服务 (实现
IExportService 接口)
- 在
ExportReportHandler 中注册新服务
7.3 集成新数据源
- 在
infrastructure/external/ 下创建服务客户端
- 定义数据传输接口
- 在应用层注入并使用
8. 安全考虑
- 认证: JWT Bearer Token (可配置)
- 授权: 基于角色的访问控制 (RBAC)
- 数据脱敏: 敏感数据在导出时进行脱敏处理
- 审计日志: 所有报表操作记录到审计表