【招聘】招聘管理
招聘管理由 yudao-module-hrm 后端模块的 recruit 包实现,前端页面位于 @/views/hrm/recruit。模块围绕“招聘职位 → 候选人 → 面试 → 待入职员工 → 确认入职”展开,招聘流程全部在管理端完成,录用后再衔接员工档案。
本文包含三个部分:
- 招聘职位:维护用人部门、招聘要求、薪资范围、负责人、面试官和招聘状态。
- 候选人与面试:维护候选人资料,完成初选、面试、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 '职位名称',
`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 '招聘原因',
`work_time` tinyint DEFAULT NULL COMMENT '工作经验要求',
`education_require` tinyint 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 '薪资单位',
`min_age` int DEFAULT NULL COMMENT '最小年龄',
`max_age` int DEFAULT NULL COMMENT '最大年龄',
`latest_entry_time` datetime DEFAULT NULL COMMENT '最迟到岗时间',
`owner_employee_id` bigint DEFAULT NULL COMMENT '招聘负责人员工编号',
`interview_employee_ids` varchar(255) DEFAULT NULL COMMENT '面试官员工编号数组',
`description` varchar(4000) DEFAULT NULL COMMENT '职位描述',
`emergency_level` tinyint DEFAULT NULL COMMENT '紧急程度',
`post_type_id` bigint 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 员工关系,员工绑定后台账号后才能接收对应站内通知。
③ 薪资“面议”和年龄“不限”使用统一哨兵值 -1。前端展示业务文案,后端按同一常量校验薪资和年龄范围。
④ status=1 表示招聘中,status=0 表示停止招聘。停止时记录 stop_reason,重新招聘时清空停止原因。
职位类型表结构
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 自关联形成职位类型树。当前由 HrmRecruitPostTypeController(/hrm/recruit/post-type)提供 GET /hrm/recruit/post-type/list,作为职位表单的选择数据;职位类型没有独立管理菜单,因此本文不虚构新增、修改或删除操作。
# 1.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 招聘职位] 菜单,列表页面位于 @/views/hrm/recruit/post/index.vue。
# 列表
列表通过 HrmRecruitPostController 的 GET /hrm/recruit/post/page 查询,默认展示招聘中的职位,可按职位名称、工作性质、工作城市、用人部门和招聘负责人筛选;招聘中、停止招聘的页签数量由 GET /hrm/recruit/post/status-count 返回。点击职位名称进入详情,行内可直接编辑或变更招聘状态。

# 新增与修改
新增、修改都使用 RecruitPostForm.vue,分别调用 POST /hrm/recruit/post/create、PUT /hrm/recruit/post/update。表单填写用人部门、工作城市、人数、招聘原因、经验与学历要求、薪资、年龄、最迟到岗时间、负责人、面试官和职位描述;生命周期初始状态由后端设置为招聘中,不在创建表单中提供。

# 详情
点击职位名称进入 @/views/hrm/recruit/post/detail/index.vue,页面调用 GET /hrm/recruit/post/get 展示完整招聘要求,并通过 HRM 操作日志接口展示操作记录。详情页的【编辑】仍打开 RecruitPostForm.vue,继续使用同一职位编号,因此历史候选人与职位关系保持不变。
# 停止与重新招聘
招聘中的职位点击【停止招聘】后必须填写停止原因,停止后的职位可点击【重新招聘】恢复;两类动作都调用 PUT /hrm/recruit/post/update-status。招聘职位不提供删除操作,已有候选人、面试和操作记录始终保留。
# 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 '应聘职位编号',
`stage_number` int 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 '最近工作单位',
`channel_id` bigint DEFAULT NULL COMMENT '招聘渠道编号',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`status` tinyint DEFAULT NULL COMMENT '候选人状态',
`eliminate` varchar(255) DEFAULT NULL COMMENT '淘汰原因',
`status_update_time` datetime DEFAULT NULL COMMENT '状态更新时间',
`entry_time` datetime DEFAULT NULL COMMENT '入职时间',
`resume_urls` varchar(4000) DEFAULT NULL COMMENT '简历附件地址数组',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘候选人';
① post_id 关联 hrm_recruit_post.id,channel_id 关联 hrm_recruit_channel.id;resume_urls 保存简历附件地址数组。
② stage_number 表示当前面试轮次,status_update_time 记录最近一次流程状态变化,用于状态统计和候选人清理。
③ 候选人状态如下:
| 值 | 状态 | 主要操作 |
|---|---|---|
1 | 新候选人 | 初选通过、直接面试通过、淘汰 |
2 | 初选通过 | 安排面试、退回、直接面试通过 |
3 | 安排面试 | 改期、登记结果、取消面试 |
4 | 面试通过 | 安排复试、发 Offer、转员工 |
5 | 已发 Offer | 转员工 |
6 | 待入职 | 等待员工确认入职 |
7 | 已淘汰 | 恢复为新候选人 |
8 | 已入职 | 流程完成 |
新候选人 → 初选通过 → 安排面试 → 面试通过 → 已发 Offer → 待入职 → 已入职
│
└──────────────→ 已淘汰
# 2.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 候选人] 菜单,列表页面位于 @/views/hrm/recruit/candidate/index.vue。
# 列表
列表调用 HrmRecruitCandidateController 的 GET /hrm/recruit/candidate/page,支持按姓名、手机号、职位、负责人、性别、年龄、学历、渠道、当前面试官、面试时间和创建时间筛选;各状态页签数量由 GET /hrm/recruit/candidate/status-count 返回。页面根据候选人当前状态,把下一步主操作直接展示在操作列,其余动作收纳在【更多】菜单。

# 新增与修改
新增、编辑使用 RecruitCandidateForm.vue,分别调用 POST /hrm/recruit/candidate/create、PUT /hrm/recruit/candidate/update,维护姓名、联系方式、学历、应聘职位、招聘渠道、简历和备注。修改候选人基本资料不会跳过当前招聘阶段;职位、渠道的批量调整由独立操作处理。
# 详情
点击候选人姓名进入 @/views/hrm/recruit/candidate/detail/index.vue,通过 GET /hrm/recruit/candidate/get 加载基本资料和简历附件,通过 HrmRecruitInterviewController 的 GET /hrm/recruit/interview/list-by-candidate 展示历次面试,并展示操作日志。详情页【编辑】继续复用 RecruitCandidateForm.vue。
# 初选、Offer、淘汰与恢复
新候选人可以调用 PUT /hrm/recruit/candidate/update-status 执行【初选通过】;面试通过后可以继续安排复试,或调用同一接口执行【发 Offer】。除已淘汰、已入职外,候选人都可通过 RecruitCandidateEliminateForm.vue 调用 PUT /hrm/recruit/candidate/eliminate,选择淘汰原因并填写备注;已淘汰候选人可调用 PUT /hrm/recruit/candidate/update-status 恢复为新候选人。
# 批量操作
勾选候选人后,页面提供以下真实批量操作:
- 批量流转:
RecruitCandidateStatusListForm.vue逐项调用PUT /hrm/recruit/candidate/update-status。 - 批量面试:
RecruitInterviewForm.vue逐项调用面试POST /hrm/recruit/interview/create。 - 修改职位:
RecruitCandidatePostListForm.vue逐项调用PUT /hrm/recruit/candidate/update-post。 - 修改渠道:
RecruitCandidateChannelListForm.vue逐项调用PUT /hrm/recruit/candidate/update-channel。 - 批量淘汰:
RecruitCandidateEliminateForm.vue逐项调用PUT /hrm/recruit/candidate/eliminate。 - 批量删除:逐项调用
DELETE /hrm/recruit/candidate/delete。
这些批量动作由前端复用单条接口执行,会反馈成功和失败数量;后端没有额外的批量状态接口。
# 一键清理
点击【一键清理】打开 RecruitCandidateCleanForm.vue。表单通过 GET /hrm/recruit/candidate/clean-ids 按状态和持续天数查询目标,支持 3、5、7、15、30、45 天;确认后逐项调用 PUT /hrm/recruit/candidate/eliminate,淘汰原因为“长期未跟进”。一键清理不是物理删除,只处理新候选人、初选通过、面试中、面试通过四类状态,并保留招聘过程记录。
# 删除限制
只有尚未生成员工档案,且处于新候选人、初选通过、面试中、面试通过或已淘汰状态时,页面才展示【删除】。已发 Offer、待入职、已入职以及已经关联员工档案的候选人不能直接删除;批量删除遵循相同限制。
# 2.3 面试记录
面试记录表结构
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 '面试地址',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`result` tinyint DEFAULT NULL COMMENT '面试结果',
`evaluate` varchar(255) DEFAULT NULL COMMENT '面试评价',
`cancel_reason` varchar(255) DEFAULT NULL COMMENT '取消原因',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘面试记录';
candidate_id 与候选人是 1:N。新建复试时轮次递增;主面试官和其他面试官都关联在职员工,其他面试官会去重并排除主面试官。
# 安排、改期与取消面试
新候选人、初选通过和面试通过状态可以使用 @/views/hrm/recruit/candidate/RecruitInterviewForm.vue 安排面试,调用 HrmRecruitInterviewController 的 POST /hrm/recruit/interview/create;面试通过后再次安排时,系统自动创建下一轮复试。当前面试可以通过同一表单调用 PUT /hrm/recruit/interview/update 改期,或通过 RecruitInterviewResultForm.vue 调用 PUT /hrm/recruit/interview/update-result 登记为取消并填写原因。

# 登记面试结果
候选人处于面试中时,使用 RecruitInterviewResultForm.vue 调用 PUT /hrm/recruit/interview/update-result 登记通过或未通过。通过后候选人进入面试通过;未通过后进入已淘汰。取消会保留当前面试阶段,HR 可以重新安排同一轮面试。
# 查看面试历史
候选人详情的 RecruitCandidateInterviewList.vue 通过 GET /hrm/recruit/interview/list-by-candidate 按轮次展示面试方式、面试官、时间、地址、结果、评价和取消原因。该历史页签当前只读,不展示编辑或删除按钮;DELETE /hrm/recruit/interview/delete 虽由后端提供,但不作为当前页面的常规业务操作。
# 2.4 转为员工
# 转为员工
面试通过或已发 Offer 且尚未关联员工档案的候选人可以【转为员工】。转换表单复用 @/views/hrm/employee/EmployeeForm.vue,自动带出姓名、手机号、性别、学历、职位、部门、招聘渠道和计划入职时间,提交时调用 HrmRecruitCandidateController 的 POST /hrm/recruit/candidate/convert-employee。
后端在同一事务中创建 hrm_employee,写入 candidate_id、channel_id,并根据员工入职状态把候选人更新为待入职或已入职。待入职员工后续执行“确认入职”时,候选人同步进入已入职状态。
# 确认入职
候选人已转成待入职员工后,候选人列表的主操作变为【确认入职】。页面继续复用 EmployeeForm.vue,调用员工接口 PUT /hrm/employee/confirm-entry 补全实际入职资料;员工和候选人状态在同一业务流程中更新为已入职。
# 3. 招聘渠道
招聘渠道由 HrmRecruitChannelController 提供接口(/hrm/recruit/channel),供候选人、招聘职位和员工招聘来源共同使用。
# 3.1 表结构
CREATE TABLE `hrm_recruit_channel` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '招聘渠道编号',
`system_flag` bit DEFAULT b'0' COMMENT '是否系统内置',
`status` tinyint DEFAULT NULL COMMENT '状态',
`name` varchar(255) NOT NULL COMMENT '渠道名称',
`sort` int DEFAULT NULL COMMENT '显示顺序',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 招聘渠道';
system_flag 区分系统内置和租户自定义渠道,status 控制是否出现在选择器中;员工、候选人的 channel_id 都逻辑关联本表。
# 3.2 管理后台
对应 [HRM 人力资源 -> 招聘管理 -> 招聘设置 -> 招聘渠道] 菜单,页面位于 @/views/hrm/recruit/channel/index.vue。
# 列表
页面调用 HrmRecruitChannelController 的 GET /hrm/recruit/channel/page,支持按渠道名称、状态筛选,列表展示系统标识、排序和备注。状态开关直接调用 PUT /hrm/recruit/channel/update-status;被停用渠道不会继续出现在新增候选人、员工等选择器中,但已有业务记录仍保留原渠道编号。

# 新增与修改
点击【新增】或【编辑】打开 RecruitChannelForm.vue,分别调用 POST /hrm/recruit/channel/create、PUT /hrm/recruit/channel/update,维护名称、排序、状态和备注。系统内置标识由后端数据决定,表单不能把自定义渠道改成系统渠道。
# 删除与渠道承接
系统内置渠道不可删除。删除自定义渠道时,RecruitChannelDeleteForm.vue 要求选择另一个启用渠道作为承接方,再调用 DELETE /hrm/recruit/channel/delete;后端先迁移员工和候选人的 channel_id,然后删除原渠道,避免历史招聘来源悬空。
# 4. 淘汰原因
淘汰原因由 HrmRecruitConfigController 提供接口(/hrm/recruit/config),供单人淘汰、批量淘汰和一键清理表单使用。
# 4.1 表结构
数据保存在通用的 hrm_config 表:type=1 表示招聘淘汰原因,每个原因使用一条记录的 value 保存,并通过 sort 控制顺序。完整表结构见 《【员工】员工管理》员工字段设置。
# 4.2 管理后台
# 新增、删除与保存
对应 [HRM 人力资源 -> 招聘管理 -> 招聘设置 -> 淘汰原因] 菜单,页面位于 @/views/hrm/recruit/setting/eliminate/index.vue。进入页面调用 GET /hrm/recruit/config/eliminate-reason/list;点击【新增】或行内【删除】只调整当前页面列表,点击【保存】才通过 POST /hrm/recruit/config/eliminate-reason/save 整组提交。页面会校验原因不能为空、不能重复。
候选人淘汰时可以选择配置原因并补充备注;如果候选人正在面试,当前未完成面试会同步登记为未通过。删除配置原因不会改写历史候选人已经保存的淘汰原因文本。