# 审计系统架构设计

**版本**: v1.0.0  
**状态**: ✅ 核心功能已实现  
**最后更新**: 2025-12-07

---

## 系统架构

### 整体架构图

```
┌─────────────────────────────────────────────────────────────────────────┐
│                           业务应用层                                      │
│  ┌───────────────┐ ┌───────────────┐ ┌───────────────┐                  │
│  │   用户管理     │ │   审批引擎     │ │   表单管理     │  ...            │
│  │  @Auditable() │ │  @Auditable() │ │  @Auditable() │                  │
│  └───────────────┘ └───────────────┘ └───────────────┘                  │
└─────────────────────────────┬───────────────────────────────────────────┘
                              │
┌─────────────────────────────▼───────────────────────────────────────────┐
│                           审计系统层                                      │
│  ┌────────────────────────────────────────────────────────────────────┐ │
│  │                    AuditLogInterceptor                             │ │
│  │              自动捕获 HTTP 请求/响应上下文                            │ │
│  └────────────────────────────────────────────────────────────────────┘ │
│                              │                                          │
│  ┌──────────────┐  ┌──────────────────┐  ┌───────────────────────────┐ │
│  │ AuditService │←─│ HashChainService │←─│ IntegrityService          │ │
│  │  核心服务     │  │   哈希链生成      │  │  定时完整性校验             │ │
│  └──────────────┘  └──────────────────┘  └───────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────────────┘
                              │
┌─────────────────────────────▼───────────────────────────────────────────┐
│                           数据存储层                                      │
│  ┌───────────────┐ ┌───────────────────┐ ┌─────────────────────────┐   │
│  │   AuditLog    │ │ DatabaseChangeLog │ │ SensitiveOperationLog   │   │
│  │    主表       │ │    数据变更表      │ │     敏感操作表           │   │
│  └───────────────┘ └───────────────────┘ └─────────────────────────┘   │
│  ┌───────────────────────────────────────────────────────────────────┐ │
│  │                    IntegrityCheckLog                              │ │
│  │                      完整性校验记录                                 │ │
│  └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```

### 模块划分

| 模块 | 职责 | 依赖 |
|------|------|------|
| **AuditService** | 审计日志核心服务：记录、查询 | HashChainService, PrismaService |
| **HashChainService** | 哈希链生成和验证 | crypto (Node.js) |
| **IntegrityService** | 完整性校验、定时任务 | AuditService, ScheduleModule |
| **AuditLogInterceptor** | HTTP 请求自动捕获 | AuditService |
| **AuditController** | API 端点 | AuditService, IntegrityService |

### 与其他系统的集成

| 系统 | 集成方式 | 说明 |
|------|----------|------|
| **组织架构模块** | 装饰器自动记录 | 用户/角色/权限变更审计 |
| **审批引擎模块** | 手动 + 装饰器 | 审批操作审计 |
| **表单管理模块** | 装饰器自动记录 | 表单发布/修改审计 |
| **库存管理模块** | 手动记录 | 出入库/盘点审计 |
| **日志系统** | 相互独立 | 日志系统负责技术运维，审计系统负责业务合规 |

---

## 数据模型

### ER 图

```
┌─────────────────┐       ┌─────────────────────┐
│    AuditLog     │       │  DatabaseChangeLog  │
├─────────────────┤       ├─────────────────────┤
│ id (PK)         │←──────│ auditLogId (FK)     │
│ region ⭐       │       │ tableName           │
│ tenantId ⭐     │       │ operation           │
│ who             │       │ recordId            │
│ what            │       │ oldData             │
│ when            │       │ newData             │
│ where           │       │ changedFields       │
│ why             │       │ userId              │
│ how             │       │ timestamp           │
│ module          │       └─────────────────────┘
│ action          │
│ entityType      │       ┌─────────────────────────┐
│ entityId        │       │ SensitiveOperationLog   │
│ oldValue        │       ├─────────────────────────┤
│ newValue        │←──────│ auditLogId (FK)         │
│ changes         │       │ operationType           │
│ userId          │       │ description             │
│ sessionId       │       │ requiresApproval        │
│ traceId         │       │ approvedBy              │
│ requestId       │       │ approvalTime            │
│ ipAddress       │       │ riskLevel               │
│ userAgent       │       │ mfaVerified             │
│ deviceId        │       │ userId                  │
│ geoLocation     │       └─────────────────────────┘
│ status          │
│ errorMessage    │       ┌─────────────────────┐
│ duration        │       │ IntegrityCheckLog   │
│ isFinancial     │       ├─────────────────────┤
│ isSensitive     │       │ id (PK)             │
│ riskLevel       │       │ region ⭐           │
│ complianceLevel │       │ tenantId ⭐         │
│ retentionYears  │       │ checkType           │
│ previousHash    │       │ startTime           │
│ currentHash     │       │ endTime             │
│ signature       │       │ recordCount         │
│ createdAt       │       │ passCount           │
│ archivedAt      │       │ failCount           │
└─────────────────┘       │ failures            │
                          │ status              │
⭐ = 必需字段（多租户隔离）  │ createdAt           │
                          └─────────────────────┘
```

### 主审计日志表 (AuditLog)

```prisma
model AuditLog {
  id              String   @id @default(uuid())
  
  // 多租户/多区域（必需字段）
  region          String   // 部署区域: cn / us / eu / ap 等
  tenantId        String   // 租户 ID（多租户隔离）
  
  // 5W1H 基础信息
  who             String   // 谁执行的 (userId)
  what            String   // 执行了什么操作 (action)
  when            DateTime // 什么时间 (timestamp)
  where           String   // 在哪里 (IP地址)
  why             String?  // 为什么 (业务原因/备注)
  how             String   // 怎么做的 (method: API/UI/CLI)
  
  // 操作详情
  module          String   // 模块名称 (User/Approval/Expense)
  action          AuditAction // 操作类型
  entityType      String   // 实体类型
  entityId        String   // 实体ID
  
  // 变更内容
  oldValue        Json?    // 变更前的值
  newValue        Json?    // 变更后的值
  changes         Json?    // 具体变更字段
  
  // 上下文信息
  userId          String   // 操作用户ID
  sessionId       String   // 会话ID
  traceId         String   // 追踪ID (分布式追踪)
  requestId       String   // 请求ID
  
  // 环境信息
  ipAddress       String   // IP地址
  userAgent       String   // 用户代理
  deviceId        String?  // 设备ID
  geoLocation     String?  // 地理位置
  
  // 状态和结果
  status          AuditStatus @default(SUCCESS)
  errorMessage    String?  // 错误信息
  duration        Int?     // 执行耗时(ms)
  
  // SOX 合规字段
  isFinancial     Boolean  @default(false) // 是否财务相关
  isSensitive     Boolean  @default(false) // 是否敏感操作
  riskLevel       String   // 风险级别: HIGH/MEDIUM/LOW（权限变更、财务操作等为 HIGH）
  complianceLevel String   // 合规级别: HIGH/MEDIUM/LOW（决定保留年限）
  retentionYears  Int      @default(7)     // 保留年限（HIGH=7, MEDIUM=5, LOW=3）
  
  // 完整性保护
  previousHash    String?  // 上一条记录的哈希
  currentHash     String   // 当前记录的哈希
  signature       String?  // 数字签名
  
  // 元数据
  createdAt       DateTime @default(now())
  archivedAt      DateTime? // 归档时间

  // 索引：支持按区域和租户的隔离查询
  @@index([region, tenantId, createdAt])
  @@index([tenantId, userId, createdAt])
  @@schema("platform_audit")
}
```

### 操作类型枚举

```prisma
enum AuditAction {
  // CRUD 操作
  CREATE
  READ
  UPDATE
  DELETE
  BULK_UPDATE
  BULK_DELETE
  
  // 审批操作
  APPROVE
  REJECT
  RETURN
  FORWARD
  WITHDRAW
  
  // 用户操作
  LOGIN
  LOGOUT
  LOGIN_FAILED
  PASSWORD_CHANGE
  PERMISSION_CHANGE
  ROLE_CHANGE
  
  // 系统操作
  CONFIG_CHANGE
  BACKUP
  RESTORE
  EXPORT
  IMPORT
  
  // 财务操作
  PAYMENT
  REFUND
  INVOICE_GENERATE
  FINANCIAL_CLOSE

  @@schema("platform_audit")
}

enum AuditStatus {
  SUCCESS
  FAILED
  PARTIAL
  PENDING

  @@schema("platform_audit")
}
```

### 数据库变更日志表 (DatabaseChangeLog)

```prisma
model DatabaseChangeLog {
  id              String   @id @default(uuid())
  tableName       String   // 表名
  operation       String   // INSERT/UPDATE/DELETE
  recordId        String   // 记录ID
  oldData         Json?    // 变更前数据
  newData         Json?    // 变更后数据
  changedFields   String[] // 变更的字段列表
  userId          String   // 操作用户
  timestamp       DateTime @default(now())
  auditLogId      String   // 关联的审计日志ID

  @@schema("platform_audit")
}
```

### 敏感操作日志表 (SensitiveOperationLog)

```prisma
model SensitiveOperationLog {
  id              String    @id @default(uuid())
  operationType   String    // 操作类型
  description     String    // 操作描述
  requiresApproval Boolean  @default(false)
  approvedBy      String?   // 批准人
  approvalTime    DateTime?
  riskLevel       String    // HIGH/MEDIUM/LOW
  mfaVerified     Boolean   @default(false)
  userId          String
  auditLogId      String    // 关联的审计日志
  createdAt       DateTime  @default(now())

  @@schema("platform_audit")
}
```

### 完整性校验日志表 (IntegrityCheckLog)

```prisma
model IntegrityCheckLog {
  id              String   @id @default(uuid())
  
  // 多租户/多区域（必需字段）
  region          String   // 部署区域: cn / us / eu / ap 等
  tenantId        String   // 租户 ID（多租户隔离）
  
  checkType       String   // HASH_CHAIN/SIGNATURE/COUNT
  startTime       DateTime
  endTime         DateTime
  recordCount     Int      // 检查的记录数
  passCount       Int      // 通过数
  failCount       Int      // 失败数
  failures        Json?    // 失败的记录详情
  status          String   // SUCCESS/FAILED
  createdAt       DateTime @default(now())

  @@index([region, tenantId, createdAt])
  @@schema("platform_audit")
}
```

---

## 核心设计决策

### 决策 1: 哈希链完整性保护

**问题**: 如何确保审计日志不可篡改？

**方案**: SHA-256 哈希链

```
GENESIS → Log1 → Log2 → Log3 → ...
    │        │        │        │
    └─hash───┘─hash───┘─hash───┘
```

每条日志包含前一条日志的哈希值，形成链式结构。任何篡改都会破坏链条并被检测到。

**理由**:
- 简单可靠，无需外部服务
- 满足 SOX 合规要求
- 可扩展为数字签名方案

### 决策 2: 异步写入

**问题**: 审计日志写入会影响主业务性能吗？

**方案**: 异步写入，不阻塞主业务

```typescript
// 审计日志通过异步方式写入，不等待完成
this.auditService.log(auditData).catch(err => {
  this.logger.error('Audit log failed', err);
});
```

**理由**:
- 主业务响应时间不受影响
- 日志写入延迟 < 20ms
- 失败时不影响业务，只记录错误

### 决策 3: 装饰器 + 拦截器自动捕获

**问题**: 如何减少业务代码中的审计日志调用？

**方案**: 组合使用装饰器和 HTTP 拦截器

```typescript
@Post()
@Auditable()  // 装饰器标记需要审计
async createUser(@Body() dto: CreateUserDto) {
  // 无需手动记录审计日志
  return this.userService.create(dto);
}
```

**理由**:
- 最小侵入性，业务代码保持简洁
- 标准化审计信息采集
- 灵活支持手动记录特殊场景

### 决策 4: 5W1H + 多维度完整记录

**问题**: 审计日志应该记录哪些信息？

**方案**: 采用 5W1H 模型 + Region/Tenant 维度

| 维度 | 字段 | 说明 |
|------|------|------|
| Who | userId, sessionId | 谁执行的 |
| What | action, entityType, entityId | 做了什么 |
| When | createdAt | 什么时间 |
| Where | ipAddress, geoLocation | 在哪里 |
| Why | why (备注字段) | 为什么 |
| How | how (API/UI/CLI) | 如何执行 |
| **Region** | region | 哪个区域（cn/us/eu/ap） |
| **Tenant** | tenantId | 哪个租户 |

**理由**:
- 满足 SOX 合规完整性要求
- 便于多维度查询和追溯
- 支持多区域、多租户数据隔离
- 满足不同地区数据主权要求（如 GDPR）

### 决策 5: 多租户数据隔离

**问题**: 如何确保不同租户的审计日志隔离？

**方案**: region + tenantId 作为必需字段，所有查询自动注入

```typescript
// 所有审计日志写入时自动注入 region 和 tenantId
const auditLog = {
  region: request.region,      // 从请求上下文获取
  tenantId: request.tenantId,  // 从用户会话获取
  // ... 其他字段
};

// 所有查询自动添加租户过滤
const logs = await prisma.auditLog.findMany({
  where: {
    region: currentUser.region,
    tenantId: currentUser.tenantId,
    // ... 其他条件
  }
});
```

**理由**:
- 数据主权合规（不同区域数据不跨境）
- 租户隔离（SaaS 场景下租户数据完全隔离）
- 支持按区域配置不同的数据保留策略
- 便于按区域/租户进行独立的完整性检查

### 决策 6: 数据保留策略

**问题**: 审计日志应该保留多久？

**方案**: 分级保留策略（按 ComplianceLevel）

| 合规级别 | 保留期限 | 典型场景 |
|----------|----------|----------|
| HIGH | 7 年 | 财务操作、权限变更、系统配置（SOX 合规） |
| MEDIUM | 5 年 | 一般业务数据增删改、审批操作 |
| LOW | 3 年 | 数据查询、草稿保存、非关键字段更新 |
| 完整性校验 | 1 年 | 运维需求 |

**理由**:
- 满足 SOX 法规要求（财务记录 7 年）
- 三级分类平衡存储成本和合规需求
- 支持自动归档和冷热分离

---

## 技术实现

### 目录结构

```
backend/src/audit/
├── audit.module.ts                 # 模块定义
├── audit.service.ts                # 核心服务
├── audit.controller.ts             # API 控制器
├── services/
│   ├── hash-chain.service.ts       # 哈希链服务
│   └── integrity.service.ts        # 完整性校验服务
├── interceptors/
│   └── audit-log.interceptor.ts    # HTTP 请求拦截器
├── decorators/
│   ├── auditable.decorator.ts      # @Auditable
│   ├── sensitive.decorator.ts      # @Sensitive
│   └── financial.decorator.ts      # @Financial
└── dto/
    └── query-audit-log.dto.ts      # DTO
```

### 核心服务实现

#### HashChainService

```typescript
import * as crypto from 'crypto';

@Injectable()
export class HashChainService {
  private readonly algorithm = 'sha256';
  
  /**
   * 计算审计日志的哈希值
   */
  calculateHash(log: AuditLogData, previousHash: string): string {
    const data = JSON.stringify({
      who: log.who,
      what: log.what,
      when: log.when.toISOString(),
      where: log.where,
      module: log.module,
      action: log.action,
      entityType: log.entityType,
      entityId: log.entityId,
      previousHash,
    });
    
    return crypto.createHash(this.algorithm).update(data).digest('hex');
  }
  
  /**
   * 验证哈希链完整性
   */
  async verifyChain(logs: AuditLog[]): Promise<VerificationResult> {
    const failures: string[] = [];
    
    for (let i = 0; i < logs.length; i++) {
      const log = logs[i];
      const expectedPreviousHash = i === 0 ? 'GENESIS' : logs[i - 1].currentHash;
      
      if (log.previousHash !== expectedPreviousHash) {
        failures.push(log.id);
      }
      
      const expectedCurrentHash = this.calculateHash(log, log.previousHash);
      if (log.currentHash !== expectedCurrentHash) {
        failures.push(log.id);
      }
    }
    
    return {
      valid: failures.length === 0,
      totalRecords: logs.length,
      failures,
    };
  }
}
```

#### AuditService

```typescript
@Injectable()
export class AuditService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly hashChainService: HashChainService,
  ) {}
  
  /**
   * 记录审计日志
   */
  async log(data: CreateAuditLogDto): Promise<AuditLog> {
    // 获取上一条日志的哈希
    const lastLog = await this.prisma.auditLog.findFirst({
      orderBy: { createdAt: 'desc' },
      select: { currentHash: true },
    });
    
    const previousHash = lastLog?.currentHash ?? 'GENESIS';
    
    // 计算当前日志的哈希
    const currentHash = this.hashChainService.calculateHash(data, previousHash);
    
    // 脱敏处理
    const sanitizedData = this.sanitizeData(data);
    
    // 写入数据库
    return this.prisma.auditLog.create({
      data: {
        ...sanitizedData,
        previousHash,
        currentHash,
        createdAt: new Date(),
      },
    });
  }
  
  /**
   * 敏感字段脱敏
   */
  private sanitizeData(data: any): any {
    const sensitiveFields = ['password', 'token', 'secret', 'accessToken', 'refreshToken'];
    
    const sanitize = (obj: any): any => {
      if (typeof obj !== 'object' || obj === null) return obj;
      
      const result = Array.isArray(obj) ? [] : {};
      for (const [key, value] of Object.entries(obj)) {
        if (sensitiveFields.includes(key.toLowerCase())) {
          result[key] = '***REDACTED***';
        } else if (typeof value === 'object') {
          result[key] = sanitize(value);
        } else {
          result[key] = value;
        }
      }
      return result;
    };
    
    return sanitize(data);
  }
}
```

#### 拦截器实现

```typescript
@Injectable()
export class AuditLogInterceptor implements NestInterceptor {
  constructor(
    private readonly auditService: AuditService,
    private readonly reflector: Reflector,
  ) {}
  
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const isAuditable = this.reflector.get<boolean>('auditable', context.getHandler());
    
    if (!isAuditable) {
      return next.handle();
    }
    
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    const startTime = Date.now();
    
    return next.handle().pipe(
      tap(async (response) => {
        const duration = Date.now() - startTime;
        
        await this.auditService.log({
          who: user?.id ?? 'anonymous',
          what: `${request.method} ${request.url}`,
          when: new Date(),
          where: request.ip,
          how: 'API',
          module: this.extractModule(request.url),
          action: this.methodToAction(request.method),
          entityType: this.extractEntityType(request.url),
          entityId: request.params?.id ?? '',
          userId: user?.id ?? '',
          sessionId: request.sessionId ?? '',
          traceId: request.headers['x-trace-id'] ?? '',
          requestId: request.headers['x-request-id'] ?? '',
          ipAddress: request.ip,
          userAgent: request.headers['user-agent'] ?? '',
          status: 'SUCCESS',
          duration,
          isFinancial: this.reflector.get<boolean>('financial', context.getHandler()) ?? false,
          isSensitive: this.reflector.get<boolean>('sensitive', context.getHandler()) ?? false,
          complianceLevel: 'MEDIUM',
          retentionYears: 3,
        });
      }),
      catchError((error) => {
        // 记录失败的操作
        this.auditService.log({
          // ... 失败日志
          status: 'FAILED',
          errorMessage: error.message,
        });
        throw error;
      }),
    );
  }
}
```

### 装饰器实现

```typescript
// @Auditable() - 标记需要审计的操作
export const Auditable = () => SetMetadata('auditable', true);

// @Sensitive() - 标记敏感操作
export const Sensitive = () => SetMetadata('sensitive', true);

// @Financial() - 标记财务操作（7 年保留）
export const Financial = () => SetMetadata('financial', true);
```

---

## 完整性保护机制

### 哈希链原理

```
┌────────────┐     ┌────────────┐     ┌────────────┐
│  Log #1    │     │  Log #2    │     │  Log #3    │
├────────────┤     ├────────────┤     ├────────────┤
│ prevHash:  │     │ prevHash:  │     │ prevHash:  │
│ "GENESIS"  │────▶│ "abc123"   │────▶│ "def456"   │
├────────────┤     ├────────────┤     ├────────────┤
│ currHash:  │     │ currHash:  │     │ currHash:  │
│ "abc123"   │     │ "def456"   │     │ "ghi789"   │
└────────────┘     └────────────┘     └────────────┘
```

### 自动校验任务

```typescript
@Injectable()
export class IntegrityService {
  @Cron('0 2 * * *')  // 每天凌晨 2 点
  async dailyCheck() {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
    
    const logs = await this.auditService.findByDateRange(yesterday, new Date());
    const result = await this.hashChainService.verifyChain(logs);
    
    await this.saveCheckResult('DAILY', result);
    
    if (!result.valid) {
      await this.alertService.sendIntegrityAlert(result.failures);
    }
  }
  
  @Cron('0 3 * * 0')  // 每周日凌晨 3 点
  async weeklyCheck() {
    const lastWeek = new Date();
    lastWeek.setDate(lastWeek.getDate() - 7);
    
    const logs = await this.auditService.findByDateRange(lastWeek, new Date());
    const result = await this.hashChainService.verifyChain(logs);
    
    await this.saveCheckResult('WEEKLY', result);
  }
}
```

---

## 性能优化

### 性能指标

| 指标 | 目标值 | 实际值 |
|------|--------|--------|
| 审计日志写入 | < 20ms | ~10-20ms（异步） |
| 哈希计算 | < 5ms | ~1-2ms |
| 查询响应 | < 200ms | ~50-200ms |
| 完整性检查 | < 5秒/万条 | ~1-5秒/万条 |

### 优化策略

1. **异步写入**: 不阻塞主业务请求
2. **批量写入**: 高并发时批量入库
3. **索引优化**: 按常用查询字段建立索引
4. **分区策略**: 按时间分区（月度）
5. **冷热分离**: 历史数据归档到冷存储

### 推荐索引

```sql
-- 多租户隔离查询（最重要，所有查询都需要）
CREATE INDEX idx_audit_tenant_time ON audit_log (region, tenant_id, created_at DESC);

-- 用户查询优化（租户内）
CREATE INDEX idx_audit_tenant_user ON audit_log (tenant_id, user_id, created_at DESC);

-- 实体查询优化（租户内）
CREATE INDEX idx_audit_tenant_entity ON audit_log (tenant_id, entity_type, entity_id, created_at DESC);

-- 模块查询优化（租户内）
CREATE INDEX idx_audit_tenant_module ON audit_log (tenant_id, module, action, created_at DESC);

-- 合规查询优化（租户内）
CREATE INDEX idx_audit_tenant_financial ON audit_log (tenant_id, is_financial, created_at DESC) 
  WHERE is_financial = true;
CREATE INDEX idx_audit_tenant_sensitive ON audit_log (tenant_id, is_sensitive, created_at DESC) 
  WHERE is_sensitive = true;

-- 追踪查询优化
CREATE INDEX idx_audit_trace ON audit_log (trace_id);

-- 分区键（按 region + 月份）
-- 建议使用 PostgreSQL 分区表，按 region 和 created_at 月份分区
```

### 分区策略

```sql
-- 按区域和月份分区（PostgreSQL 示例）
CREATE TABLE audit_log (
  id UUID PRIMARY KEY,
  region VARCHAR(10) NOT NULL,
  tenant_id VARCHAR(50) NOT NULL,
  created_at TIMESTAMPTZ NOT NULL,
  -- ... 其他字段
) PARTITION BY LIST (region);

-- 每个区域一个分区，区域内再按月分区
CREATE TABLE audit_log_cn PARTITION OF audit_log FOR VALUES IN ('cn')
  PARTITION BY RANGE (created_at);
CREATE TABLE audit_log_us PARTITION OF audit_log FOR VALUES IN ('us')
  PARTITION BY RANGE (created_at);
CREATE TABLE audit_log_eu PARTITION OF audit_log FOR VALUES IN ('eu')
  PARTITION BY RANGE (created_at);
```

---

## 存储估算

### 单条日志大小

| 字段类别 | 估算大小 |
|----------|----------|
| 基础字段 (5W1H) | ~200 bytes |
| 操作详情 | ~100 bytes |
| 变更内容 (JSON) | ~500-1000 bytes |
| 上下文信息 | ~200 bytes |
| 完整性字段 | ~200 bytes |
| **总计** | ~1.2-2 KB |

### 存储增长预估

| 时间范围 | 日均 10 万条 | 日均 50 万条 |
|----------|-------------|-------------|
| 日 | ~200 MB | ~1 GB |
| 月 | ~6 GB | ~30 GB |
| 年 | ~72 GB | ~360 GB |
| 7 年（SOX） | ~500 GB | ~2.5 TB |

---

## 安全设计

### 访问控制

| 角色 | 权限 |
|------|------|
| admin | 全部访问 |
| auditor | 查询、导出、验证 |
| finance | 仅财务日志 |
| security | 仅敏感日志 |
| developer | 追踪链路（traceId） |

### 敏感数据处理

自动脱敏的字段：
- password / passwordHash
- token / accessToken / refreshToken
- secret / apiKey
- creditCard / ssn

### 防篡改机制

1. **哈希链**: 每条记录包含前一条的哈希
2. **数据库规则**: 禁止 UPDATE/DELETE（可选）
3. **定期校验**: 每日/每周自动检查
4. **告警通知**: 完整性失败立即告警

---

## 部署配置

### 环境变量

```bash
# 审计系统配置
AUDIT_ENABLED=true
AUDIT_ASYNC=true
AUDIT_RETENTION_YEARS=7
AUDIT_INTEGRITY_CHECK_ENABLED=true
```

### 定时任务配置

```typescript
// backend/src/audit/audit.module.ts
@Module({
  imports: [
    ScheduleModule.forRoot(),
    // ...
  ],
})
export class AuditModule {}
```

---

## 相关文档

- [PRD.md](PRD.md) - 产品需求文档
- [API.md](API.md) - API 接口文档
- [TODO.md](TODO.md) - 开发待办
- [日志系统](../logging-system/PRD.md) - 技术运维日志

---

**最后更新**: 2025-12-07
