SpringBoot+Vue3 招聘管理系统设计:需求审批→职位→候选人→面试→录用→入职全流程
SpringBootVue3 招聘管理系统设计需求审批→职位→候选人→面试→录用→入职全流程演示地址http://ruoyioffice.com | 源码1·GitHubruoyi-office | 源码2·GitCoderuoyi-office | 源码3·Giteeruoyi-office | 微信17156169080备注「RuoYi Office」招聘是 HR 工作里链条最长、参与方最多的一摊事——业务部门提需求、HR 开岗、招聘负责人在各渠道捞简历、面试官多轮面试、定薪发 offer、最后还要落地入职。多数公司却还停留在Excel 管简历、微信群约面试、口头通知发 offer的阶段简历散落各处、候选人到哪一步全靠记忆、面试评价没沉淀、offer 没审批就发出去、入职信息再手工抄一遍。RuoYi Office 用 8 张表把招聘拆成需求 → 职位 → 候选人 → 投递 → 面试 → 录用 → 入职七段链路需求单与录用单双 BPM 审批、面试结果驱动候选人状态机自动流转、录用审批通过自动建入职单、邮箱抓取简历自动转候选人让招聘从凭记忆升级为有数据、有留痕、可追溯的引擎。▲ 招聘管理功能架构全景①需求审批区BPM→ ②职位发布区 → ③候选人/投递/面试执行区 → ④录用审批区BPM→ ⑤自动入职联动8 张表贯穿招聘全链路引言招聘管理到底难在哪“不就是发个招聘、收点简历、面试录用吗”——没真正搭过招聘系统的人都这么想。但只要落地一次就会撞上一连串难题需求没审批岗位随便开业务部门一句话我们要招人HR 就开始招结果招到一半被告知编制没批、预算超了。招聘需求必须先走审批流明确岗位、人数、紧急度、预算、期望到岗时间批了才能开岗。候选人状态全靠记同一个候选人投了 A 岗、面了两轮、还在等 offer——这些状态散在 HR 脑子里和微信聊天记录里。一旦招聘负责人离职或请假候选人就丢了。候选人状态必须由系统自动流转投递→面试→待发 offer→已录用/已淘汰。面试评价不沉淀面试官面完口头说还行没有打分、没有评语、没有轮次记录。下一轮面试官无从参考复盘更无据可查。每一轮面试都要留轮次、时间、方式、结果、评分、总结。offer 没审批就发薪资、职级、试用期这些敏感信息招聘专员一拍脑袋就发给候选人结果定薪超标、和内部薪酬倒挂。录用必须走审批流部门和 HR 把关后才能发 offer且审批通过要自动生成入职单不用再抄一遍。简历散在各邮箱招聘邮箱里每天几十封简历HR 手工下载、手工录入候选人效率极低。系统应能定时抓取招聘邮箱解析出姓名/手机/邮箱一键转为候选人。本文以 RuoYi Office 的yudao-module-hrm招聘模块为例完整拆解其数据建模、双 BPM 流程、候选人状态机、录用-入职联动与邮箱抓取的设计方案。所有结论均来自真实源码。一、业务设计七段链路两道审批闸门1.1 核心理念长链路拆段关键节点设审批闸招聘 一条从想招人到人入职的长链路。RuoYi Office 把它拆成七段并在开岗前和发 offer 前两个关键节点设置 BPM 审批闸门[招聘需求申请单] ──审批通过──▶ [招聘职位] ──▶ [候选人档案] (BPM 闸门①) 手动开岗 │ ▼ [投递关系 Application] │ ▼ [面试记录(多轮)] │ 面试通过 ▼ [录用审批单] ──审批通过──▶ [自动生成入职单] (BPM 闸门②) 职位已录用人数1段实体是否走 BPM关键动作① 需求RecruitmentRequirementBillDO✅ 审批提交需求、部门HR 审批② 职位RecruitmentPositionDO❌HR 手动开岗关联需求单③ 候选人RecruitmentCandidateDO❌录入/导入/邮箱转入④ 投递RecruitmentApplicationDO❌候选人 × 职位记录阶段⑤ 面试RecruitmentInterviewRecordDO❌多轮面试结果驱动状态⑥ 录用RecruitmentOfferBillDO✅ 审批定薪定职级审批发 offer⑦ 入职EmployeeEntryBillDO✅ 审批录用通过后自动创建关键设计需求审批通过后不自动开岗——因为一个需求可能拆成多个职位不同渠道、不同子团队由 HR 在招聘职位里手动创建并关联requirementBillId保留灵活性。1.2 候选人状态机一条贯穿全程的主线候选人RecruitmentCandidateDO的status字段是一条由投递、面试、录用三类操作共同驱动的状态主线取自源码常量RecruitmentConstantsnew 新建 → applied 已投递 → interview 面试中 → offer_pending 待发offer → hired 已录用 │ rejected 已淘汰任意环节均可进入状态值含义由什么触发new新建刚录入档案候选人创建默认值applied已投递创建投递关系interview面试中投递阶段进入 interviewoffer_pending待发 offer面试结果 通过hired已录用录用审批单审批通过rejected已淘汰面试不通过 / 手动淘汰投递关系RecruitmentApplicationDO也有一条平行的stage字段applied / interview / offer_pending / offer_approved / rejected候选人状态由投递阶段映射而来——这样一个候选人投多个岗时每个投递的阶段独立候选人主状态取最新进展。二、系统设计模块职责与设计决策2.1 模块组成招聘管理位于HRM 人力资源 → 招聘管理目录下由 4 个一级页面 内嵌管理组成子模块页面功能面向角色招聘需求需求列表 / 详情BPM 表单提交需求、走审批、查看进度用人部门 HR招聘职位职位列表 / 详情开岗、关联需求、内嵌投递与面试管理招聘负责人候选人中心候选人列表 / 详情档案管理、Excel 导入、邮箱线索转入HR 招聘负责人录用审批录用列表 / 详情BPM 表单定薪定职级、走审批、联动入职HR 部门负责人投递/面试内嵌在职位详情投递登记、多轮面试录入招聘负责人 面试官邮箱抓取内嵌在候选人列表弹窗邮箱配置、线索列表、一键转候选人HR2.2 核心设计决策决策点方案理由需求是否审批需求单走 BPM控编制、控预算避免随意开岗需求与职位关系1 需求 : N 职位手动开岗一个需求可拆多个渠道/团队岗位候选人与职位关系投递关系Application多对多同一候选人可投多个岗各自独立流转状态流转面试结果自动驱动 stage / status减少人工改状态避免遗漏offer 是否审批录用单走 BPM定薪敏感需部门 HR 把关录用-入职衔接审批通过自动建入职单信息一次录入不重复抄录简历来源手动录入 Excel 导入 邮箱抓取覆盖内推、渠道、邮箱投递多来源三、PC 端功能实现3.1 招聘需求BPM 审批单招聘需求是整条链路的起点——用人部门填写岗位名称、需求人数、紧急度、期望到岗日期、岗位职责与任职资格、预算说明提交后走部门负责人 → 人力资源岗位两级审批。▲ 招聘需求列表每条需求带单据编号与流程状态草稿/审批中/通过/拒绝展示岗位名称、类别、需求人数、紧急度、期望到岗日期、申请部门需求设计要点走 BPM 控编制需求单processDefinitionKey hr_recruitment_requirement_bill流程为发起人 → 部门负责人审批 → 人力资源岗位审批 → 结束批了才能开岗。前后端字段别名前端用headcount / urgencyLevel / jobDescription后端 DO 用requirementCount / urgency / jobResponsibilities通过SaveReqVO的 getter/setter 别名兼容避免大改前端。紧急度字典驱动urgency关联字典hrm_recruitment_urgency普通/紧急/非常紧急列表可按紧急度筛选与排序。3.2 招聘职位与内嵌投递、面试需求批准后HR 在招聘职位开岗关联requirementBillId设置招聘负责人、发布日期。职位详情页内嵌了投递列表和面试记录弹窗把这个岗位有哪些候选人、面到第几轮集中呈现。▲ 招聘职位详情顶部展示岗位名称、类别、需求/已录用人数、招聘负责人、状态下方投递列表逐行展示候选人、当前阶段、面试操作入口职位与投递设计要点仅 open 状态可投递职位status有open / completed / closed三态Service 校验只有open职位能创建投递否则抛RECRUITMENT_POSITION_NOT_OPEN。已录用人数自动累加录用审批通过时hiredCount 1等于需求人数时 HR 可手动置为completed。面试多轮留痕每次面试是一条RecruitmentInterviewRecordDO记录轮次、时间、方式、结果、总分、总结下一轮面试官可参考历史。3.3 候选人中心与邮箱线索候选人中心是简历库——支持手动录入、Excel 批量导入以及从招聘邮箱抓取的线索一键转候选人。![候选人中心列表 - 展示姓名、手机、邮箱、来源、最高学历、当前公司、期望薪资、状态]▲ 候选人中心列表逐行展示候选人姓名、手机、邮箱、来源渠道、最高学历、当前公司与岗位、期望薪资、候选人状态新建/已投递/面试中/待发offer/已录用/已淘汰候选人设计要点多来源汇聚sourceType / sourceChannel区分内推、渠道BOSS/智联/猎聘/校招/猎头、邮箱抓取等来源便于统计各渠道转化率。Excel 导入去重按mobile / email去重支持updateSupport覆盖更新批量录入校园招聘简历高效。附件管理简历附件按业务类型hrm_recruitment_candidate关联详情页可在线预览。邮箱线索转候选人定时任务抓取招聘邮箱正则解析姓名/手机/邮箱HR 在线索列表点转候选人即创建或关联档案。四、流程设计需求单 录用单双 BPM 编排4.1 两条流程结构一致招聘模块有两张 BPM 单据都实现了FlowBillServiceHrmBillTypeEnum接口流程结构相同流程processDefinitionKey单据类型 code流程节点招聘需求hr_recruitment_requirement_bill207发起人 → 部门负责人 → 人力资源岗位 → 结束录用审批hr_recruitment_offer_bill208发起人 → 部门负责人 → 人力资源岗位 → 结束提交时调用processInstanceApi.submitProcessInstance(...)启动流程并把businessKey单据 ID绑定到流程实例上publicLongsubmitRecruitmentRequirementBill(RecruitmentRequirementBillSaveReqVOreqVO,LonguserId){// 1. 生成单据编号落库processStatus 审批中(1)RecruitmentRequirementBillDObillsaveOrGetBill(reqVO);// 2. 启动 BPM 流程绑定 businessKey 单据 IDStringprocessInstanceIdprocessInstanceApi.submitProcessInstance(userId,newBpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(HrmBillTypeEnum.HRM_RECRUITMENT_REQUIREMENT_BILL.getProcessDefinitionKey()).setBusinessKey(String.valueOf(bill.getId())));// 3. 回写流程实例 IDrecruitmentRequirementBillMapper.updateById(newRecruitmentRequirementBillDO().setId(bill.getId()).setProcessInstanceId(processInstanceId));returnbill.getId();}4.2 审批回调需求单只更新状态录用单联动入职两张单据的updateProcessStatus(businessKey, status)回调差异很大体现了需求是轻动作、录用是重动作需求单审批回调仅更新processStatus通过/拒绝不做其他联动——开岗是 HR 的人工决策。录用单审批通过时触发自动建入职单 候选人置为已录用 投递阶段推进 职位已录用人数 1四连动作。publicvoidupdateProcessStatus(StringbusinessKey,Integerstatus){RecruitmentOfferBillDObillrecruitmentOfferBillMapper.selectById(Long.valueOf(businessKey));RecruitmentOfferBillDOupdateObjnewRecruitmentOfferBillDO().setId(bill.getId()).setProcessStatus(status);// 审批通过 → 自动创建员工入职申请单并回写入职单 IDif(BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(status)){LongentryBillIdcreateEmployeeEntryBillFromOffer(bill);updateObj.setEmployeeEntryBillId(entryBillId);}recruitmentOfferBillMapper.updateById(updateObj);}五、后端核心实现5.1 投递创建候选人状态同步创建投递时先校验候选人存在、职位存在且为open默认阶段为applied并把候选人状态同步为对应值。mapCandidateStatus是投递阶段 → 候选人状态的映射函数privatevoidsyncCandidateStatus(LongcandidateId,Stringstage){recruitmentCandidateMapper.updateById(newRecruitmentCandidateDO().setId(candidateId).setStatus(mapCandidateStatus(stage)));}privateStringmapCandidateStatus(Stringstage){returnswitch(stage){caseRecruitmentConstants.APPLICATION_STAGE_INTERVIEW-RecruitmentConstants.CANDIDATE_STATUS_INTERVIEW;caseRecruitmentConstants.APPLICATION_STAGE_OFFER_PENDING-RecruitmentConstants.CANDIDATE_STATUS_OFFER_PENDING;caseRecruitmentConstants.APPLICATION_STAGE_REJECTED-RecruitmentConstants.CANDIDATE_STATUS_REJECTED;caseRecruitmentConstants.APPLICATION_STAGE_OFFER_APPROVED-RecruitmentConstants.CANDIDATE_STATUS_HIRED;default-RecruitmentConstants.CANDIDATE_STATUS_APPLIED;};}5.2 面试结果驱动阶段流转核心录入面试结果时syncApplicationStage根据面试结果决定投递的下一阶段并把候选人状态一并刷新——这是面试官填个结果系统自动推进候选人的关键privatevoidsyncApplicationStage(RecruitmentInterviewRecordDOinterviewRecord){StringnextStageresolveNextStage(interviewRecord.getInterviewResult());// 1. 更新投递阶段recruitmentApplicationMapper.updateById(newRecruitmentApplicationDO().setId(interviewRecord.getApplicationId()).setStage(nextStage));// 2. 同步候选人状态recruitmentCandidateMapper.updateById(newRecruitmentCandidateDO().setId(interviewRecord.getCandidateId()).setStatus(resolveCandidateStatus(nextStage)));}privateStringresolveNextStage(StringinterviewResult){// 字典值 pass/通过 → 待发 offerfail/不通过 → 淘汰其余保持面试中if(StringUtils.equalsAnyIgnoreCase(interviewResult,pass,通过)){returnRecruitmentConstants.APPLICATION_STAGE_OFFER_PENDING;}if(StringUtils.equalsAnyIgnoreCase(interviewResult,reject,fail,不通过)){returnRecruitmentConstants.APPLICATION_STAGE_REJECTED;}returnRecruitmentConstants.APPLICATION_STAGE_INTERVIEW;}设计巧思resolveNextStage同时兼容字典英文值pass / fail和中文通过 / 不通过即使字典配置不规范也能正确流转pending / hold待定/暂缓不会误推进到 offer。5.3 录用通过 → 自动建入职单 全链路收尾录用审批通过后createEmployeeEntryBillFromOffer从候选人与 offer 字段映射出入职单并完成候选人、投递、职位三处状态收尾privateLongcreateEmployeeEntryBillFromOffer(RecruitmentOfferBillDOofferBill){// 1. 从候选人 offer 映射入职单字段姓名/部门/计划入职日期/试用期/职级…创建入职申请单LongentryBillIdexecuteCreateEntryBill(buildEntryReqVO(offerBill));// 2. 候选人 → 已录用recruitmentCandidateMapper.updateById(newRecruitmentCandidateDO().setId(offerBill.getCandidateId()).setStatus(RecruitmentConstants.CANDIDATE_STATUS_HIRED));// 3. 投递阶段 → offer_approvedrecruitmentApplicationMapper.updateById(newRecruitmentApplicationDO().setId(offerBill.getApplicationId()).setStage(RecruitmentConstants.APPLICATION_STAGE_OFFER_APPROVED));// 4. 职位已录用人数 1RecruitmentPositionDOpositionrecruitmentPositionMapper.selectById(offerBill.getPositionId());recruitmentPositionMapper.updateById(newRecruitmentPositionDO().setId(position.getId()).setHiredCount(position.getHiredCount()1));returnentryBillId;}5.4 邮箱抓取简历线索招聘邮箱配置RecruitmentMailboxConfigDO支持 IMAP/POP3定时任务RecruitmentMailboxFetchJob按fetchIntervalMinutes拉取新邮件落为线索RecruitmentEmailLeadDO再正则解析手机号/邮箱HR 一键convertEmailLeadToCandidate转候选人——杜绝手工下载、手工录入。六、RuoYi Office 招聘模块创新设计6.1 双闸门而非全流程审批不是每一步都审批那会让招聘寸步难行而是只在开岗和发 offer两个钱与编制相关的节点设 BPM 闸门中间的投递、面试纯业务操作不走流程兼顾管控与效率。6.2 状态机自动驱动HR 少改状态候选人状态不靠 HR 手动点而是由投递、面试结果级联驱动面试官填通过投递自动到offer_pending、候选人自动到待发 offer。减少人工操作避免状态遗漏导致候选人卡死。6.3 录用-入职零断点传统流程里offer 发完 HR 要再去入职模块手工建单、抄一遍候选人信息。这里录用审批通过自动生成入职单并回写employeeEntryBillId招聘与入职无缝衔接信息只录一次。6.4 字段别名兼容前后端解耦前端历史命名headcount / planEntryDate / probationMonths与后端 DO 命名requirementCount / plannedEntryDate / probationPeriod不一致时通过SaveReqVO的别名 getter/setter 桥接无需大改任一端——这是真实项目里既要规范又不想推倒重来的务实做法。七、数据结构7.1 招聘需求申请单hrm_recruitment_requirement_bill字段类型说明bill_codevarchar(64)单据编号process_instance_id/process_statusvarchar / tinyintBPM 流程实例与状态position_name/position_categoryvarchar岗位名称 / 类别requirement_countint需求人数urgencyvarchar(32)紧急度字典expected_arrival_datedate期望到岗日期job_responsibilities/job_qualificationstext岗位职责 / 任职资格budget_remarkvarchar(500)预算说明7.2 候选人hrm_recruitment_candidate/ 投递hrm_recruitment_application表关键字段说明候选人name / mobile / email / id_card基本信息候选人source_type / source_channel来源类型 / 渠道候选人expected_salary / status期望薪资 / 候选人状态投递candidate_id / position_id候选人 × 职位投递stage / reject_reason当前阶段 / 淘汰原因投递talent_pool_flag是否进入人才库7.3 录用审批单hrm_recruitment_offer_bill字段类型说明candidate_id / application_id / position_idbigint关联链路employee_entry_bill_idbigint审批通过后生成的入职单 IDplanned_entry_datedate计划入职日期probation_periodint试用期月job_level / job_positionvarchar职级 / 职务salary_remarkvarchar(500)薪资说明设计要点录用单冗余了候选人、投递、职位三个 ID形成可追溯链路employee_entry_bill_id把录用与入职双向关联详情页可直接跳转入职单。八、技术亮点总结设计要点实现方式价值长链路拆段8 张表 7 段链路每段职责清晰可独立维护双 BPM 闸门需求单 录用单走 Flowable控编制、控定薪关键节点留痕候选人状态机投递/面试结果级联驱动 status减少人工改状态避免遗漏录用-入职联动审批通过自动建入职单信息一次录入零断点衔接多对多投递Application 关联候选人 × 职位一人投多岗各自独立流转已录用人数累加offer 通过 hiredCount1招聘进度实时可见邮箱抓取IMAP/POP3 定时任务 正则解析简历自动入库告别手工录入字段别名兼容SaveReqVO getter/setter 桥接前后端解耦不推倒重来字典驱动状态字符串常量 system_dict_data状态可配、前端统一渲染多来源汇聚source_type/channel 区分各渠道转化率可统计九、快速体验在线演示http://ruoyioffice.com/web/账号admin/admin123操作路径人力资源 → 招聘管理 → 招聘需求 / 招聘职位 / 候选人中心 / 录用审批推荐体验流程进入「招聘需求」新建一条需求并提交到「我的待办」走完审批在「招聘职位」基于已批需求开岗设置招聘负责人与发布日期在「候选人中心」录入或导入候选人也可配置邮箱抓取线索转候选人进入职位详情为候选人登记投递、录入多轮面试结果观察候选人状态自动流转面试通过后到「录用审批」新建录用单、定薪定职级并提交审批审批通过后回到「员工入职」查看自动生成的入职申请单验证录用-入职联动。源码仓库仓库地址GitHubgithub.com/yuqing2026/ruoyi-officeGitCodegitcode.com/zhouzhongyan/ruoyi-officeGiteegitee.com/yqzy1688/ruoyi-office结语招聘系统的核心设计思想是把一条最长的 HR 业务链路拆成可管控的段在花钱和定编制的节点设审批闸门其余环节用状态机自动驱动最后让录用无缝衔接入职。这套长链路 双闸门 状态机 自动联动的模式不只适用于招聘——采购申请→比价→下单→入库、合同起草→评审→签署→归档等同样长链路 关键节点审批的业务都可以照搬这套设计。你们团队现在是怎么管招聘的还在用 Excel 微信群吗欢迎在评论区聊聊你遇到的招聘管理痛点。常见问题FAQRuoYi Office 的招聘管理是开源免费的吗是。基于 RuoYi-Vue-Pro / Yudao 架构后端 Spring Boot 3.5 前端 Vue3开源可商用本地约 10 分钟即可启动。招聘模块位于yudao-module-hrm。招聘需求和录用 offer 支持自定义审批流程吗支持。两者均基于 Flowable BPMN 引擎processDefinitionKey分别为hr_recruitment_requirement_bill和hr_recruitment_offer_bill可在流程设计器中自定义审批节点、会签、条件分支无需改代码。候选人状态是手动改还是自动流转的自动流转为主。创建投递、录入面试结果时系统按映射规则自动同步候选人status如面试通过自动到待发 offerHR 也可手动淘汰。录用审批通过后还要手工建入职单吗不需要。录用审批通过会自动调用createEmployeeEntryBillFromOffer生成员工入职申请单并回写employeeEntryBillId候选人、投递、职位状态同步收尾。能批量导入简历或对接招聘邮箱吗能。候选人支持 Excel 批量导入按手机/邮箱去重也支持配置 IMAP/POP3 招聘邮箱定时任务抓取邮件解析为线索后一键转候选人。想要体验 RuoYi Office 的强大功能在线演示http://ruoyioffice.com/web/账号 admin / admin123源码仓库GitHub | GitCode | Gitee技术咨询添加微信17156169080备注「RuoYi Office」⭐如果觉得不错请给个 Star 支持一下