017.009.story.md 10 KB

故事017.009: 管理后台用户管理功能完善

元信息

  • 史诗: 017 - 人才小程序功能实现
  • 优先级: P0 - 阻塞性任务(人才登录必需)
  • 状态: Approved
  • 创建日期: 2025-12-26
  • 负责人: 开发团队

故事描述

作为 系统管理员, 我想要 在管理后台的用户管理页面中创建和编辑人才用户, 以便 残疾人能够配置为人才用户并使用身份证号/残疾证号登录人才小程序。

背景

现有系统状态:

  • 史诗015已完成人才用户认证API,支持通过personId关联残疾人记录
  • 数据库users2表已包含userType字段(支持admin/employer/talent)和personId字段
  • 后端Schema(CreateUserDto/UpdateUserDto)已支持userTypepersonId字段
  • 管理后台路由已配置/admin/users,使用@d8d/user-management-uiUserManagement组件
  • 残疾人选择器组件已存在:@d8d/allin-disability-person-management-ui/components/DisabledPersonSelector

问题分析:

  • 前端UserManagement组件的创建/编辑表单缺少userType下拉选择器
  • 前端UserManagement组件缺少personId残疾人选择器字段
  • 当选择人才用户类型时,无法关联残疾人记录
  • 结果: 残疾人无法登录人才小程序,因为缺少users2记录中的personId关联

人才登录流程(史诗015已实现):

身份证号/残疾证号 → disabled_person表 → person_id → users2表 (user_type='talent')

关键依赖:

  • packages/user-management-ui/src/components/UserManagement.tsx - 需要修改
  • @d8d/allin-disability-person-management-ui/components/DisabledPersonSelector - 已存在
  • packages/core-module/user-module/src/schemas/user.schema.ts - Schema已支持

技术实现方案

需要添加的字段:

  1. userType下拉选择器 - 选择用户类型(admin/employer/talent)
  2. personId残疾人选择器 - 当userType='talent'时显示

条件渲染逻辑:

  • 默认隐藏残疾人选择器
  • 当选择用户类型为"人才用户"时,显示残疾人选择器
  • 当选择用户类型为"企业用户"时,显示企业选择器(已存在)

复用现有模式:

  • 参考CompanySelectorWrapper组件模式创建DisabledPersonSelectorWrapper
  • 使用@d8d/allin-disability-person-management-ui/componentsDisabledPersonSelector

验收标准

功能完整性

  • 管理员可以在用户管理页面创建用户时选择用户类型(admin/employer/talent)
  • 当选择"人才用户"类型时,显示残疾人选择器
  • 残疾人选择器支持搜索、筛选、分页功能
  • 创建人才用户成功后,users2记录包含正确的userType='talent'personId
  • 编辑用户时可以修改用户类型和残疾人关联

用户体验

  • 用户类型切换流畅,选择器位置合理
  • 残疾人选择器对话框功能完整(搜索、筛选、分页)
  • 表单验证正确,必填字段提示清晰
  • 错误提示友好,操作反馈及时

集成验证

  • 创建人才用户后,残疾人可以使用身份证号/残疾证号登录人才小程序
  • 登录流程验证:身份证号/残疾证号 → 找到残疾人记录 → 通过personId找到用户 → 登录成功
  • 用户列表正确显示用户类型和关联的残疾人信息
  • 现有功能不受影响(admin用户和employer用户创建流程不变)

测试覆盖

  • 编写组件测试验证用户类型选择器功能
  • 编写集成测试验证残疾人选择器集成
  • 编写E2E测试验证完整的人才用户创建和登录流程
  • 类型检查通过(pnpm typecheck)

任务列表

任务1: 创建DisabledPersonSelectorWrapper组件 (AC: 功能完整性)

  • 1.1 在packages/user-management-ui/src/components/下创建DisabledPersonSelectorWrapper.tsx
  • 1.2 参考CompanySelectorWrapper.tsx的实现模式
  • 1.3 集成@d8d/allin-disability-person-management-ui/components/DisabledPersonSelector
  • 1.4 实现单选模式(mode='single')
  • 1.5 支持显示已选择的残疾人信息(姓名、残疾证号)
  • 1.6 支持清除选择功能
  • 1.7 添加data-testid测试选择器

组件接口设计:

interface DisabledPersonSelectorWrapperProps {
  value: number | null;  // personId
  onChange: (value: number | null) => void;
  placeholder?: string;
  disabled?: boolean;
}

任务2: 扩展UserManagement组件 - 添加userType字段 (AC: 功能完整性)

  • 2.1 在创建用户表单中添加userType字段(FormField)
  • 2.2 使用Select组件实现下拉选择器
  • 2.3 添加用户类型选项: admin(管理员), employer(企业用户), talent(人才用户)
  • 2.4 在编辑用户表单中添加userType字段
  • 2.5 更新表单默认值,userType默认为admin
  • 2.6 添加表单验证,userType为必填字段
  • 2.7 添加data-testid="user-type-select"

任务3: 实现条件渲染逻辑 (AC: 功能完整性, 用户体验)

  • 3.1 添加useWatchuseState监听userType字段变化
  • 3.2 当userType === 'talent'时,显示残疾人选择器
  • 3.3 当userType === 'employer'时,显示企业选择器(已存在)
  • 3.4 当userType === 'admin'时,隐藏企业选择器和残疾人选择器
  • 3.5 添加条件渲染的过渡动画(可选)
  • 3.6 确保切换用户类型时,清空不相关字段的值

任务4: 集成DisabledPersonSelectorWrapper到表单 (AC: 功能完整性)

  • 4.1 在创建用户表单中添加personId字段(FormField)
  • 4.2 条件渲染:仅当userType === 'talent'时显示
  • 4.3 使用DisabledPersonSelectorWrapper组件
  • 4.4 添加字段描述:"选择关联的残疾人记录"
  • 4.5 在编辑用户表单中添加personId字段
  • 4.6 编辑模式下显示当前关联的残疾人信息
  • 4.7 添加data-testid="disabled-person-selector"

任务5: 更新表单Schema和默认值 (AC: 功能完整性)

  • 5.1 确认CreateUserDto包含userTypepersonId字段
  • 5.2 确认UpdateUserDto包含userTypepersonId字段
  • [ ] 5.3 更新创建表单默认值:

    defaultValues: {
    ...existingFields,
    userType: UserType.ADMIN,  // 新增
    personId: null,            // 新增
    }
    
  • [ ] 5.4 更新编辑表单默认值逻辑,加载现有用户的userTypepersonId

  • [ ] 5.5 确保表单提交时包含userTypepersonId字段

任务6: 用户体验优化 (AC: 用户体验)

  • 6.1 用户类型选择器添加图标或颜色标识
  • 6.2 残疾人选择器打开时显示搜索提示
  • 6.3 选择残疾人后显示姓名和残疾证号(便于确认)
  • 6.4 添加表单验证提示:
    • 选择人才用户时,残疾人为必填
    • 选择企业用户时,企业为必填
  • 6.5 优化错误提示信息
  • 6.6 添加加载状态提示

任务7: 用户列表显示优化 (AC: 集成验证)

  • 7.1 在用户列表表格中添加"用户类型"列
  • 7.2 使用Badge组件区分用户类型(不同颜色)
  • 7.3 添加"关联残疾人"列,显示残疾人姓名
  • 7.4 人才用户行显示残疾人信息,其他用户显示"-"
  • 7.5 更新表格列宽和响应式布局

任务8: 测试编写 (AC: 测试覆盖)

  • 8.1 编写DisabledPersonSelectorWrapper组件测试
    • 测试渲染正确性
    • 测试选择功能
    • 测试清除功能
  • 8.2 编写UserManagement组件集成测试
    • 测试用户类型切换
    • 测试条件渲染逻辑
    • 测试表单提交
  • 8.3 编写E2E测试
    • 测试完整的人才用户创建流程
    • 测试人才用户登录小程序流程
  • 8.4 运行类型检查pnpm typecheck
  • 8.5 确保所有测试通过

任务9: 文档更新 (AC: 集成验证)

  • 9.1 更新史诗017的PRD文档,标记故事017.009为进行中
  • 9.2 在本故事的Dev Agent Record中记录实现细节
  • 9.3 添加使用说明:如何在管理后台创建人才用户
  • 9.4 更新史诗017的进度统计

技术细节

组件依赖关系

UserManagement
  ├── DisabledPersonSelectorWrapper (新建)
  │   └── DisabledPersonSelector (已存在,来自@d8d/allin-disability-person-management-ui)
  ├── CompanySelectorWrapper (已存在)
  └── Form, Select,等 (来自@d8d/shared-ui-components)

包依赖更新

需要在packages/user-management-ui/package.json中添加:

{
  "dependencies": {
    "@d8d/allin-disability-person-management-ui": "workspace:*"
  }
}

UserType枚举值

enum UserType {
  ADMIN = 'admin',      // 管理员
  EMPLOYER = 'employer', // 企业用户
  TALENT = 'talent'      // 人才用户
}

条件渲染逻辑示例

const userType = createForm.watch('userType');

{userType === UserType.TALENT && (
  <FormField
    control={createForm.control}
    name="personId"
    render={({ field }) => (
      <FormItem>
        <FormLabel>关联残疾人</FormLabel>
        <FormControl>
          <DisabledPersonSelectorWrapper
            value={field.value}
            onChange={field.onChange}
            placeholder="请选择残疾人"
          />
        </FormControl>
        <FormMessage />
      </FormItem>
    )}
  />
)}

风险与注意事项

风险1: 残疾人选择器API不兼容

  • 缓解措施: 确认DisabledPersonSelector组件的props接口,必要时创建适配器

风险2: 表单验证逻辑复杂

  • 缓解措施: 使用react-hook-form的自定义验证规则,清晰定义验证逻辑

风险3: 现有功能回归

  • 缓解措施: 充分测试admin和employer用户创建流程,确保不受影响

风险4: 数据一致性

  • 缓解措施: 确保后端API正确处理userTypepersonId字段的关联关系

兼容性要求

  • 现有admin用户创建流程不受影响
  • 现有employer用户创建流程不受影响
  • 用户列表表格兼容新增的用户类型列
  • 响应式布局在不同屏幕尺寸下正常显示
  • 现有用户编辑功能不受影响

完成定义

  • 所有任务完成,验收标准全部满足
  • 管理员可以通过管理后台创建人才用户
  • 创建的人才用户可以成功登录人才小程序
  • 所有测试通过(组件测试、集成测试、E2E测试)
  • 类型检查通过
  • 代码审查通过
  • 文档更新完成

Dev Agent Record

实现笔记

在此记录实现过程中的关键决策和技术细节

调试日志

在此记录遇到的问题和解决方案

完成总结

在此记录任务完成后的总结和改进建议