# 架构设计文档

## 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<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 聚合根设计

```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)
- **数据脱敏**: 敏感数据在导出时进行脱敏处理
- **审计日志**: 所有报表操作记录到审计表
