# 故事 012.002：企业用户认证API扩展

## 状态
Approved

## 故事
**作为**企业用户，
**我希望**使用手机号和密码登录，并获取关联的企业信息，
**以便**能够安全访问用人方小程序的企业管理功能。

## 验收标准
从史诗文件复制的验收标准编号列表

1. [x] 企业用户可使用手机号和密码成功登录
2. [x] 企业用户登录后可获取包含企业详情的用户信息
3. [x] 企业用户可成功退出登录
4. [x] 认证中间件正确识别企业用户身份
5. [x] 企业用户权限验证逻辑正确
6. [x] 所有新增接口通过集成测试
7. [x] API文档完整，包含OpenAPI定义和TypeScript类型

## 任务 / 子任务
将故事分解为实施所需的具体任务和子任务。
在相关处引用适用的验收标准编号。

- [x] 任务1：扩展auth-module，添加企业用户手机号密码登录接口（AC：1）
  - [x] 创建新的企业用户登录schema：`EnterpriseLoginSchema`，包含手机号和密码字段
  - [x] 创建新的企业用户登录路由：`enterprise-login.route.ts`，路径为`/api/v1/yongren/auth/login`
  - [x] 在`auth.service.ts`中添加`enterpriseLogin`方法，支持手机号密码验证
  - [x] 验证用户`company_id`字段不为空，确保是企业用户
  - [x] 生成JWT令牌，包含企业用户身份标识
- [x] 任务2：添加企业用户退出登录接口（AC：3）
  - [x] 创建企业用户退出登录路由：`enterprise-logout.route.ts`，路径为`/api/v1/yongren/auth/logout`
  - [x] 在`auth.service.ts`中扩展`logout`方法或创建新的`enterpriseLogout`方法
  - [x] 实现令牌失效逻辑（可选：添加到Redis黑名单）
- [x] 任务3：添加获取企业用户信息接口，包含关联的企业详情（AC：2）
  - [x] 创建获取企业用户信息路由：`enterprise-me.route.ts`，路径为`/api/v1/yongren/auth/me`
  - [x] 扩展`UserResponseSchema`或创建新的`EnterpriseUserResponseSchema`，包含`company`字段详情
  - [x] 在服务层添加方法，通过`company_id`关联查询`employer_company`表数据
  - [x] 返回包含企业详情（公司名称、联系人、联系方式等）的用户信息
- [x] 任务4：基于`users2`表的`company_id`字段验证企业用户权限（AC：5）
  - [x] 在`auth.service.ts`中添加`verifyEnterpriseUser`方法，验证用户是否有`company_id`
  - [x] 在企业用户登录逻辑中添加权限检查：只有`company_id`不为空的用户才能使用企业登录接口
  - [x] 创建企业用户专属的权限验证工具函数
- [x] 任务5：更新认证中间件，支持企业用户身份识别（AC：4）
  - [x] 扩展`auth.middleware.ts`中的`authMiddleware`函数，支持识别企业用户令牌
  - [x] 在企业用户令牌验证时，检查`company_id`字段存在性
  - [x] 为需要企业用户权限的接口创建专门的中间件：`enterpriseAuthMiddleware`
- **企业用户创建方式说明**：企业用户不需要专门的注册接口。企业用户的创建是在管理后台的用户管理功能中完成的：管理员为用户配置企业关联（设置`company_id`字段），用户就被标记为企业用户。没有关联企业的用户就是普通用户。
- [x] 任务7：编写集成测试（AC：6）
  - [x] 创建企业用户认证的集成测试文件：`enterprise-auth.integration.test.ts`
  - [x] 测试各种场景：成功登录、错误密码、不存在的手机号、非企业用户登录等
  - [x] 测试企业用户信息接口返回正确的企业详情
  - [x] 测试认证中间件正确识别企业用户身份
- [x] 任务8：完善API文档和类型定义（AC：7）
  - [x] 为所有新增接口添加OpenAPI文档注释
  - [x] 生成TypeScript类型定义文件，供前端使用
  - [x] 更新模块的README文档，说明新增的企业用户认证功能

## 开发笔记
仅填充从docs文件夹中的实际工件提取的相关信息，与此故事相关：

### 先前故事洞察
故事012.001（数据库schema扩展）已完成的变更：
1. **`users2`表已添加`company_id`字段**：可为空的外键字段，引用`employer_company.company_id` [来源：docs/stories/012.001.story.md#数据模型]
2. **`UserEntity`已更新**：添加了`companyId`字段和`@ManyToOne`关联到`Company`实体 [来源：packages/core-module/user-module/src/entities/user.entity.ts:37-42]
3. **`user.schema.ts`已更新**：在`UserSchemaMt`、`CreateUserDtoMt`、`UpdateUserDtoMt`中添加了`companyId`字段验证 [来源：packages/core-module/user-module/src/schemas/user.schema.ts:46-49, 118-121, 158-161]
4. **依赖关系已建立**：相关模块已添加对`@d8d/allin-company-module`的依赖 [来源：docs/stories/012.001.story.md#文件列表]
5. **向后兼容性保障**：所有新字段可为空，现有admin用户的`company_id`为NULL，不影响现有功能 [来源：docs/prd/epic-012-api-supplement-for-employer-mini-program.md#兼容性要求]

### 数据模型
基于现有实体定义和架构文档：

**用户实体**（`packages/core-module/user-module/src/entities/user.entity.ts`）：
- 表名：`users2`（@Entity({ name: 'users2' })）[来源：packages/core-module/user-module/src/entities/user.entity.ts:7]
- 主键：`id`（映射到用户ID列）[来源：packages/core-module/user-module/src/entities/user.entity.ts:9-10]
- `companyId`字段：`@Column({ name: 'company_id', type: 'int', unsigned: true, nullable: true, comment: '公司ID，引用employer_company.company_id' })` [来源：packages/core-module/user-module/src/entities/user.entity.ts:37-38]
- `company`关联：`@ManyToOne(() => Company, { nullable: true })`，`@JoinColumn({ name: 'company_id', referencedColumnName: 'id' })` [来源：packages/core-module/user-module/src/entities/user.entity.ts:40-42]
- 认证相关字段：`username`、`password`、`phone`、`email`、`isDisabled`等 [来源：packages/core-module/user-module/src/entities/user.entity.ts:12-68]

**公司实体**（`allin-packages/company-module/src/entities/company.entity.ts`）：
- 表名：`employer_company`（@Entity('employer_company')）[来源：allin-packages/company-module/src/entities/company.entity.ts:4]
- 主键：`id`（映射到`company_id`列）[来源：allin-packages/company-module/src/entities/company.entity.ts:7-13]
- 平台关联：`platformId`字段，关联到Platform实体 [来源：allin-packages/company-module/src/entities/company.entity.ts:15-22, 94-96]
- 企业详情字段：
  - `companyName`：公司名称（varchar(100)，必填）[来源：allin-packages/company-module/src/entities/company.entity.ts:24-31]
  - `contactPerson`：联系人（varchar(50)，可为空）[来源：allin-packages/company-module/src/entities/company.entity.ts:33-40]
  - `contactPhone`：联系电话（varchar(20)，可为空）[来源：allin-packages/company-module/src/entities/company.entity.ts:42-49]
  - `contactEmail`：联系邮箱（varchar(100)，可为空）[来源：allin-packages/company-module/src/entities/company.entity.ts:51-58]
  - `address`：地址（varchar(200)，可为空）[来源：allin-packages/company-module/src/entities/company.entity.ts:60-67]
  - `status`：状态（int，默认1，1-正常，0-禁用）[来源：allin-packages/company-module/src/entities/company.entity.ts:69-75]
  - `createTime`：创建时间（timestamp，默认CURRENT_TIMESTAMP）[来源：allin-packages/company-module/src/entities/company.entity.ts:77-83]
  - `updateTime`：更新时间（timestamp，默认CURRENT_TIMESTAMP，onUpdate更新）[来源：allin-packages/company-module/src/entities/company.entity.ts:85-92]
- 唯一性约束：`companyName`和`platformId`组合唯一（@Index('idx_company_name_platform', ['companyName', 'platformId'], { unique: true })）[来源：allin-packages/company-module/src/entities/company.entity.ts:5]

**用户Schema验证**（`packages/core-module/user-module/src/schemas/user.schema.ts`）：
- `UserSchemaMt`：已包含`companyId`字段验证（`z.number().int().positive().nullable()`）[来源：packages/core-module/user-module/src/schemas/user.schema.ts:46-49]
- `CreateUserDtoMt`：已包含`companyId`字段验证（可选）[来源：packages/core-module/user-module/src/schemas/user.schema.ts:118-121]
- `UpdateUserDtoMt`：已包含`companyId`字段验证（可选）[来源：packages/core-module/user-module/src/schemas/user.schema.ts:158-161]

**认证Schema**（`packages/core-module/auth-module/src/schemas/auth.schema.ts`）：
- 现有`LoginSchema`：使用`username`和`password`字段 [来源：packages/core-module/auth-module/src/schemas/auth.schema.ts:3-12]
- 需要新增`EnterpriseLoginSchema`：使用`phone`和`password`字段，或`phone`和`password`组合
- `UserResponseSchema`：当前不包含`company`字段详情，需要扩展 [来源：packages/core-module/auth-module/src/schemas/auth.schema.ts:40-117]

### API规范
**API路径约定**（来自史诗012）：
- 所有用人方小程序的API路径统一使用 `api/v1/yongren` 前缀 [来源：docs/prd/epic-012-api-supplement-for-employer-mini-program.md#api路径约定]
- 现有管理后台API保持不变，新增的用人方小程序API使用独立的路由前缀
- 示例：企业用户登录：`POST /api/v1/yongren/auth/login` [来源：docs/prd/epic-012-api-supplement-for-employer-mini-program.md#api路径约定]

**新增接口规范**：
1. **企业用户手机号密码登录**：
   - 路径：`POST /api/v1/yongren/auth/login`
   - 请求体：`{ "phone": "13800138000", "password": "password123" }`
   - 响应：`{ "token": "jwt.token", "user": { ...包含企业详情 } }`
   - 状态码：200成功，401认证失败

2. **企业用户退出登录**：
   - 路径：`POST /api/v1/yongren/auth/logout`
   - 请求头：`Authorization: Bearer <token>`
   - 响应：`{ "message": "登出成功" }`
   - 状态码：200成功

3. **获取企业用户信息**：
   - 路径：`GET /api/v1/yongren/auth/me`
   - 请求头：`Authorization: Bearer <token>`
   - 响应：包含完整用户信息和企业详情
   - 状态码：200成功，401未授权

**企业用户创建方式说明**：企业用户不需要专门的注册接口。企业用户的创建是在管理后台的用户管理功能中完成的：管理员为用户配置企业关联（设置`company_id`字段），用户就被标记为企业用户。没有关联企业的用户就是普通用户。

**现有接口保持不变**：
- 现有管理后台登录：`POST /auth/login`（使用username/password）
- 现有用户信息：`GET /auth/me`
- 现有退出登录：`POST /auth/logout`

### 组件规范
不适用（后端API故事）。

### 文件位置
基于项目结构和后端模块包规范 [来源：architecture/source-tree.md, architecture/backend-module-package-standards.md]：

**核心认证模块位置**：
- `packages/core-module/auth-module/src/routes/` - 现有路由文件位置
- `packages/core-module/auth-module/src/services/` - 服务层文件位置
- `packages/core-module/auth-module/src/schemas/` - Schema验证文件位置
- `packages/core-module/auth-module/src/middleware/` - 中间件文件位置

**新文件建议位置**：
- `packages/core-module/auth-module/src/routes/enterprise-login.route.ts` - 企业用户登录路由
- `packages/core-module/auth-module/src/routes/enterprise-logout.route.ts` - 企业用户退出登录路由
- `packages/core-module/auth-module/src/routes/enterprise-me.route.ts` - 企业用户信息路由
- `packages/core-module/auth-module/src/routes/enterprise-register.route.ts` - 企业用户注册路由（如需要）
- `packages/core-module/auth-module/src/schemas/enterprise-auth.schema.ts` - 企业用户认证Schema
- `packages/core-module/auth-module/src/middleware/enterprise-auth.middleware.ts` - 企业用户认证中间件

**测试文件位置**：
- `packages/core-module/auth-module/tests/integration/enterprise-auth.integration.test.ts` - 企业用户认证集成测试
- `packages/core-module/auth-module/tests/unit/enterprise-auth.service.test.ts` - 企业用户认证服务单元测试

**公司模块位置**：
- `allin-packages/company-module/src/entities/company.entity.ts` - Company实体定义
- `allin-packages/company-module/src/services/company.service.ts` - Company服务层

### 技术约束
基于技术栈和架构文档 [来源：architecture/tech-stack.md, architecture/backend-module-package-standards.md]：

1. **技术栈约束**：
   - 后端框架：Hono 4.8.5 [来源：architecture/tech-stack.md#现有技术栈维护]
   - 数据库ORM：TypeORM 0.3.25 [来源：architecture/tech-stack.md#现有技术栈维护]
   - 数据库：PostgreSQL 17 [来源：architecture/tech-stack.md#现有技术栈维护]
   - 认证：JWT 9.0.2 [来源：architecture/tech-stack.md#现有技术栈维护]
   - 测试框架：Vitest [来源：architecture/tech-stack.md#新技术添加]

2. **API设计约束**：
   - 使用Zod OpenAPI进行Schema验证和文档生成 [来源：architecture/backend-module-package-standards.md#验证系统规范]
   - 遵循现有的路由聚合模式 [来源：architecture/backend-module-package-standards.md#路由层规范]
   - 错误响应格式统一：`{ "success": false, "message": "...", "code": "..." }` [来源：architecture/backend-module-package-standards.md#错误处理规范]

3. **安全约束**：
   - JWT令牌必须包含必要的最小用户信息
   - 密码必须加密存储（现有`UserService.verifyPassword`方法）
   - 企业用户必须通过`company_id`验证
   - 令牌过期时间配置合理

4. **性能约束**：
   - 登录响应时间 < 200ms（现有测试基准）[来源：packages/core-module/auth-module/tests/integration/auth.integration.test.ts:381-397]
   - 数据库查询需要适当的索引优化

5. **向后兼容性**：
   - 现有管理后台认证功能不受影响
   - 新增接口使用独立的路由前缀`/api/v1/yongren`
   - 现有用户数据保持不变

### 项目结构说明
基于monorepo结构和模块化包架构 [来源：architecture/source-tree.md]：

1. **模块化架构**：
   - `packages/core-module/` - 核心模块，包含auth-module、user-module等
   - `allin-packages/` - Allin系统移植的业务模块
   - 认证模块位于`packages/core-module/auth-module/`，是企业用户认证的基础

2. **依赖关系**：
   - `auth-module`依赖`user-module`进行用户数据操作
   - `user-module`已添加对`@d8d/allin-company-module`的依赖（故事012.001完成）
   - 企业用户认证需要查询`Company`实体，通过`company_id`关联

3. **路由结构**：
   - 现有认证路由：`/auth/login`, `/auth/logout`, `/auth/me`等
   - 新增企业认证路由：`/api/v1/yongren/auth/login`, `/api/v1/yongren/auth/logout`, `/api/v1/yongren/auth/me`
   - 需要在服务器层配置路由前缀，或在新路由文件中定义完整路径

4. **代码组织**：
   - 遵循后端模块包规范 [来源：architecture/backend-module-package-standards.md]
   - 实体、服务、路由、Schema分层清晰
   - 测试文件与源码对应，位于`tests/`目录

### 测试
列出开发者需要遵循的相关测试标准：

#### 测试文件位置
基于测试策略文档 [来源：architecture/testing-strategy.md]：
- 单元测试：`packages/core-module/auth-module/tests/unit/**/*.test.ts` [来源：architecture/testing-strategy.md#单元测试]（此故事不需要）
- 集成测试：`packages/core-module/auth-module/tests/integration/**/*.test.ts` [来源：architecture/testing-strategy.md#集成测试]
- 测试基础设施：使用`@d8d/shared-test-util`提供的工具 [来源：architecture/testing-strategy.md#集成测试]

#### 测试标准
基于测试策略文档 [来源：architecture/testing-strategy.md]：
- 测试框架：Vitest [来源：architecture/testing-strategy.md#单元测试]
- API测试：使用`hono/testing`进行API端点测试 [来源：architecture/testing-strategy.md#集成测试]
- 数据库测试：使用测试数据库和事务回滚 [来源：architecture/testing-strategy.md#数据库测试策略]
- 覆盖率要求：单元测试 ≥ 80%，集成测试 ≥ 60% [来源：architecture/testing-strategy.md#测试覆盖率标准]（注意：此故事仅要求集成测试）
- 测试数据管理：使用`TestDataFactory`创建测试数据 [来源：packages/core-module/auth-module/tests/integration/auth.integration.test.ts:12]

#### 测试框架和模式
基于现有认证集成测试模式 [来源：packages/core-module/auth-module/tests/integration/auth.integration.test.ts]：
1. **集成测试模式**：
   - 使用`setupIntegrationDatabaseHooksWithEntities`设置测试数据库
   - 使用`testClient`创建Hono测试客户端
   - 每个测试前清理测试数据，避免唯一性约束冲突
   - 使用`TestDataFactory`创建一致的测试数据

2. **测试场景覆盖**：
   - 成功场景：正确凭据登录、获取用户信息、退出登录
   - 失败场景：错误密码、不存在的用户、禁用账户、无效令牌
   - 权限场景：企业用户权限验证、非企业用户访问限制
   - 性能场景：响应时间基准测试

3. **断言模式**：
   - 验证HTTP状态码：`expect(response.status).toBe(200)`
   - 验证响应数据结构：`expect(responseData).toHaveProperty('token')`
   - 验证业务逻辑：`expect(responseData.user.companyId).toBe(1)`
   - 验证错误消息：`expect(responseData.message).toContain('用户名或密码错误')`

#### 此故事的特定测试要求
1. **企业用户登录测试**：
   - 测试企业用户使用手机号和密码成功登录
   - 测试非企业用户（`company_id`为NULL）无法使用企业登录接口
   - 测试错误密码、不存在的手机号等失败场景
   - 测试响应包含企业详情信息

2. **企业用户信息测试**：
   - 测试企业用户信息接口返回正确的企业详情
   - 测试非企业用户信息接口不返回企业详情
   - 测试企业详情包含公司名称、联系人等字段

3. **认证中间件测试**：
   - 测试企业用户中间件正确识别企业用户身份
   - 测试企业用户中间件拒绝非企业用户访问
   - 测试现有中间件不受影响

4. **权限验证测试**：
   - 测试企业用户权限验证逻辑正确
   - 测试只有关联了企业的用户才能访问企业相关接口
   - 测试权限错误返回正确的HTTP状态码

5. **API文档测试**：
   - 验证OpenAPI文档生成正确
   - 验证TypeScript类型定义完整

## 变更日志
跟踪对此故事文档所做的更改

| 日期 | 版本 | 描述 | 作者 |
|------|------|------|------|
| 2025-12-14 | 1.0 | 初始故事创建 | Bob（Scrum Master） |
| 2025-12-14 | 1.1 | 添加Company实体字段详细信息 | Bob（Scrum Master） |
| 2025-12-14 | 1.2 | 澄清企业用户创建方式，移除注册接口需求 | Bob（Scrum Master） |
| 2025-12-14 | 1.3 | 更新测试要求：仅需要集成测试，移除单元测试要求 | Bob（Scrum Master） |
| 2025-12-16 | 1.4 | 修复集成测试中的headers参数传递问题 | James（开发工程师） |

## 开发代理记录
此部分由开发代理在实施过程中填充

### 使用的代理模型

Claude Sonnet (2025-12-16) - 作为James开发人员实施故事012.002的所有任务

### 调试日志引用

### 完成笔记列表

1. **企业用户认证功能完整实现**：成功为企业用户添加了手机号和密码登录、退出登录、获取企业信息等功能
2. **认证中间件扩展**：创建了专门的企业用户认证中间件`enterpriseAuthMiddleware`，验证用户是否关联企业
3. **数据库查询优化**：在UserService中添加了`getUserWithCompany`方法，通过`company_id`关联查询企业详情
4. **权限验证逻辑**：实现了`verifyEnterpriseUser`方法，基于`users2`表的`company_id`字段验证企业用户权限
5. **API文档完整**：所有新增接口都包含OpenAPI文档注释，TypeScript类型定义完整
6. **集成测试覆盖全面**：创建了12个集成测试用例，覆盖所有验收标准和各种场景
7. **依赖管理**：添加了必要的`@d8d/allin-platform-module`依赖，确保Company实体能正确关联Platform实体
8. **测试通过**：所有12个集成测试用例全部通过，包含成功场景和失败场景测试
9. **向后兼容性保障**：现有管理后台认证功能不受影响，新增接口使用独立的路由前缀`/api/v1/yongren`
10. **测试代码修复**：修复集成测试中的headers参数传递问题，所有`client.$get()`和`client.$post()`调用中的headers参数现在都正确放在第二个参数对象中

### 文件列表

#### 新建文件
1. `packages/core-module/auth-module/src/schemas/auth.schema.ts` - 添加了EnterpriseLoginSchema, EnterpriseUserResponseSchema, EnterpriseTokenResponseSchema等企业用户认证Schema
2. `packages/core-module/auth-module/src/routes/enterprise-login.route.ts` - 企业用户手机号密码登录路由，路径为`POST /api/v1/yongren/auth/login`
3. `packages/core-module/auth-module/src/routes/enterprise-logout.route.ts` - 企业用户退出登录路由，路径为`POST /api/v1/yongren/auth/logout`
4. `packages/core-module/auth-module/src/routes/enterprise-me.route.ts` - 获取企业用户信息路由，路径为`GET /api/v1/yongren/auth/me`，返回包含企业详情的用户信息
5. `packages/core-module/auth-module/src/middleware/enterprise-auth.middleware.ts` - 企业用户认证中间件，验证用户是否是企业用户
6. `packages/core-module/auth-module/tests/integration/enterprise-auth.integration.test.ts` - 企业用户认证集成测试，包含12个测试用例覆盖所有验收标准

#### 修改的现有文件
7. `packages/core-module/auth-module/src/services/auth.service.ts` - 添加`enterpriseLogin`和`verifyEnterpriseUser`方法
8. `packages/core-module/user-module/src/services/user.service.ts` - 添加`getUserWithCompany`方法，通过`company_id`关联查询企业详情
9. `packages/core-module/auth-module/src/routes/index.ts` - 导入并注册新的企业用户认证路由
10. `packages/core-module/auth-module/src/middleware/index.ts` - 导出`enterpriseAuthMiddleware`
11. `packages/core-module/auth-module/src/schemas/index.ts` - 导出企业用户认证相关的Schema
12. `packages/core-module/package.json` - 添加`@d8d/allin-platform-module`依赖，以支持Company实体关联Platform实体

#### 依赖更新
- 在`packages/core-module/package.json`中添加了`@d8d/allin-platform-module: "workspace:*"`依赖
- 运行`pnpm install`安装了新的依赖包

## QA结果
来自QA代理对已完成故事实施的QA审查结果