# 审计功能集成示例：用户管理模块

> **示例说明**: 本文档展示如何将审计功能集成到用户管理模块  
> **难度**: ⭐⭐ 中级  
> **预计时间**: 30 分钟

---

## 📋 改造前后对比

### 改造前（无审计）

```typescript
@Controller('users')
export class UsersController {
  @Post()
  async create(@Body() dto: CreateUserDto) {
    return this.usersService.create(dto);
  }
}
```

### 改造后（完整审计）

```typescript
@Controller('users')
export class UsersController {
  @Post()
  @Auditable()              // ✅ 添加审计装饰器
  @RequirePermissions('user:create')
  async create(@Body() dto: CreateUserDto) {
    return this.usersService.create(dto);
  }
  
  @Patch(':id/password')
  @Auditable()              // ✅ 添加审计装饰器
  @Sensitive()              // ⚠️ 标记为敏感操作
  @RequirePermissions('user:update')
  async changePassword(
    @Param('id') id: string,
    @Body() dto: ChangePasswordDto,
  ) {
    return this.usersService.changePassword(id, dto);
  }
}
```

---

## 🔧 完整实现

### 1. Controller 层（自动审计）

```typescript
// users.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  ParseUUIDPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';
import { Auditable, Sensitive } from '@/audit/decorators/auditable.decorator';
import { RequirePermissions } from '@/common/decorators/permissions.decorator';
import { CurrentUser } from '@/common/decorators/current-user.decorator';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  /**
   * 创建用户
   * ✅ 普通审计
   */
  @Post()
  @Auditable()  // 🔍 自动记录创建操作
  @RequirePermissions('user:create')
  async create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  /**
   * 更新用户信息
   * ✅ 普通审计
   */
  @Patch(':id')
  @Auditable()  // 🔍 自动记录更新操作
  @RequirePermissions('user:update')
  async update(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() updateUserDto: UpdateUserDto,
  ) {
    return this.usersService.update(id, updateUserDto);
  }

  /**
   * 修改密码
   * ⚠️ 敏感操作 - 需要特别关注
   */
  @Patch(':id/password')
  @Auditable()
  @Sensitive()  // ⚠️ 标记为敏感操作
  @RequirePermissions('user:update')
  async changePassword(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: { oldPassword: string; newPassword: string },
  ) {
    return this.usersService.changePassword(id, dto);
  }

  /**
   * 重置密码
   * ⚠️ 敏感操作 - 管理员重置他人密码
   */
  @Post(':id/reset-password')
  @Auditable()
  @Sensitive()  // ⚠️ 标记为敏感操作
  @RequirePermissions('user:admin')
  async resetPassword(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() admin: any,
  ) {
    return this.usersService.resetPassword(id, admin.id);
  }

  /**
   * 分配角色
   * ⚠️ 敏感操作 - 权限变更
   */
  @Patch(':id/roles')
  @Auditable()
  @Sensitive()  // ⚠️ 标记为敏感操作
  @RequirePermissions('user:assign-roles')
  async assignRoles(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: { roleIds: string[] },
  ) {
    return this.usersService.assignRoles(id, dto.roleIds);
  }

  /**
   * 删除用户
   * ⚠️ 敏感操作 - 高风险
   */
  @Delete(':id')
  @Auditable()
  @Sensitive()  // ⚠️ 标记为敏感操作
  @RequirePermissions('user:delete')
  async remove(@Param('id', ParseUUIDPipe) id: string) {
    return this.usersService.remove(id);
  }

  /**
   * 导出用户数据
   * ⚠️ 敏感操作 - 数据导出
   */
  @Get('export')
  @Auditable()
  @Sensitive()  // ⚠️ 标记为敏感操作
  @RequirePermissions('user:export')
  async exportUsers() {
    return this.usersService.exportToExcel();
  }
}
```

---

### 2. Service 层（手动审计详细变更）

```typescript
// users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@/prisma/prisma.service';
import { AuditService } from '@/audit/audit.service';
import { AuditAction, AuditStatus } from '@prisma/client';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UsersService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly auditService: AuditService,  // ✅ 注入 AuditService
  ) {}

  /**
   * 创建用户
   * 自动审计已在 Controller 层完成，这里不需要额外代码
   */
  async create(dto: CreateUserDto) {
    const hashedPassword = await bcrypt.hash(dto.password, 10);
    
    return this.prisma.user.create({
      data: {
        ...dto,
        passwordHash: hashedPassword,
      },
    });
  }

  /**
   * 分配角色
   * 手动记录详细的权限变更
   */
  async assignRoles(userId: string, roleIds: string[]) {
    // 1. 获取旧的角色列表
    const oldRoles = await this.prisma.userRoleRel.findMany({
      where: { userId },
      include: { role: true },
    });

    // 2. 执行角色更新
    await this.prisma.$transaction([
      // 删除旧的角色关联
      this.prisma.userRoleRel.deleteMany({ where: { userId } }),
      // 创建新的角色关联
      this.prisma.userRoleRel.createMany({
        data: roleIds.map(roleId => ({ userId, roleId })),
      }),
    ]);

    // 3. 获取新的角色列表
    const newRoles = await this.prisma.userRoleRel.findMany({
      where: { userId },
      include: { role: true },
    });

    // 4. 手动记录详细的审计日志
    await this.auditService.log({
      // 5W1H
      who: 'system',  // 或从请求上下文获取
      what: `为用户分配角色`,
      
      // 操作详情
      module: 'User',
      action: AuditAction.ROLE_CHANGE,
      entityType: 'User',
      entityId: userId,
      
      // 详细变更
      oldValue: {
        roles: oldRoles.map(r => ({ id: r.roleId, name: r.role.name })),
      },
      newValue: {
        roles: newRoles.map(r => ({ id: r.roleId, name: r.role.name })),
      },
      changes: {
        added: newRoles
          .filter(nr => !oldRoles.some(or => or.roleId === nr.roleId))
          .map(r => r.role.name),
        removed: oldRoles
          .filter(or => !newRoles.some(nr => nr.roleId === or.roleId))
          .map(r => r.role.name),
      },
      
      // 标记为敏感操作
      isSensitive: true,
    });

    return newRoles;
  }

  /**
   * 重置密码（管理员操作）
   * 手动记录详细的审计日志
   */
  async resetPassword(userId: string, adminId: string) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 生成临时密码
    const tempPassword = this.generateTempPassword();
    const hashedPassword = await bcrypt.hash(tempPassword, 10);

    // 更新密码
    await this.prisma.user.update({
      where: { id: userId },
      data: {
        passwordHash: hashedPassword,
        passwordResetRequired: true,
        passwordResetAt: new Date(),
      },
    });

    // 手动记录审计日志
    await this.auditService.log({
      who: adminId,
      what: `重置用户密码: ${user.username}`,
      why: '管理员重置',
      
      module: 'User',
      action: AuditAction.PASSWORD_RESET,
      entityType: 'User',
      entityId: userId,
      
      oldValue: {
        passwordResetRequired: false,
      },
      newValue: {
        passwordResetRequired: true,
        passwordResetAt: new Date().toISOString(),
      },
      
      isSensitive: true,
    });

    return { tempPassword };
  }

  /**
   * 删除用户（软删除）
   * 手动记录删除前的完整信息
   */
  async remove(id: string) {
    // 1. 获取完整用户信息（删除前备份）
    const user = await this.prisma.user.findUnique({
      where: { id },
      include: {
        userRoles: { include: { role: true } },
        userDepartments: { include: { department: true } },
      },
    });

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 2. 执行软删除
    const deletedUser = await this.prisma.user.update({
      where: { id },
      data: {
        deletedAt: new Date(),
        status: 'INACTIVE',
      },
    });

    // 3. 记录详细的删除审计
    await this.auditService.log({
      who: 'system',
      what: `删除用户: ${user.username}`,
      
      module: 'User',
      action: AuditAction.DELETE,
      entityType: 'User',
      entityId: id,
      
      // 保存完整的用户信息（用于恢复）
      oldValue: {
        id: user.id,
        username: user.username,
        email: user.email,
        displayName: user.displayName,
        status: user.status,
        roles: user.userRoles.map(ur => ({
          id: ur.roleId,
          name: ur.role.name,
        })),
        departments: user.userDepartments.map(ud => ({
          id: ud.departmentId,
          name: ud.department.name,
        })),
        createdAt: user.createdAt,
      },
      newValue: {
        status: 'INACTIVE',
        deletedAt: deletedUser.deletedAt,
      },
      
      isSensitive: true,
    });

    return deletedUser;
  }

  private generateTempPassword(): string {
    // 生成 8 位临时密码
    return Math.random().toString(36).slice(-8);
  }
}
```

---

### 3. Module 配置（无需修改）

```typescript
// users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}
// ✅ 无需导入 AuditModule（它是 @Global() 的）
```

---

## 📊 审计日志示例

### 示例 1: 创建用户

**请求**:
```http
POST /api/v1/users
Content-Type: application/json

{
  "username": "john_doe",
  "email": "john@example.com",
  "displayName": "John Doe"
}
```

**自动生成的审计日志**:
```json
{
  "id": "550e8400-...",
  "who": "admin",
  "what": "POST /api/v1/users",
  "when": "2025-12-18T10:30:00Z",
  "where": "192.168.1.100",
  "how": "HTTP_API",
  "module": "Users",
  "action": "CREATE",
  "entityType": "users",
  "entityId": "新创建的用户ID",
  "status": "SUCCESS",
  "duration": 45,
  "isFinancial": false,
  "isSensitive": false,
  "riskLevel": "MEDIUM"
}
```

---

### 示例 2: 分配角色（详细变更）

**请求**:
```http
PATCH /api/v1/users/550e8400-.../roles
Content-Type: application/json

{
  "roleIds": ["role-id-1", "role-id-2"]
}
```

**手动记录的审计日志**:
```json
{
  "id": "660e8400-...",
  "who": "admin",
  "what": "为用户分配角色",
  "module": "User",
  "action": "ROLE_CHANGE",
  "entityType": "User",
  "entityId": "550e8400-...",
  "oldValue": {
    "roles": [
      { "id": "old-role-1", "name": "普通用户" }
    ]
  },
  "newValue": {
    "roles": [
      { "id": "role-id-1", "name": "管理员" },
      { "id": "role-id-2", "name": "审核员" }
    ]
  },
  "changes": {
    "added": ["管理员", "审核员"],
    "removed": ["普通用户"]
  },
  "isSensitive": true,
  "riskLevel": "HIGH"
}
```

---

### 示例 3: 删除用户（完整备份）

**请求**:
```http
DELETE /api/v1/users/550e8400-...
```

**手动记录的审计日志**:
```json
{
  "id": "770e8400-...",
  "who": "admin",
  "what": "删除用户: john_doe",
  "module": "User",
  "action": "DELETE",
  "entityType": "User",
  "entityId": "550e8400-...",
  "oldValue": {
    "id": "550e8400-...",
    "username": "john_doe",
    "email": "john@example.com",
    "displayName": "John Doe",
    "status": "ACTIVE",
    "roles": [
      { "id": "role-1", "name": "管理员" }
    ],
    "departments": [
      { "id": "dept-1", "name": "技术部" }
    ],
    "createdAt": "2025-01-01T00:00:00Z"
  },
  "newValue": {
    "status": "INACTIVE",
    "deletedAt": "2025-12-18T10:30:00Z"
  },
  "isSensitive": true,
  "riskLevel": "HIGH"
}
```

---

## 🎯 审计效果

### 前端查看

访问 `http://localhost:6010/audit/logs` 可以看到：

| 时间 | 操作人 | 操作 | 模块 | 状态 | 风险等级 |
|------|-------|------|------|------|---------|
| 10:30 | admin | 删除用户 | User | 成功 | 🔴 高 |
| 10:28 | admin | 分配角色 | User | 成功 | 🟠 高 |
| 10:25 | admin | 创建用户 | User | 成功 | 🟡 中 |

### 查询用户操作历史

```bash
GET /api/v1/audit/user/550e8400-...
```

响应：
```json
{
  "userId": "550e8400-...",
  "username": "john_doe",
  "operations": [
    {
      "action": "DELETE",
      "module": "User",
      "when": "2025-12-18T10:30:00Z",
      "status": "SUCCESS",
      "isSensitive": true
    },
    {
      "action": "ROLE_CHANGE",
      "module": "User",
      "when": "2025-12-18T10:28:00Z",
      "status": "SUCCESS",
      "isSensitive": true
    }
  ],
  "summary": {
    "total": 12,
    "sensitiveCount": 3,
    "failedCount": 0
  }
}
```

---

## ✅ 集成检查清单

完成以下步骤，确保审计功能正确集成：

### Controller 层
- [ ] 所有写操作（POST/PUT/PATCH/DELETE）添加 `@Auditable()`
- [ ] 敏感操作添加 `@Sensitive()`（密码、权限、删除等）
- [ ] 财务操作添加 `@Financial()`
- [ ] 导入装饰器：`import { Auditable, Sensitive, Financial } from '@/audit/decorators/auditable.decorator'`

### Service 层
- [ ] 注入 `AuditService`（如需手动审计）
- [ ] 关键业务逻辑记录详细变更（oldValue, newValue, changes）
- [ ] 错误情况也记录审计日志（status: FAILED）
- [ ] 敏感字段已脱敏

### 测试验证
- [ ] 执行操作后，在审计日志页面能看到记录
- [ ] 审计日志包含完整的 5W1H 信息
- [ ] 敏感操作被正确标记
- [ ] 详细变更被正确记录

---

## 🔧 故障排查

### 问题 1: 审计日志没有生成

**检查项**:
1. Controller 方法是否添加了 `@Auditable()`
2. 用户是否已登录（req.user 是否存在）
3. 检查后端控制台是否有错误日志

**解决方案**:
```typescript
// ✅ 确保添加装饰器
@Post()
@Auditable()  // 👈 不要忘记
async create(@Body() dto: CreateUserDto) {
  return this.service.create(dto);
}
```

---

### 问题 2: 手动审计报错 "AuditService is not defined"

**原因**: 没有注入 `AuditService`

**解决方案**:
```typescript
@Injectable()
export class YourService {
  constructor(
    private readonly auditService: AuditService,  // 👈 添加注入
  ) {}
}
```

---

### 问题 3: 审计日志缺少 userId

**原因**: 用户未登录或 JWT Guard 未启用

**解决方案**:
```typescript
@Controller('users')
@UseGuards(JwtAuthGuard)  // 👈 确保添加认证守卫
export class UsersController {
  // ...
}
```

---

## 📚 相关文档

- [集成指南](./INTEGRATION_GUIDE.md) - 完整的集成指南
- [API 文档](./API.md) - 审计 API 详细说明
- [架构设计](./ARCHITECTURE.md) - 系统架构

---

**最后更新**: 2025-12-18  
**维护者**: FFOA 开发团队
