【考勤】考勤管理
考勤管理模块,管理端由 yudao-module-hrm 后端模块的 attendance 包实现,员工端由 portal.attendance 包实现,前端实现在 @/views/hrm/attendance 和 @/views/hrm/portal/attendance 目录。
考勤是 HRM 中实时计算特征最明显的模块:除考勤组、节假日、打卡和请假四张基础表外,日统计和月统计都不落库,而是按月加载规则与原始记录后现算。
- 考勤设置:按部门或员工配置班次、特殊日期、打卡条件和扣款规则,并维护全局节假日。
- 打卡与统计:HR 可补录手工打卡,系统根据班次、节假日��打卡和有效请假实时计算日 / 月考勤。
- 请假审批:员工从 HRM 员工端提交请假,审批复用 BPM;管理端只查询、查看流程和导出。
本文涉及表如下图所示:
# 1. 考勤组
考勤组,由 HrmAttendanceGroupController 提供接口(/hrm/attendance/group)。班次、特殊日期、地点、WiFi 和扣款规则都以 JSON 随考勤组整体保存,不建立独立业务表。
# 1.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段;JSON 字段由 TypeHandler 完成对象转换
CREATE TABLE `hrm_attendance_group` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '考勤组编号',
`name` varchar(50) NOT NULL COMMENT '考勤组名称',
`default_status` bit NOT NULL DEFAULT 0 COMMENT '是否默认考勤组',
`dept_ids` varchar(4000) DEFAULT NULL COMMENT '适用部门编号列表',
`employee_ids` varchar(4000) DEFAULT NULL COMMENT '适用员工编号列表',
`shifts` varchar(10000) NOT NULL COMMENT '班次配置 JSON',
`rest` bit NOT NULL DEFAULT 1 COMMENT '法定节假日是否休息',
`special_dates` varchar(4000) NOT NULL COMMENT '特殊日期 JSON',
`open_point_card` bit NOT NULL DEFAULT 0 COMMENT '是否启用定位打卡',
`open_wifi_card` bit NOT NULL DEFAULT 0 COMMENT '是否启用 WiFi 打卡',
`points` varchar(10000) NOT NULL COMMENT '打卡地点 JSON',
`wifis` varchar(10000) NOT NULL COMMENT '打卡 WiFi JSON',
`deduct_rule` varchar(4000) NOT NULL COMMENT '扣款规则 JSON',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 考勤组';
① dept_ids 关联 system_dept 表,employee_ids 关联 hrm_employee 表。保存适用范围时,系统会从其它考勤组移除冲突的部门和员工,保证一名员工只命中一个考勤组。
② 员工按「显式员工 → 当前部门及最近父部门 → 默认考勤组」的顺序匹配。default_status = true 的默认考勤组承担未匹配员工的兜底规则,可以修改但不能删除。
③ shifts 保存星期、上下班时点、允许打卡窗口和休息区间,支持跨日班次;special_dates 可把指定日期设为上班或休息。
④ deduct_rule 保存迟到、早退、旷工和缺卡的扣款方式:迟到、早退支持每月固定扣款 / 按分钟 / 按次数(HrmAttendanceLateEarlyDeductMethodEnum),旷工按天(HrmAttendanceAbsenteeismDeductMethodEnum),缺卡按次数(HrmAttendanceMisscardDeductMethodEnum)。
该表包含 5 个 JSON 配置字段。它们都没有独立编号和生命周期,由 HrmAttendanceGroupDO 的内嵌对象承载,随考勤组一次提交、整体保存。下面逐个展开:
shifts(班次配置):工作日、上下班时点、允许打卡窗口和休息区间。
shifts JSON 字段结构
[
{
"weeks": [1, 2, 3, 4, 5],
"startTime": "09:00",
"endTime": "18:00",
"clockInStartTime": "08:00",
"clockInEndTime": "09:30",
"clockOutStartTime": "18:00",
"clockOutEndTime": "20:00",
"restStartTime": "12:00",
"restEndTime": "13:00",
"excludeRestTime": true
}
]
① weeks 使用 1 至 7 表示星期一至星期日,同一考勤组可配置多个班次,把不同工作日拆到不同元素里。
② 所有时间字段统一使用 HH:mm。clockIn* 是上班打卡的允许窗口,clockOut* 是下班打卡的允许窗口,窗口之外的打卡不计入当日考勤。
③ restStartTime / restEndTime 是午休区间,excludeRestTime = true 时该区间不计入工作时长。下班时间早于上班时间即表示跨日班次。
special_dates(特殊日期):指定日期是上班还是休息,优先级高于全局节假日和每周班次。
special_dates JSON 字段结构
[
{ "type": 2, "date": "2026-10-01 00:00:00" },
{ "type": 1, "date": "2026-10-11 00:00:00" }
]
type 对应 HrmAttendanceHolidayTypeEnum:1 上班、2 休息。判定某一天是否需要出勤时,先看特殊日期,再看节假日,最后才看每周班次,因此调休上班可以直接在这里配置。
points(打卡地点):地点名称、地址、经纬度和有效打卡半径。
points JSON 字段结构
[
{
"name": "公司总部",
"address": "杭州市余杭区文一西路",
"latitude": 30.28172,
"longitude": 120.02341,
"radius": 300
}
]
radius 有效打卡半径,单位为米,员工定位落在任一地点半径内即视为有效。该配置需要考勤组开启 open_point_card 才生效。
wifis(打卡 WiFi):WiFi 名称和 MAC 地址。
wifis JSON 字段结构
[
{ "ssid": "Yudao-Office", "mac": "00:11:22:33:44:55" }
]
ssid 是 WiFi 名称,mac 是路由器 MAC 地址;同名 WiFi 靠 MAC 区分,避免蹭到同名热点。该配置需要考勤组开启 open_wifi_card 才生效。
deduct_rule(扣款规则):迟到、早退、旷工和缺卡的扣款方式与金额。
deduct_rule JSON 字段结构
不同于上面四个字段,扣款规则是单个对象而非数组:
{
"lateMethod": 2,
"lateDeductMoney": 1.00,
"earlyMethod": 2,
"earlyDeductMoney": 1.00,
"absenteeismMethod": 1,
"absenteeismDeductMoney": 100.00,
"misscardMethod": 1,
"misscardDeductMoney": 20.00
}
① lateMethod、earlyMethod 对应 HrmAttendanceLateEarlyDeductMethodEnum:1 每月固定扣款、2 按分钟、3 按次数。示例中按分钟扣款,迟到 1 分钟扣 1 元。
② absenteeismMethod = 1 表示按旷工天数(HrmAttendanceAbsenteeismDeductMethodEnum),misscardMethod = 1 表示按缺卡次数(HrmAttendanceMisscardDeductMethodEnum)。
③ 四类扣款金额最终汇总为考勤扣款,供薪资核算取数,详见 《【薪资】月度工资、工资条》。
# 1.2 管理后台
对应 [HRM 人力资源 -> 考勤管理 -> 考勤设置 -> 考勤组设置] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/attendance/config/group 目录。
# 列表
展示名称、适用范围、打卡方式和是否默认组,可按名称筛选。默认考勤组的删除按钮不可用。

# 新增
通过弹窗 AttendanceGroupForm.vue 完成。表单在一个 Dialog 中依次配置基本信息、适用部门 / 员工、班次、法定节假日、特殊日期、定位、WiFi 和扣款规则;班次与特殊日期使用表单内的编辑弹窗,不需要跳转其它菜单。
表单要求至少启用定位或 WiFi 一种打卡方式,并校验地点、半径、SSID 和 MAC。

# 修改
弹窗结构与新增相同,打开时回显完整聚合。保存适用范围时同样会从其它考勤组移除冲突的部门和员工。
# 删除
非默认考勤组可在确认后逻辑删除。默认考勤组承担未匹配员工的兜底规则,后端会拒绝删除。
# 2. 节假日
节假日,由 HrmAttendanceHolidayController 提供接口(/hrm/attendance/holiday),用于维护全局的上班日和休息日。
# 2.1 表结构
CREATE TABLE `hrm_attendance_holiday` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`date` datetime NOT NULL COMMENT '日期',
`type` tinyint DEFAULT 2 COMMENT '日期类型',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 考勤节假日';
① 枚举 type 日期类型(HrmAttendanceHolidayTypeEnum),对应字典 hrm_attendance_holiday_type:1 上班、2 休息。
② 实际班次按「考勤组特殊日期 → 全局节假日 → 每周班次」的优先级解析。补班日没有命中星期班次时,使用考勤组的第一条班次。
# 2.2 管理后台
对应 [HRM 人力资源 -> 考勤管理 -> 考勤设置 -> 节假日设置] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/attendance/config/holiday 目录。
# 列表
可按日期范围和日期类型筛选。

# 新增 / 修改
新增 / 修改通过弹窗 AttendanceHolidayForm.vue 完成,维护日期和「上班 / 休息」类型。

# 删除
删除后,该日期重新按考勤组的特殊日期或每周班次解析。
# 3. 打卡记录
打卡记录,由 HrmAttendanceClockController 提供接口(/hrm/attendance/clock),保存员工实际打卡、应打卡时点、来源和考勤结果。
# 3.1 表结构
CREATE TABLE `hrm_attendance_clock` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '打卡记录编号',
`employee_id` bigint DEFAULT NULL COMMENT '员工编号',
`clock_time` datetime NOT NULL COMMENT '实际打卡时间',
`attendance_time` datetime NOT NULL COMMENT '应打卡时间',
`type` tinyint NOT NULL COMMENT '打卡类型',
`source_type` tinyint DEFAULT 2 COMMENT '打卡来源',
`status` tinyint DEFAULT 0 COMMENT '打卡状态',
`stage` int DEFAULT 1 COMMENT '打卡阶段',
`address` varchar(255) DEFAULT NULL COMMENT '打卡地址',
`longitude` decimal(10,6) DEFAULT NULL COMMENT '经度',
`latitude` decimal(10,6) DEFAULT NULL COMMENT '纬度',
`ssid` varchar(50) DEFAULT NULL COMMENT 'WiFi 名称',
`mac` varchar(50) DEFAULT NULL COMMENT 'WiFi MAC 地址',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='HRM 打卡记录';
① employee_id 关联 hrm_employee 表的 id 字段;attendance_time 是班次的应打卡时点,也是判断迟到、早退和跨日归属的依据。
② 枚举 type 打卡类型(HrmAttendanceClockTypeEnum,字典 hrm_attendance_clock_type):1 上班打卡、2 下班打卡。
③ 枚举 source_type 打卡来源(HrmAttendanceClockSourceEnum,字典 hrm_attendance_clock_source):1 手机端、2 手工录入。手机端来自员工在 《移动端 HRM》 的打卡,address、经纬度和 WiFi 字段由打卡时的设备信息写入;手工录入由管理端补录。
④ 枚举 status 打卡状态(HrmAttendanceClockStatusEnum,字典 hrm_attendance_clock_status):0 正常、1 迟到、2 早退、3 缺卡。
⑤ stage 打卡阶段(HrmAttendanceClockStageEnum)当前仅支持第一段,即一天一次上下班打卡。
⑥ 管理端补录时,后端固定 source_type = 2、stage = 1,并根据 clock_time 与 attendance_time 计算 status,前端不能自行指定这些生命周期字段。只有手工录入的记录允许修改和删除,手机端来源的历史记录在管理端只读。
# 3.2 管理后台
对应 [HRM 人力资源 -> 考勤管理 -> 打卡记录] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/attendance/clock 目录。
# 打卡概况
AttendanceClockOverview.vue 以「员工 × 日期」矩阵展示月度打卡概况,点击日期单元格打开 AttendanceClockDailyDetail.vue,查看当天应打卡时点、实际打卡和考勤状态。
概况数据来自 HrmAttendanceStatisticsController,与月度汇总使用同一套实时统计口径。

# 打卡明细与导出
AttendanceClockRecordList.vue 查询打卡明细,可按员工、部门和打卡时间等条件筛选,并支持按当前条件导出 Excel。

# 补录
点击【新增】打开 AttendanceClockForm.vue。选择员工、打卡类型和日期后,表单会加载当天的应打卡时点及允许窗口;没有有效班次,或打卡时间不在允许窗口内时不能提交。

# 修改
只有手工录入的记录才显示【修改】。弹窗结构与补录相同,但员工不可更换;保存后月度统计按新记录实时重算。

# 删除
支持单条删除和批量删除,同样只针对手工录入的数据。删除后对应日期和月份的统计结果随之变化。
# 4. 月度考勤统计
月度统计,由 HrmAttendanceStatisticsController 提供接口(/hrm/attendance/statistics),不建立月度结果表。系统按月份加载员工、考勤组、节假日、打卡和审批通过的请假,实时计算出勤、迟到、早退、缺卡、旷工、请假和扣款,结果文案见 HrmAttendanceResultEnum。
# 4.1 管理后台
对应 [HRM 人力资源 -> 考勤管理 -> 月度汇总] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/attendance/month 目录。
# 月度汇总
可按月份、员工、工号、部门和是否全勤筛选,并支持导出 Excel。

# 员工月度详情
点击员工姓名进入 @/views/hrm/attendance/month/detail/index.vue,展示出勤概况和每日状态日历;点击具体日期后,查看当天的班次、打卡和请假明细。

实时计算
统计不是归档快照。修改考勤组、节假日、手工打卡或请假审批结果后,重新查询历史月份也可能得到不同结果。
因此薪资核算既支持直接同步考勤统计,也支持上传 Excel 固化当月考勤结果,详见 《【薪资】月度工资、工资条》。
# 5. 请假与 BPM 审批
请假记录,管理端由 HrmAttendanceLeaveController 提供查询接口(/hrm/attendance/leave),员工端申请由 HrmPortalAttendanceLeaveController 提供接口(/hrm/portal/attendance/leave),审批任务和流程记录复用 BPM。
# 5.1 表结构
CREATE TABLE `hrm_attendance_leave` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '请假记录编号',
`employee_id` bigint NOT NULL COMMENT '员工编号',
`type` varchar(64) DEFAULT NULL COMMENT '请假类型',
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
`day` decimal(10,2) DEFAULT NULL COMMENT '请假天数',
`reason` varchar(300) DEFAULT NULL COMMENT '请假理由',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`approval_status` tinyint NOT NULL COMMENT 'BPM 流程状态',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM 流程实例编号',
`approval_time` datetime DEFAULT NULL COMMENT '审批结束时间',
`approval_reason` varchar(500) DEFAULT NULL COMMENT '审批或取消原因',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_hrm_attendance_leave_process_instance_id` (`process_instance_id`)
) ENGINE=InnoDB COMMENT='HRM 请假记录';
① employee_id 关联 hrm_employee 表的 id 字段;type 请假类型对应字典 hrm_attendance_leave_type。
② 本表 id 同时作为 BPM 的 businessKey,流程定义 Key 固定为 hrm_attendance_leave;process_instance_id 唯一,保证一条请假只对应一个流程实例。
BPM 前置配置
请假对应的审批流程,需要先在 [工作流程 -> 流程管理 -> 流程模型] 菜单,配置并部署一个流程标识为 hrm_attendance_leave 的流程模型。
表单类型选择「业务表单」,表单提交路由填写 /hrm/portal/attendance/report,表单查看地址填写 /hrm/attendance/leave/AttendanceLeaveProcessDetail.vue。审批节点和处理人按企业的请假制度配置,保存后还要发布流程,员工端才能正常发起申请。
当前 HRM 代码不会自动创建该流程模型。未配置或未发布时,createLeave 无法创建 BPM 流程实例,也就不会回写 process_instance_id。如果不了解怎么配置,可以先阅读 《BPM 工作流》 文档。
③ approval_status 直接复用 BPM 流程状态(1 = 审批中,2 = 审批通过,3 = 审批不通过,4 = 已取消)。只有审批通过的时间段参与考勤统计。详见 §5.2 状态流转。
④ 发起前会拒绝与本人审批中或已通过申请重叠的时间。创建顺序为:写入 HRM 记录 → 发起 BPM 流程 → 回写流程实例编号。
# 5.2 状态流转
请假生命周期由 HrmAttendanceLeaveServiceImpl 与 BPM 共同控制,HRM 不自建状态机,直接复用 BPM 流程状态 BpmProcessInstanceStatusEnum:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
| 1 | RUNNING | 审批中 | 本人取消、审批通过或驳回 |
| 2 | APPROVE | 审批通过 | —(参与考勤统计) |
| 3 | REJECT | 审批不通过 | — |
| 4 | CANCEL | 已取消 | — |
状态流转说明
员工提交 ──→ 审批中(1) ──通过──→ 审批通过(2)(参与考勤统计)
│
├──不通过──→ 审批不通过(3)
│
└──本人取消──→ 已取消(4)
- 提交请假(
createLeave):写入请假记录并发起 BPM 流程,初始状态为审批中。 - 审批完成(HrmAttendanceLeaveStatusListener):只监听
hrm_attendance_leave流程,把终态和审批意见回写业务表。 - 取消申请(
cancelLeave):仅审批中可取消,BPM 流程与 HRM 记录同步变为已取消。
# 5.3 管理后台
对应 [HRM 人力资源 -> 考勤管理 -> 请假记录] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/attendance/leave 目录。管理后台只提供查询、流程详情和导出,不提供新增、修改或删除。
# 列表与导出
可按月份、员工、部门、请假类型和审批状态查询,并支持按当前条件导出 Excel。

# 查看审批进度
存在 process_instance_id 时,列表提供【审批进度】操作,进入 BPM 流程详情查看节点、处理人和意见。业务详情由 AttendanceLeaveProcessDetail.vue 渲染在流程页面中。
运行态说明
【审批进度】不是固定显示的页面入口。只有请假成功发起 BPM,并且当前记录存在 process_instance_id 时,操作列才显示该按钮;历史导入或人工迁移但没有流程实例的记录显示「-」。
如果当前租户还没有发布 hrm_attendance_leave 流程模型,请先完成 §5.1 表结构 中的 BPM 前置配置,再从员工端提交一条新申请。不要给历史记录手工填写一个不存在的流程实例编号。
# 5.4 员工端
对应 [HRM 人力资源 -> HRM 员工端 -> 考勤报表] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/hrm/portal/attendance 目录。
# 查看考勤
页面使用 AttendanceCalendar.vue 展示本人的月度汇总和每日打卡,数据来自 HrmPortalAttendanceStatisticsController 与 HrmPortalAttendanceClockController。

# 提交请假
点击【请假申请】打开 AttendanceLeaveForm.vue,填写请假类型、开始时间、结束时间、天数、理由和备注。表单校验结束时间晚于开始时间后发起 BPM 流程,成功后刷新考勤和申请列表。

# 查看与取消申请
AttendanceLeaveList.vue 查询本人申请,并可进入流程详情。只有「审批中」的申请显示取消按钮,填写取消原因并确认后,BPM 和 HRM 记录同步变为已取消。
在线打卡只在移动端
PC 端员工端只查看已有打卡,没有打卡按钮;在线打卡由 uni-app 移动端的 [HRM 人力资源 -> HRM 员工端 -> 打卡] 提供,考勤组配置的定位半径和 WiFi 在那里参与校验,打卡成功后写入 source_type = 1(手机端)的记录。详见 《移动端 HRM》。
申请列表操作
员工端的【我的请假申请】列表始终展示本人申请。存在 process_instance_id 时显示【审批进度】;只有 approval_status = 1(审批中)的申请显示【取消】,确认取消原因后同步终止 BPM 流程并把 HRM 记录更新为已取消。
审批通过、审批不通过和已取消的申请不再显示【取消】。如果当前没有审批中的本人申请,列表中自然不会出现该入口。