Ready for Review
作为 人才用户, 我希望 能够查看我的个人信息,包括基本信息、银行卡信息和证件照片, 以便 我能够了解和管理我的个人数据。
[ ] 任务1: 创建人才个人信息查询API (AC: 1, 4)
talent-personal-info.routes.ts/personal/info 接口 - 查询人才基本信息[ ] 任务2: 创建银行卡信息查询API (AC: 2, 4)
/personal/bank-cards 接口 - 查询银行卡列表[ ] 任务3: 创建证件照片查询API (AC: 3, 4)
/personal/photos 接口 - 查询证件照片列表[ ] 任务4: 扩展DisabledPersonService添加人才专用查询方法
getPersonalInfo(personId: number) 方法getBankCardsByPersonId(personId: number) 方法getPhotosByPersonId(personId: number, photoType?: string) 方法[ ] 任务5: 创建人才认证中间件 (如需要)
talentAuthMiddleware 验证人才用户身份[ ] 任务6: 在server包注册人才个人信息路由
talent-personal-info.routes.ts/api/v1/rencai 前缀[ ] 任务7: 编写单元测试和集成测试 (AC: 5)
[ ] 任务8: 更新模块导出和文档
/api/v1/rencai 前缀,模块内路由不包含前缀 [Source: docs/prd/epic-015-talent-mini-program-api-support.md#L53-L61]DisabledPerson实体关键字段 [Source: allin-packages/disability-module/src/entities/disabled-person.entity.ts#L8-L238]:
id: person_id (主键)name: 姓名gender: 性别 (男/女)idCard: 身份证号 (唯一索引)disabilityId: 残疾证号 (唯一索引)disabilityType: 残疾类型disabilityLevel: 残疾等级phone: 联系方式province: 省级city: 市级district: 区县级detailedAddress: 详细地址birthDate: 出生日期idAddress: 身份证地址idValidDate: 身份证有效期disabilityValidDate: 残疾证有效期canDirectContact: 是否可直接联系isMarried: 是否已婚nation: 民族isInBlackList: 是否在黑名单中jobStatus: 在职状态specificDisability: 具体残疾部位和情况bankCards (一对多), photos (一对多)DisabledBankCard实体关键字段 [Source: allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts]:
id: card_id (主键)personId: 残疾人ID (外键)subBankName: 发卡支行bankNameId: 银行名称ID (外键引用bank_name表)cardNumber: 卡号 (需要脱敏)cardholderName: 持卡人姓名cardType: 银行卡类型 (一类卡/二类卡)fileId: 文件ID (外键引用files表)isDefault: 是否默认person (多对一), file (多对一), bankName (多对一)DisabledPhoto实体关键字段 [Source: allin-packages/disability-module/src/entities/disabled-photo.entity.ts]:
id: photo_id (主键)personId: 残疾人ID (外键)photoType: 照片类型 (身份证、残疾证、体检报告、征信报告等)fileId: 文件ID (外键引用files表)uploadTime: 上传时间canDownload: 是否可下载person (多对一), file (多对一)UserEntity关键字段 (参考故事015.002):
id: 用户ID (主键)username: 用户名userType: 用户类型 (admin/employer/talent)personId: 残疾人ID (外键,可为空) [Source: docs/stories/015.001.story.md#L132-L133]/api/v1/rencai [Source: docs/prd/epic-015-talent-mini-program-api-support.md#L53-L61]具体接口路径:
GET /api/v1/rencai/personal/infoGET /api/v1/rencai/personal/bank-cardsGET /api/v1/rencai/personal/photos模块位置:
allin-packages/disability-module/ [Source: docs/architecture/source-tree.md]packages/core-module/auth-module/ (认证中间件) [Source: docs/architecture/source-tree.md#L109-L129]packages/server/ (路由注册) [Source: docs/architecture/source-tree.md#L56-L61]disability-module路由文件结构 (参考现有模式):
allin-packages/disability-module/src/routes/
├── talent-personal-info.routes.ts # 新增: 人才个人信息路由
├── person-extension.route.ts # 参考: 企业用户扩展路由模式
├── disabled-person.routes.ts # 主路由
└── index.ts # 路由导出
disability-module服务文件结构:
allin-packages/disability-module/src/services/
├── disabled-person.service.ts # 现有服务,需要扩展
└── index.ts # 服务导出
个人信息响应Schema (参考):
const PersonalInfoResponseSchema = z.object({
name: z.string(),
gender: z.string(),
idCard: z.string(),
disabilityId: z.string(),
disabilityType: z.string(),
disabilityLevel: z.string(),
phone: z.string(),
province: z.string(),
city: z.string(),
district: z.string().nullable(),
detailedAddress: z.string().nullable(),
birthDate: z.string().nullable(), // ISO日期字符串
idAddress: z.string(),
idValidDate: z.string().nullable(),
disabilityValidDate: z.string().nullable(),
canDirectContact: z.number(),
isMarried: z.number().nullable(),
nation: z.string().nullable(),
jobStatus: z.number(),
});
银行卡信息响应Schema (包含卡号脱敏):
const BankCardInfoSchema = z.object({
id: z.number(),
subBankName: z.string(),
bankName: z.string().nullable(), // 关联查询
cardNumber: z.string(), // 脱敏: 1234****5678
cardholderName: z.string(),
cardType: z.string().nullable(),
isDefault: z.number(),
fileUrl: z.string().nullable(), // 关联file表
});
const BankCardsResponseSchema = z.object({
data: z.array(BankCardInfoSchema),
total: z.number(),
});
证件照片响应Schema:
const PhotoInfoSchema = z.object({
id: z.number(),
photoType: z.string(),
fileUrl: z.string().nullable(), // 关联file表
fileName: z.string().nullable(),
uploadTime: z.string(), // ISO时间字符串
canDownload: z.number(),
});
const PhotosResponseSchema = z.object({
data: z.array(PhotoInfoSchema),
total: z.number(),
});
查询参数Schema (证件照片过滤):
const PhotosQuerySchema = z.object({
photoType: z.string().optional(), // 可选: 按类型过滤
skip: z.coerce.number().int().min(0).default(0),
take: z.coerce.number().int().min(1).max(100).default(10),
});
卡号脱敏规则: 保留前4位和后4位,中间用****代替
6222021234567890123 → 6222****0123使用talentAuthMiddleware:
talentAuthMiddleware [Source: docs/stories/015.002.story.md#L325]权限验证逻辑:
查询专用接口:
File模块集成 (参考现有模式):
packages/file-module/src/entities/file.entity.ts [Source: docs/architecture/source-tree.md#L130-L149]HTTP状态码 [Source: docs/architecture/backend-module-package-standards.md#L442-L449]:
200 OK: 查询成功400 Bad Request: 请求参数错误401 Unauthorized: 未授权或token无效403 Forbidden: 权限不足 (尝试访问他人数据)404 Not Found: 资源不存在 (用户不存在)错误响应格式 [Source: docs/architecture/backend-module-package-standards.md#L450-L458]:
{
success: false,
code: 403,
message: "权限不足,无法访问该数据",
timestamp: "2025-12-25T10:30:00Z"
}
TypeORM关系配置 (参考现有模式):
@OneToMany 关系: bankCards, photos [Source: allin-packages/disability-module/src/entities/disabled-person.entity.ts#L224-L228]@ManyToOne 关系: bankName, file [Source: allin-packages/disability-module/src/entities/disabled-bank-card.entity.ts#L84-L97]@ManyToOne 关系: file [Source: allin-packages/disability-module/src/entities/disabled-photo.entity.ts#L58-L65]relations 选项预加载关联数据person-extension.route.ts 的路由定义模式 [Source: allin-packages/disability-module/src/routes/person-extension.route.ts]talentAuthMiddleware [Source: docs/stories/015.002.story.md#L325]需要创建/修改的文件:
allin-packages/disability-module/src/routes/talent-personal-info.routes.ts - 新增: 人才个人信息路由allin-packages/disability-module/src/routes/index.ts - 修改: 导出新路由allin-packages/disability-module/src/schemas/talent-personal-info.schema.ts - 新增: 人才个人信息Schemaallin-packages/disability-module/src/schemas/index.ts - 修改: 导出新Schemaallin-packages/disability-module/src/services/disabled-person.service.ts - 修改: 添加人才专用查询方法packages/server/src/index.ts - 修改: 注册人才个人信息路由 (添加/api/v1/rencai前缀)allin-packages/disability-module/tests/integration/talent-personal-info.integration.test.ts - 新增: 集成测试@d8d/allin-disability-person-management 包 [Source: docs/architecture/backend-module-package-standards.md#L39-L42]allin-packages/disability-module/tests/integration/talent-personal-info.integration.test.ts [Source: docs/architecture/testing-strategy.md#L53]单元测试 (可选):
集成测试 (必须):
测试场景:
个人信息查询成功场景:
银行卡信息查询成功场景:
证件照片查询成功场景:
权限验证场景:
错误场景:
测试数据工厂 (参考现有模式):
| Date | Version | Description | Author |
|---|---|---|---|
| 2025-12-25 | 1.0 | 初始故事创建 | Scrum Master |
| 2025-12-25 | 1.1 | 修复日期字段处理和测试:使用 z.coerce.date().transform() 优雅处理日期格式转换 | Claude Code |
此部分由开发代理在实施过程中填写
claude-sonnet
birthDate.toISOString() 报错 toISOString is not a functionz.coerce.date().transform() 在 schema 层优雅处理注意事项:
日期字段处理修复 (2025-12-25):
birthDate.toISOString() 在测试中报错 toISOString is not a functiontalent-personal-info.schema.ts):
z.coerce.date<string>() 自动接受 Date 对象、字符串等多种输入格式.transform(val => val ? val.toISOString().split('T')[0] : null) 将 Date 转换为 YYYY-MM-DD 格式birthDate、idValidDate、disabilityValidDatedisabled-person.service.ts):
parseWithAwait() 和 schema 处理类型转换z.coerce.date() 可处理 Date 对象、字符串等各种输入.transform() 确保统一的 YYYY-MM-DD 格式新增文件:
packages/shared-types/src/index.ts - 添加TalentAuthContext和TalentUserBase类型allin-packages/disability-module/src/schemas/talent-personal-info.schema.ts - 人才个人信息Schemaallin-packages/disability-module/src/routes/talent-personal-info.routes.ts - 人才个人信息路由allin-packages/disability-module/tests/integration/talent-personal-info.integration.test.ts - 集成测试修改文件:
allin-packages/disability-module/src/schemas/index.ts - 添加talent-personal-info.schema导出allin-packages/disability-module/src/routes/index.ts - 添加talent-personal-info.routes导出allin-packages/disability-module/src/services/disabled-person.service.ts - 添加人才专用查询方法,优化日期字段处理(直接返回原始数据)allin-packages/disability-module/src/schemas/talent-personal-info.schema.ts - 使用 z.coerce.date().transform() 优雅处理日期转换(2025-12-25)packages/server/src/index.ts - 注册人才个人信息路由和认证路由此部分由QA代理在审查完成后填写