咱们先聊聊为啥要做人事管理系统。企业招聘、入职、转正、调动……这些环节一环扣一环,如果全部靠 Excel+邮件、人肉审批,效率低还容易出错。尤其是“转正管理”和“调动管理”这俩块,既涉及员工职业轨迹,也关联薪酬、组织架构,做不好团队士气、流程合规就会出问题。
所以,咱今天的目标就是:带你从零开始,搭建一套高效可落地的“转正管理”和“调动管理”模块,给出架构图、流程图、开发技巧、实战代码,让企业马上用起来。
注:本文示例所用方案模板:简道云人事管理系统,给大家示例的是一些通用的功能和模块,都是支持自定义修改的,你可以根据自己的需求修改里面的功能。
一、什么是人事管理系统?
人事管理系统(HRMS)本质上是覆盖员工全生命周期的数字化平台,包含:
- 招聘管理(招人)
- 入职管理(办手续)
- 转正管理(试用到正式)
- 调动管理(岗位变更)
- 离职管理(结束劳动关系)
- 绩效考核、培训等附属模块
今天重点聚焦第 3 和第 4 块:
- 转正管理:帮HR和部门主管统一审批员工试用期转正,自动触发薪资、权限调整。
- 调动管理:处理员工内部调岗、部门变更的审核,保证组织架构、薪资、报表及时同步。
二、系统整体架构图
markdown
┌───────────────────────────────────────────────┐
│ 前端(Vue/React) │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ 转正模块 │ │ 调动模块 │ │ 公共组件 │ │
│ └──────────┘ └──────────┘ └───────────┘ │
└───────────────────────────────────────────────┘
│ ▲
▼ │
┌───────────────────────────────────────────────┐
│ 后端(Spring Boot) │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │转正Controller│ 调动Controller │ 公共Service │ │
│ └──────────┘ └──────────┘ └───────────┘ │
│ │ │ │ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │转正Service │ 调动Service │ 用户权限Service │ │
│ └──────────┘ └──────────┘ └───────────┘ │
│ │ │ │ │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ 转正DAO │ │ 调动DAO │ │ 通知消息DAO │ │
│ └──────────┘ └──────────┘ └───────────┘ │
│ │ │
└───────────────┼────────────────────────────────┘
│
▼
┌───────────────┐
│ MySQL/Oracle │
└───────────────┘
三、转正管理模块
1.功能概览
- 员工试用期到期自动提醒
- 转正申请发起、审批(多级)
- 审批结果自动更新员工状态、薪资方案
- 通知邮件/站内信推送
- 管理员可回溯历史审批记录
2.业务流程
mermaid
graph TD
A[试用期到期提醒] --> B[员工发起转正申请]
B --> C[直线经理审批]
C -->|同意| D[HR审批]
C -->|拒绝| E[流程结束]
D -->|同意| F[转正成功,更新状态]
D -->|拒绝| G[流程结束,通知员工]
F --> H[触发薪资调整&权限开放]
3.开发技巧
- 定时任务结合消息队列 用 Spring 的 @Scheduled 定时扫描即将到期试用期的员工,发送消息到 RabbitMQ。 异步消费后,生成待办任务,减少高峰期数据库压力。
- 工作流引擎 如果审批节点复杂,可以集成 Activiti/Flowable,动态配置多级审批。 简单场景下,用状态机(StateMachine)也可以搞定。
- 统一异常和审批日志 所有审批操作走相同入口,捕获异常。 审批日志统一记录到 approval_log,方便审计、回溯。
- 前端组件化 把审批表单、审批流组件抽成可复用的 UI 组件,方便调动模块复用。
4.代码参考
转正实体类 PromotionApplication.java
java
@Entity
@Table(name = "promotion_application")
public class PromotionApplication {
@Id @GeneratedValue private Long id;
private Long employeeId;
private Date applyDate;
private String status; // PENDING, APPROVED, REJECTED
private Long currentApproverId;
private String remark;
// getters & setters
}
Service 层:PromotionService.java
java
@Service
public class PromotionService {
@Autowired private PromotionDao dao;
@Autowired private ApprovalLogService logService;
public void apply(Long empId) {
PromotionApplication app = new PromotionApplication();
app.setEmployeeId(empId);
app.setApplyDate(new Date());
app.setStatus("PENDING");
app.setCurrentApproverId(findManager(empId));
dao.save(app);
// 推送待办
notificationService.notify(app.getCurrentApproverId(), "有新的转正申请待审批");
}
public void approve(Long appId, Long approverId, boolean pass, String remark) {
PromotionApplication app = dao.findById(appId).orElseThrow();
if (!app.getCurrentApproverId().equals(approverId)) throw new BizException("无审批权限");
logService.record(appId, approverId, pass, remark);
if (!pass) {
app.setStatus("REJECTED");
dao.save(app);
notificationService.notify(app.getEmployeeId(),"您的转正申请被拒绝:"+remark);
return;
}
if (isHR(approverId)) {
app.setStatus("APPROVED");
dao.save(app);
completePromotion(app);
} else {
app.setCurrentApproverId(findHR());
dao.save(app);
notificationService.notify(app.getCurrentApproverId(),"有转正申请待审批");
}
}
private void completePromotion(PromotionApplication app) {
// 更新员工档案、薪资表、权限表
employeeService.updateStatus(app.getEmployeeId(), "正式员工");
salaryService.adjustToFormal(app.getEmployeeId());
notificationService.notify(app.getEmployeeId(),"恭喜,您已转正!");
}
}
Controller 层:PromotionController.java
java
@RestController
@RequestMapping("/api/promotion")
public class PromotionController {
@Autowired private PromotionService service;
@PostMapping("/apply")
public Result apply(@RequestParam Long empId) {
service.apply(empId);
return Result.success("申请提交成功");
}
@PostMapping("/approve")
public Result approve(@RequestBody ApproveDTO dto) {
service.approve(dto.getAppId(), dto.getApproverId(), dto.isPass(), dto.getRemark());
return Result.success("审批完成");
}
}
- 员工侧:在“转正申请”页面,一键提交申请,实时查看审批状态。
- 审批侧:在“待办审批”列表,点击审批弹窗,填写备注,一键同意/拒绝。
- 后台:审批日志、状态变更一目了然;自动触发薪资与权限同步,无需运维再跑脚本。
四、调动管理模块
1.功能概览
- 发起岗位/部门调动申请
- 多级审批:部门经理→HR→目标部门经理
- 调动生效日期管理
- 自动更新组织架构、薪资、副本开关
- 历史记录、回退功能
2.业务流程
mermaid
graph LR
A[员工发起调动申请] --> B[原部门经理审批]
B -->|同意| C[HR审批]
B -->|拒绝| D[结束,通知员工]
C -->|同意| E[目标部门经理审批]
C -->|拒绝| D
E -->|同意| F[调动完成,更新组织架构]
E -->|拒绝| D
F --> G[触发权限、薪资同步]
3.开发技巧
- 动态审批流配置 用数据库表配置每种调动类型(如:跨部门、跨岗位)对应审批节点,系统启动读取动态组装流程。
- 日期维度处理 调动可能是“未来某日生效”,要在生效日的零点通过定时器执行实际变更。
- 权限隔离 调动期间,员工可能需要同时保留原部门和目标部门权限,直到生效后统一切换。
- 回退方案 如果审批过程卡顿或审批人离职,可提供管理员强制回退/撤销功能。
4.代码参考
调动实体类 TransferApplication.java
java
@Entity
@Table(name = "transfer_application")
public class TransferApplication {
@Id @GeneratedValue private Long id;
private Long employeeId;
private String fromDept;
private String toDept;
private Date applyDate;
private Date effectiveDate;
private String status; // PENDING, APPROVED, REJECTED
private Long currentApproverId;
// getters/setters
}
Service 层:TransferService.java
java
@Service
public class TransferService {
@Autowired private TransferDao dao;
@Autowired private ApprovalLogService logService;
public void apply(TransferDTO dto) {
TransferApplication app = new TransferApplication();
BeanUtils.copyProperties(dto, app);
app.setApplyDate(new Date());
app.setStatus("PENDING");
app.setCurrentApproverId(findManager(app.getFromDept()));
dao.save(app);
notificationService.notify(app.getCurrentApproverId(),"有新的调动申请待审批");
}
public void approve(Long appId, Long approverId, boolean pass, String remark) {
TransferApplication app = dao.findById(appId).orElseThrow();
if (!app.getCurrentApproverId().equals(approverId)) throw new BizException("无审批权限");
logService.record(appId, approverId, pass, remark);
if (!pass) {
app.setStatus("REJECTED");
dao.save(app);
notificationService.notify(app.getEmployeeId(),"调动申请被拒绝:"+remark);
return;
}
Long next = findNextApprover(app, approverId);
if (next == null) {
app.setStatus("APPROVED");
dao.save(app);
scheduleEffective(app);
} else {
app.setCurrentApproverId(next);
dao.save(app);
notificationService.notify(next,"有调动申请待审批");
}
}
private void scheduleEffective(TransferApplication app) {
// 用定时任务触发生效
schedulerService.schedule(app.getId(), app.getEffectiveDate());
}
public void executeTransfer(Long appId) {
TransferApplication app = dao.findById(appId).orElseThrow();
employeeService.updateDept(app.getEmployeeId(), app.getToDept());
salaryService.adjustDeptAllowance(app.getEmployeeId(), app.getToDept());
app.setStatus("TRANSFERRED");
dao.save(app);
notificationService.notify(app.getEmployeeId(),"祝贺,您已正式调动到新部门");
}
}
5.实现效果
- 员工侧:填写调动申请单,指定生效日期,可以上传补充说明;实时查看审批进度。
- 审批侧:多级审批界面、审批历史;一键通过/驳回。
- 定时执行:生效当日凌晨自动执行组织架构变更、权限切换、薪资调整;运维无需介入。
五、FAQ
Q1:试用期延长后如何在转正流程中体现?
一般企业存在试用期延期的情况,比如需要再考察一个月。要兼容这种场景,可以在“转正审批”提交前,后台查询员工档案中的“试用到期日”,若延期,前端审批页面显示延期选项。审批人可在“审批备注”里选择“同意延期”并填写新的到期日期。后端处理时,若审批结果为延期,更新员工档案中的试用期结束时间,并将流程置为“延期待申请”状态,不走转正后续审批,直到新到期日再重新触发提醒。这样的方案既保留了审批轨迹,又能动态适配不同人。
Q2:调动后员工原有考核、培训等记录如何处理?
员工调动到新部门后,原部门的考核、培训数据仍然有效,但在报表中需要隔离。建议在数据模型上,把考核/培训记录与“所在部门”字段绑定,并且在报表查询时,动态根据“记录创建日期”对部门进行分段统计。比如“2025-07-01前属于A部的所有培训记录”依旧展现在A部报表里;“2025-07-02后属于B部的记录”则归到B部。这样既保证了历史数据不丢,又能使调动后的数据在新部门的统计里实时可见。开发时,可在 service 层统一封装:List records = recordDao.findByEmployeeAndDateRange(empId, start, end),再按部门分流即可。
Q3:如何保证审批流程中断或审批人失效时的后续处理?
现实里经理调岗、离职常常打断流程。系统需要提供:
- 代理审批:审批人离岗前,可在个人设置里指定代理人;流程到达自己节点时,自动转给代理人处理。
- 管理员干预:HR 管理员可在后台看到“卡住”的审批,直接移交给其他审批人或直接决策。
- 超时提醒和自动流转:若审批超过 SLA(如 48 小时)未处理,系统自动提醒或者直接跳过当前节点给下一节点。 建议在审批逻辑中统一调用:
java
Long actualApprover = isOverdue(node) ? findNextApprover(app, node) : node;
app.setCurrentApproverId(actualApprover);
并在前端给出黄色预警提示,使审批人及时响应。