# 架构设计文档 ## 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) ```typescript // 领域层定义接口 (端口) export interface IReportSnapshotRepository { save(snapshot: ReportSnapshot): Promise; findById(id: bigint): Promise; findByCodeAndPeriodKey(code: string, periodKey: string): Promise; } // 基础设施层实现接口 (适配器) @Injectable() export class ReportSnapshotRepository implements IReportSnapshotRepository { constructor(private readonly prisma: PrismaService) {} async save(snapshot: ReportSnapshot): Promise { // Prisma实现细节 } } ``` ### 4.2 聚合根设计 ```typescript // 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 值对象不可变性 ```typescript // 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 添加新报表类型 1. 在 `ReportType` 枚举中添加新类型 2. 创建对应的外部服务客户端 (如需要) 3. 在 `GenerateReportHandler` 中添加数据获取逻辑 4. 更新 `ReportDefinition` 种子数据 ### 7.2 添加新导出格式 1. 在 `OutputFormat` 枚举中添加新格式 2. 创建新的导出服务 (实现 `IExportService` 接口) 3. 在 `ExportReportHandler` 中注册新服务 ### 7.3 集成新数据源 1. 在 `infrastructure/external/` 下创建服务客户端 2. 定义数据传输接口 3. 在应用层注入并使用 ## 8. 安全考虑 - **认证**: JWT Bearer Token (可配置) - **授权**: 基于角色的访问控制 (RBAC) - **数据脱敏**: 敏感数据在导出时进行脱敏处理 - **审计日志**: 所有报表操作记录到审计表