【招聘】招聘管理
招聘管理模块,由 yudao-module-hrm 后端模块的 recruit 包实现,前端实现在 @/views/hrm/recruit 目录。
招聘流程全部在管理端完成,围绕「招聘职位 → 候选人 → 面试 → Offer → 待入职员工 → 确认入职」展开,录用后再衔接员工档案。
- 招聘职位:维护用人部门、招聘要求、薪资范围、负责人、面试官和招聘状态。
- 候选人与面试:维护候选人资料,完成初选、面试、Offer、淘汰和转员工。
- 招聘设置:维护招聘渠道和淘汰原因,供职位、候选人和员工档案复用。
本文涉及表如下图所示:
# 1. 招聘职位
招聘职位,由 HrmRecruitPostController 提供接口(/hrm/recruit/post)。职位创建后默认处于「招聘中」,通过停止招聘和重新招聘管理生命周期,不提供删除操作。
# 1.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
CREATE TABLE `hrm_recruit_post` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '招聘职位编号',
`post_name` varchar(255) NOT NULL COMMENT '职位名称',
`post_type_id` bigint DEFAULT NULL COMMENT '职位类型编号',
`dept_id` bigint DEFAULT NULL COMMENT '用人部门编号',
`job_nature` tinyint DEFAULT NULL COMMENT '工作性质',
`area_id` int DEFAULT NULL COMMENT '工作城市地区编号',
`recruit_num` int DEFAULT NULL COMMENT '招聘人数',
`reason` varchar(255) DEFAULT NULL COMMENT '招聘原因',
`emergency_level` tinyint DEFAULT NULL COMMENT '紧急程度',
`latest_entry_time` datetime DEFAULT NULL COMMENT '最迟到岗时间',
`work_time` tinyint DEFAULT NULL COMMENT '工作经验要求',
`education_require` tinyint DEFAULT NULL COMMENT '学历要求',
`min_age` int DEFAULT NULL COMMENT '最小年龄',
`max_age` int DEFAULT NULL COMMENT '最大年龄',
`min_salary` decimal(10,2) DEFAULT NULL COMMENT '最低薪资',
`max_salary` decimal(10,2) DEFAULT NULL COMMENT '最高薪资',
`salary_unit` tinyint DEFAULT NULL COMMENT '薪资单位',
`owner_employee_id` bigint DEFAULT NULL COMMENT '招聘负责人员工编号',
`interview_employee_ids` varchar(255) DEFAULT NULL COMMENT '面试官员工编号数组',
`description` varchar(4000) DEFAULT NULL COMMENT '职位描述',
`status` tinyint DEFAULT NULL COMMENT '职位状态',
`stop_reason` varchar(255) DEFAULT NULL COMMENT '停止原因',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘职位';
① dept_id 关联 system_dept 表的 id 字段;area_id 使用平台地区编号,前端通过公共 AreaSelect 选择工作城市。
② owner_employee_id 和 interview_employee_ids 都关联 hrm_employee 表的 id 字段。负责人和面试官属于 HRM 员工关系,员工绑定后台账号后才能接收对应的站内通知。
③ job_nature 工作性质、work_time 工作经验要求、education_require 学历要求、salary_unit 薪资单位、emergency_level 紧急程度,分别对应字典 hrm_recruit_job_nature、hrm_recruit_work_time、hrm_recruit_post_education、hrm_recruit_salary_unit、hrm_recruit_emergency_level。
④ 薪资「面议」和年龄「不限」使用统一哨兵值 -1。前端展示业务文案,后端按同一常量校验薪资和年龄范围。
⑤ 枚举 status 职位状态(HrmRecruitPostStatusEnum),对应字典 hrm_recruit_post_status:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
| 1 | RECRUITING | 招聘中 | 编辑、停止招聘 |
| 0 | STOPPED | 停止招聘 | 编辑、重新招聘 |
停止时必须记录 stop_reason,重新招聘时清空停止原因。
该表包含一个关联表:
hrm_recruit_post_type(招聘职位类型):由 HrmRecruitPostTypeController 提供只读列表,作为职位表单的选择数据。
hrm_recruit_post_type 表结构
CREATE TABLE `hrm_recruit_post_type` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '职位类型编号',
`name` varchar(40) DEFAULT NULL COMMENT '类型名称',
`parent_id` bigint DEFAULT NULL COMMENT '父类型编号',
`sort` int DEFAULT NULL COMMENT '显示顺序',
`status` tinyint DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘职位类型';
parent_id 自关联本表 id,形成职位类型树。职位类型没有独立管理菜单,数据随 HRM 初始化脚本导入。
# 1.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 招聘职位] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/recruit/post 目录。
# 列表
默认展示招聘中的职位,可按职位名称、工作性质、工作城市、用人部门和招聘负责人筛选,顶部按「招聘中」「停止招聘」展示页签及数量。点击职位名称进入详情,行内可直接编辑或变更招聘状态。

# 新增
通过弹窗 RecruitPostForm.vue 完成,填写用人部门、工作城市、人数、招聘原因、经验与学历要求、薪资、年龄、最迟到岗时间、负责人、面试官和职位描述。职位状态由后端初始化为「招聘中」,不在创建表单中提供。

# 修改
弹窗结构与新增相同。修改沿用同一职位编号,因此历史候选人与职位的关联关系保持不变。
# 详情
点击职位名称进入 @/views/hrm/recruit/post/detail/index.vue,展示完整招聘要求与操作日志。详情页的【编辑】仍打开 RecruitPostForm.vue。

# 停止与重新招聘
招聘中的职位点击【停止招聘】后必须填写停止原因;停止后的职位可点击【重新招聘】恢复。招聘职位不提供删除操作,已有候选人、面试和操作记录始终保留。

# 2. 候选人与面试
候选人,由 HrmRecruitCandidateController 提供接口(/hrm/recruit/candidate);面试记录,由 HrmRecruitInterviewController 提供接口(/hrm/recruit/interview)。候选人的当前阶段保存在主表,完整面试过程保存在面试记录中。
# 2.1 主表表结构
CREATE TABLE `hrm_recruit_candidate` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '候选人编号',
`name` varchar(255) NOT NULL COMMENT '候选人姓名',
`mobile` varchar(255) NOT NULL COMMENT '手机号码',
`sex` tinyint NOT NULL COMMENT '性别',
`age` int DEFAULT NULL COMMENT '年龄',
`email` varchar(255) DEFAULT NULL COMMENT '邮箱',
`post_id` bigint NOT NULL COMMENT '应聘职位编号',
`channel_id` bigint DEFAULT NULL COMMENT '招聘渠道编号',
`work_time` int DEFAULT NULL COMMENT '工作年限(年)',
`education` tinyint NOT NULL COMMENT '学历',
`graduate_school` varchar(255) DEFAULT NULL COMMENT '毕业院校',
`latest_work_place` varchar(255) DEFAULT NULL COMMENT '最近工作单位',
`resume_urls` varchar(4000) DEFAULT NULL COMMENT '简历附件地址数组',
`status` tinyint DEFAULT NULL COMMENT '候选人状态',
`stage_number` int DEFAULT NULL COMMENT '当前面试轮次',
`eliminate` varchar(255) DEFAULT NULL COMMENT '淘汰原因',
`status_update_time` datetime DEFAULT NULL COMMENT '状态更新时间',
`entry_time` datetime DEFAULT NULL COMMENT '入职时间',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘候选人';
① post_id 关联 hrm_recruit_post 表的 id 字段,channel_id 关联 hrm_recruit_channel 表的 id 字段。
② stage_number 表示当前面试轮次,安排复试时递增;status_update_time 记录最近一次流程状态变化的时间,用于状态统计和「一键清理」判断跟进时长。
③ resume_urls 只保存平台文件地址,简历上传复用 Infra 文件能力。
④ 枚举 status 候选人状态(HrmRecruitCandidateStatusEnum),对应字典 hrm_recruit_candidate_status。详见 §2.2 状态流转。
# 2.2 状态流转
候选人生命周期由 HrmRecruitCandidateServiceImpl 控制。状态枚举 HrmRecruitCandidateStatusEnum:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
| 1 | NEW | 新候选人 | 初选通过、安排面试、淘汰、删除 |
| 2 | PRIMARY_PASS | 初选通过 | 安排面试、退回、淘汰、删除 |
| 3 | INTERVIEW | 安排面试 | 改期、登记结果、取消面试、淘汰、删除 |
| 4 | INTERVIEW_PASS | 面试通过 | 安排复试、发 Offer、转为员工、淘汰 |
| 5 | OFFER_SENT | 已发 Offer | 转为员工、淘汰 |
| 6 | PENDING_ENTRY | 待入职 | 确认入职、淘汰 |
| 7 | ELIMINATED | 已淘汰 | 恢复为新候选人、删除 |
| 8 | JOINED | 已入职 | — |
状态流转说明
新候选人(1) ──初选通过──→ 初选通过(2) ──安排面试──→ 安排面试(3) ──面试通过──→ 面试通过(4)
│
┌───────发 Offer───────────┘
↓
已发 Offer(5) ──转为员工──→ 待入职(6) ──确���入职──→ 已入职(8)
任一在途状态 ──淘汰──→ 已淘汰(7) ──恢复──→ 新候选人(1)
- 初选通过 / 发 Offer / 恢复(
updateRecruitCandidateStatus):均为纯状态推进,不产生额外业务数据。 - 安排面试(
createRecruitInterview):新建面试记录并把候选人置为「安排面试」;面试通过后再次安排时,stage_number递增为复试。 - 登记面试结果(
updateRecruitInterviewResult):通过 → 面试通过;未通过 → 已淘汰;取消 → 保留当前面试阶段,可重新安排同一轮。 - 淘汰(
eliminateRecruitCandidate):选择淘汰原因并填写备注。若候选人正在面试,未完成的面试会同步登记为未通过。 - 转为员工(
convertRecruitCandidateToEmployee):在同一事务中创建hrm_employee,并按员工入职状态把候选人更新为待入职或已入职。 - 确认入职(
confirmRecruitCandidateEntry):待入职 → 已入职,由员工侧的「确认入职」联动触发。
# 2.3 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 候选人] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/recruit/candidate 目录。
# 列表
支持按姓名、手机号、职位、负责人、性别、年龄、学历、渠道、当前面试官、面试时间和创建时间筛选,顶部按候选人状态展示页签及数量。页面根据候选人当前状态,把下一步主操作直接展示在操作列,其余动作收纳在【更多】菜单。

# 新增 / 修改
新增 / 修改通过弹窗 RecruitCandidateForm.vue 完成,维护姓名、联系方式、学历、应聘职位、招聘渠道、简历和备注。修改候选人基本资料不会跳过当前招聘阶段;职位、渠道的批量调整由独立操作处理。

# 详情
点击候选人姓名进入 @/views/hrm/recruit/candidate/detail/index.vue,展示基本资料、简历附件、历次面试和操作日志。详情页【编辑】继续复用 RecruitCandidateForm.vue。

# 初选、Offer、淘汰与恢复
新候选人可执行【初选通过】;面试通过后可继续安排复试,或执行【发 Offer】。除已淘汰、已入职外,候选人都可通过 RecruitCandidateEliminateForm.vue 选择淘汰原因并填写备注;已淘汰候选人可【恢复】为新候选人。
# 批量操作
勾选候选人后,页面提供以下批量操作。它们由前端复用单条接口逐项执行,并反馈成功和失败数量:
- 批量流转:
RecruitCandidateStatusListForm.vue,批量推进候选人状态。 - 批量面试:
RecruitInterviewForm.vue,批量安排同一场面试。 - 修改职位:
RecruitCandidatePostListForm.vue,批量改签应聘职位。 - 修改渠道:
RecruitCandidateChannelListForm.vue,批量修正招聘渠道。 - 批量淘汰:
RecruitCandidateEliminateForm.vue,批量选择淘汰原因。 - 批量删除:受下方「删除限制」约束。
# 一键清理
点击【一键清理】打开 RecruitCandidateCleanForm.vue,按状态和持续未跟进天数(3、5、7、15、30、45 天)查询目标候选人,确认后批量淘汰,淘汰原因固定为「长期未跟进」。
一键清理不是物理删除,只处理新候选人、初选通过、安排面试、面试通过四类状态,并保留全部招聘过程记录。
# 删除限制
只有尚未生成员工档案,且处于新候选人、初选通过、安排面试、面试通过或已淘汰状态时,页面才展示【删除】。已发 Offer、待入职、已入职以及已经关联员工档案的候选人不能直接删除,批量删除遵循相同限制。
# 2.4 面试记录
面试记录由 HrmRecruitInterviewController 提供接口(/hrm/recruit/interview),在候选人详情的【面试记录】页签中展示。
hrm_recruit_interview 表结构
CREATE TABLE `hrm_recruit_interview` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '面试编号',
`candidate_id` bigint NOT NULL COMMENT '候选人编号',
`type` tinyint DEFAULT NULL COMMENT '面试方式',
`stage_number` int DEFAULT NULL COMMENT '面试轮次',
`interview_employee_id` bigint DEFAULT NULL COMMENT '主面试官员工编号',
`other_interview_employee_ids` varchar(255) DEFAULT NULL COMMENT '其他面试官员工编号数组',
`interview_time` datetime DEFAULT NULL COMMENT '面试时间',
`address` varchar(255) DEFAULT NULL COMMENT '面试地址',
`result` tinyint DEFAULT NULL COMMENT '面试结果',
`evaluate` varchar(255) DEFAULT NULL COMMENT '面试评价',
`cancel_reason` varchar(255) DEFAULT NULL COMMENT '取消原因',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘面试记录';
① candidate_id 关联 hrm_recruit_candidate ���的 id 字段,一名候选人可以有多轮面试记录。
② type 面试方式对应字典 hrm_recruit_interview_type。主面试官和其他面试官都关联在职员工,其他面试官会去重并排除主面试官。
③ 枚举 result 面试结果(HrmRecruitInterviewResultEnum),对应字典 hrm_recruit_interview_result:1 面试未完成、2 面试通过、3 面试未通过、4 面试取消。
# 安排、改期与取消面试
新候选人、初选通过和面试通过状态可以通过 RecruitInterviewForm.vue 安排面试;面试通过后再次安排时,系统自动创建下一轮复试。当前面试可以通过同一表单改期,或通过 RecruitInterviewResultForm.vue 登记为取消并填写原因。

# 登记面试结果
候选人处于「安排面试」时,使用 RecruitInterviewResultForm.vue 登记通过或未通过。通过后候选人进入面试通过,未通过后进入已淘汰;取消会保留当前面试阶段,HR 可以重新安排同一轮面试。

# 查看面试历史
候选人详情的 RecruitCandidateInterviewList.vue 按轮次展示面试方式、面试官、时间、地址、结果、评价和取消原因。该页签只读,不提供编辑或删除按钮。

# 2.5 转为员工
# 转为员工
面试通过或已发 Offer,且尚未关联员工档案的候选人可以【转为员工】。转换表单复用 @/views/hrm/employee/EmployeeForm.vue,自动带出姓名、手机号、性别、学历、职位、部门、招聘渠道和计划入职时间。
后端在同一事务中创建 hrm_employee,写入 candidate_id、channel_id,并根据员工入职状态把候选人更新为待入职或已入职。

# 确认入职
候选人已转成待入职员工后,候选人列表的主操作变为【确认入职】。页面继续复用 EmployeeForm.vue 补全实际入职资料,员工和候选人状态在同一业务流程中更新为已入职,详见 《【员工】员工管理》。

# 3. 招聘渠道
招聘渠道,由 HrmRecruitChannelController 提供接口(/hrm/recruit/channel),供候选人、招聘职位和员工招聘来源共同使用。
# 3.1 表结构
CREATE TABLE `hrm_recruit_channel` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '招聘渠道编号',
`name` varchar(255) NOT NULL COMMENT '渠道名称',
`system_flag` bit DEFAULT b'0' COMMENT '是否系统内置',
`status` tinyint DEFAULT NULL COMMENT '状态',
`sort` int DEFAULT NULL COMMENT '显示顺序',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘渠道';
① system_flag 区分系统内置和租户自定义渠道。系统内置渠道随初始化脚本导入,不可删除,也不能把自定义渠道改成系统渠道。
② status 控制是否出现在选择器中。停用渠道不再出现在新增候选人、员工等选择器中,但已有业务记录仍保留原渠道编号。
③ hrm_employee.channel_id 和 hrm_recruit_candidate.channel_id 都逻辑关联本表的 id 字��。
# 3.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 招聘设置 -> 招聘渠道] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/recruit/channel 目录。
# 列表
支持按渠道名称、状态筛选,列表展示系统标识、排序和备注,并提供状态开关。

# 新增 / 修改
新增 / 修改通过弹窗 RecruitChannelForm.vue 完成,维护名称、排序、状态和备注。

# 删除与渠道承接
系统内置渠道不可删除。删除自定义渠道时,RecruitChannelDeleteForm.vue 要求选择另一个启用渠道作为承接方:后端先把员工和候选人的 channel_id 迁移到承接渠道,然后删除原渠道,避免历史招聘来源悬空。
# 4. 淘汰原因
淘汰原因,由 HrmRecruitConfigController 提供接口(/hrm/recruit/config),供单人淘汰、批量淘汰和一键清理表单使用。
# 4.1 表结构
淘汰原因保存在通用的 hrm_config 表:type=1(HrmConfigTypeEnum.RECRUIT_ELIMINATE)表示招聘淘汰原因,每个原因使用一条记录的 value 保存,并通过 sort 控制顺序。完整表结构详见 《【员工】员工管理》§3.1 表结构。
# 4.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 招聘设置 -> 淘汰原因] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/recruit/setting/eliminate 目录。

# 新增、删除与保存
页面以整组编辑的方式维护:点击【新增】或行内【删除】只调整当前页面列表,点击【保存】才整组提交,并校验原因不能为空、不能重复。
候选人淘汰时可以选择配置原因并补充备注。删除配置原因不会改写历史候选人已经保存的淘汰原因文本。