# req.user 真实 shape：currentOrganizationId（不是 organizationId / orgId）

**日期**: 2026-05-15
**模块**: 任何使用 `@CurrentUser()` 或 `req.user.xxx` 的 controller
**关键词**: jwt, organizationId, x-organization-id, multi-tenant

## 现象

实现新模块 controller 时凭直觉写：

```ts
const organizationId = req.user?.organizationId ?? req.user?.orgId;
```

跑 L1 集成测试时 `organizationId` 始终 `undefined`，导致 Prisma create 抛 `Argument
'user' is missing` 或 `Field 'organizationId' undefined` 这类困惑错误。

## 真相

JWT 验证后 `req.user` 的真实 shape（见 `src/modules/organization/auth/strategies/jwt.strategy.ts` 调 `sliceAuthPayload`）：

```ts
{
  userId, username, email, defaultRegion,
  currentOrganizationId,    // ← 来自请求 header X-Organization-Id
  roles, permissions, organizationPermissions, organizationRoles,
  regionPermissions, regionRoles,
  dataScopes, jti
}
```

**没有** `organizationId`、`orgId`、`tenantId` 这些命名。

## 几个关键点

1. `currentOrganizationId` 只在请求带 `X-Organization-Id` header 时才有值
   - 前端 `OrganizationContext` 切组织时由 `api-client.ts` 自动注入
   - 集成测试**必须**显式发该 header：
     ```ts
     .set('Authorization', `Bearer ${token}`)
     .set('X-Organization-Id', org.id)
     ```
   - CLI / 外部 agent 集成时也要带

2. User 模型本身**没有** `organizationId` 标量字段
   - 多租户归属通过 `UserRole.organizationId` + 当前会话 `X-Organization-Id` header 联合决定
   - 创建 user 时不要传 `organizationId`（factory 已经知道；但新写 helper 时易踩）

3. 标准字段铁律（CLAUDE.md）说"所有业务表必含 organizationId"，但 **User 不算业务表**，是 IAM 主体表，不适用此铁律

## 检查清单

写新 controller 时：
- [ ] `req.user?.currentOrganizationId`（不是 `organizationId`）
- [ ] 集成测试发 `X-Organization-Id` header
- [ ] 直接建 user 的 factory / seed 不传 `organizationId` 给 user 表
- [ ] 业务表 `organizationId` 写入用 `req.user.currentOrganizationId`，可能为 `undefined` → DataScopeService 会用 region 兜底（v2.1）或拒绝（v2.2+）

## 参考实现

`backend/src/modules/ai-usage/controllers/me.controller.ts` —
所有依赖 organizationId 的处都用：

```ts
const organizationId = req.user?.currentOrganizationId;
```
