Approved
作为 人才用户, 我想要 能够使用手机号和密码登录人才小程序, 以便 当我不记得身份证号或残疾证号时,仍然可以方便地登录系统。
[ ] 任务1:扩展人才用户登录服务,支持手机号查找 (AC: 1, 2)
UserService.getTalentUserByIdentifier方法,支持手机号查找[ ] 任务2:更新登录Schema和API文档 (AC: 5)
TalentLoginSchema的identifier字段描述[ ] 任务3:优化错误提示信息 (AC: 4)
[ ] 任务4:编写和更新测试 (AC: 6)
当前登录流程:
// 位置: packages/core-module/auth-module/src/services/auth.service.ts:151
async talentLogin(identifier: string, password: string) {
const user = await this.userService.getTalentUserByIdentifier(identifier);
// ... 验证密码并生成token
}
当前查找逻辑 (UserService.getTalentUserByIdentifier):
// 位置: packages/core-module/user-module/src/services/user.service.ts:259
async getTalentUserByIdentifier(identifier: string) {
// 1. 先通过身份证号/残疾证号查找disabled_person表
const disabledPerson = await this.getDisabledPersonByIdentifier(identifier);
// 2. 再通过person_id查找users2表
return await this.repository.findOne({
where: { personId: disabledPerson.id, userType: UserType.TALENT }
});
}
数据库字段:
users2.phone: 手机号字段(可为空) [Source: user.entity.ts:18]users2.user_type: 用户类型枚举 [Source: user.entity.ts:34]users2.person_id: 残疾人ID外键 [Source: user.entity.ts:54]方案1: 扩展getTalentUserByIdentifier方法(推荐)
修改UserService.getTalentUserByIdentifier方法,添加手机号查找逻辑:
async getTalentUserByIdentifier(identifier: string): Promise<UserEntity | null> {
try {
// 1. 先尝试通过手机号直接查找users2表
const userByPhone = await this.repository.findOne({
where: {
phone: identifier,
userType: UserType.TALENT
},
relations: ['person', 'roles', 'avatarFile']
});
if (userByPhone) {
return userByPhone;
}
// 2. 如果手机号查找失败,继续原有的身份证号/残疾证号查找逻辑
const disabledPerson = await this.getDisabledPersonByIdentifier(identifier);
if (!disabledPerson) {
return null;
}
return await this.repository.findOne({
where: {
personId: disabledPerson.id,
userType: UserType.TALENT
},
relations: ['person', 'roles', 'avatarFile']
});
} catch (error) {
console.error('Error getting talent user by identifier:', error);
throw new Error(`Failed to get talent user: ${error instanceof Error ? error.message : String(error)}`);
}
}
优势:
方案2: 创建独立的手机号登录方法(不推荐)
创建单独的talentLoginByPhone方法和路由。
劣势:
登录标识符优先级:
输入提示优化:
错误处理:
重要: 手机号登录依赖于users2.phone字段有值!
管理员创建人才用户时的注意事项:
users2.phone字段(手机号)disabled_person.phone复制到users2.phone数据迁移建议:
如果历史数据中users2.phone为空,需要批量补充:
-- 从disabled_person表同步手机号到users2表
UPDATE users2 u
SET phone = dp.phone
FROM disabled_person dp
WHERE u.person_id = dp.id
AND u.phone IS NULL
AND dp.phone IS NOT NULL;
手机号验证:
防暴力破解:
隐私保护:
单元测试场景 (UserService):
集成测试场景 (AuthService + 路由):
前端集成测试 (故事017.002后续):
Schema更新 (rencai-auth.schema.ts):
export const TalentLoginSchema = z.object({
identifier: z.string().min(1, '账号不能为空').openapi({
example: '13800138000', // 改为手机号示例
description: '手机号、身份证号或残疾证号' // 更新描述
}),
password: z.string().min(6, '密码至少6个字符').openapi({
example: 'password123',
description: '登录密码'
})
});
路由文档更新 (rencai/login.route.ts):
description: '人才用户登录接口,支持手机号、身份证号或残疾证号登录'
错误响应更新:
message: '账号或密码错误' // 统一错误消息
查询优化:
索引建议:
users2.phone字段应添加普通索引(如果还没有)users2.user_type已有索引 [Source: user.entity.ts:40](phone, user_type) 可进一步提升性能依赖故事:
后续故事影响:
风险1: 历史数据手机号为空
风险2: 手机号重复
风险3: 手机号格式不统一
风险4: 现有登录方式受影响
| Date | Version | Description | Author |
|---|---|---|---|
| 2025-12-26 | 1.0 | 创建故事文档 | James |
待实现时填写
待实现时填写
待实现时填写
待实现时填写