HR 请假管理
本页是一篇完整方案指南:以开源模板插件 hr-essentials(pluginId com.auraboot.template.hr-essentials,命名空间 thr)为例,从用户场景一路讲到开发实施和典型错误。它是 config 型插件——3 个 model、命令、页面、字典、菜单全部是 DSL JSON 声明,没有一行后端 Java;下面每个标识符你都能在 plugins/hr-essentials/config/ 里 grep 到。
这是一个 template(
catalogType: template):它演示生命周期状态机、父子引用和 agent-ready 命令,既能直接装上用,也能复制成自己的 HR 插件起点。它包含员工、考勤、请假三套对象;本页聚焦请假审批这条线。
1. 用户场景
一家公司用它管理员工档案、日常考勤和请假审批。请假这条线日常是:
- 员工提交请假申请,选假期类型(年假 / 病假 / 事假 / 产假 / 其他)、起止日期、天数,可填原因;
- 申请落到
pending(待审批)状态,等 HR / 管理者处理; - 审批人批准、拒绝或取消这条申请;
- HR 看一张汇总表,按员工和假期类型统计已批准的请假天数,掌握假期用量。
员工、HR 看到的视图和能做的操作不同:员工提交,HR 审批和统计。
2. 需求痛点
- 没有受控状态:Excel 里的「请假表」谁都能改成「已批准」,没有谁能从
pending流转到approved的约束,也没有审批轨迹。 - 裸改字段绕过规则:直接把状态列从待审改成已批,跳过了「只有 pending 的申请才能批准」这类前置条件,导致已取消的申请被重复批准。
- 统计靠手工:每个员工今年用了多少年假、多少病假,要人工汇总,容易错、不实时。
- 难以被自动化和 agent 调用:想让 AI 助手帮员工预填一条请假申请,或在每日摘要里列出待审批任务,表格做不到。
这些不是「再加一列」能解决的,而是受控状态变更 + 统一命令入口的问题。
3. 产品方案
在 AuraBoot 里,每一个请假动作都是一条命令,走统一的 命令管道:
- 「批准」按钮调用
thr:approve_leave,而不是裸写状态字段;命令在管道里统一鉴权 → 校验前置条件 → 状态流转 → 审计。状态机保证只有pending的申请才能流向approved/rejected/cancelled。 - 新建申请走
thr:create_leave_request,由命令自动生成请假编号(LV-{yyyyMMdd}-{seq})并把状态固定为pending,不需要前端传状态。 - 假期统计不是另一个 model,而是一条命名查询
thr_leave_summary_by_employee:按员工、按假期类型聚合已批准天数,供 HR 看板消费。 - 同一条命令既是 UI 按钮的目标,也带
agent_hint和cmd_risk_level,能被自动化规则或 AI agent 调用——所以 AI 助手能帮员工预填申请、能列出待审批任务,详见 Agent 就绪性。
4. 功能设计
4.1 数据模型(3 个)
插件 config/models.json 声明 3 个 model:
| Model | 用途 | 生命周期 |
|---|---|---|
thr_employee | 员工主档:姓名、部门、职位、入职日期、状态 | thr_em_status:active → on_leave(↔ active)→ resigned |
thr_attendance | 考勤记录:每日打卡、考勤分类(无状态机) | 分类字段 thr_at_type:normal / late / early_leave / absent |
thr_leave_request | 请假申请,承载审批工作流 | thr_lv_status:pending → approved / rejected / cancelled |
没有独立的「假期余额」model。余额/用量靠命名查询
thr_leave_summary_by_employee聚合thr_leave_request得出。
thr_leave_request 的字段(config/fields/thr_leave_request.json):
| 字段 | 类型(dataType) | 说明 |
|---|---|---|
thr_lv_code | string | 请假编号,创建时自动生成 LV-{yyyyMMdd}-{seq},只读 |
thr_lv_employee_id | reference | 引用 thr_employee(refModelCode),显示员工姓名 |
thr_lv_leave_type | enum(字典 thr_leave_type) | annual / sick / personal / maternity / other |
thr_lv_start_date | date | 开始日期,必填 |
thr_lv_end_date | date | 结束日期,必填 |
thr_lv_days | decimal | 天数,支持半天(0.5 / 1.5),必填 |
thr_lv_status | enum(字典 thr_leave_status) | pending / approved / rejected / cancelled,默认 pending |
thr_lv_reason | text | 请假原因,可选,最长 2000 字 |
字段类型用平台 dataType(string / reference / enum / date / decimal / text),引用字段把 refModelCode 放在 extension 里,枚举字段绑 dictCode。详见 Model 与 Field。
4.2 命令与状态机
命令命名是冒号 + 动词_名词:thr:<动词>_<名词>(例如 thr:approve_leave),不是点号(hr.leave.approve 跑不通)。thr_leave_request 一共 8 条命令(config/commands/thr_leave_request.json):
| 命令 | 形状 | 作用 |
|---|---|---|
thr:create_leave_request | create | 新建申请,自动编号 + 状态固定 pending |
thr:update_leave_request | update | 编辑申请(状态不可改,走状态命令) |
thr:approve_leave | state_transition pending → approved | 批准 |
thr:reject_leave | state_transition pending → rejected | 拒绝 |
thr:cancel_leave | state_transition pending → cancelled | 取消 |
thr:delete_leave_request | delete(前置:状态须 pending) | 删除 |
thr:detail_leave_request | query | 查看单条 |
thr:list_leave_requests | query | 分页/筛选列表 |
三条状态命令(批准 / 拒绝 / 取消)的 fromStates 都是 ["pending"]——也就是说只有待审批的申请能被处理;approved / rejected / cancelled 都是终态,不能再流转。删除也带前置条件,只有 pending 的申请能删。
每条命令都是一段声明。例如 thr:approve_leave 的真实定义:
{
"code": "thr:approve_leave",
"displayName:zh-CN": "批准请假",
"type": "state_transition",
"modelCode": "thr_leave_request",
"stateField": "thr_lv_status",
"fromStates": ["pending"],
"toState": "approved",
"permissions": ["thr.leave.manage"],
"agent_hint": "Approve a pending leave request. Transitions status from pending to approved. Only leave requests in pending status can be approved.",
"cmd_risk_level": "L2",
"precondition_description": "Leave request must be in pending status.",
"idempotent": false,
"reversible": false
}读出来的设计信息:这是一条 state_transition(pending → approved),只对 thr_lv_status 字段做流转,要求 thr.leave.manage 权限,风险级 L2,通过 agent_hint 告诉 agent 它能做什么、只有 pending 能批准。reversible: false 说明批准后不能由命令自身回退。
4.3 权限与角色
config/permissions.json 声明 6 个权限码(<模块>.<资源>.<动作>,每个资源 manage + read):
thr.employee.manage thr.employee.read
thr.attendance.manage thr.attendance.read
thr.leave.manage thr.leave.read
请假相关:写操作(新建 / 编辑 / 批准 / 拒绝 / 取消 / 删除)全部要 thr.leave.manage;读操作(查看 / 列表)要 thr.leave.read。每条命令的 permissions 字段就绑在这两个码上。
这个模板插件不内置角色——
plugin.json的resourceDirs里没有roles,config/下也没有roles.json。它只声明权限码;由谁来持有这些权限,在部署侧用平台的角色配置去组合(例如建一个「HR」角色绑thr.leave.manage,建一个「员工」角色只绑thr.leave.read)。权限模型本身详见 权限。
4.4 页面
config/pages/ 共 9 个页面:3 个 model 各有 list / form / detail。请假对应 thr_leave_request_list.json / thr_leave_request_form.json / thr_leave_request_detail.json。菜单(config/menus.json)在「人事管理」根节点下挂员工、考勤、请假三个入口,请假入口路由 /p/thr_leave_request、用 thr.leave.read 控制可见性。
5. 具体开发与实施
先掌握基础。本插件没有任何后端 Java,全靠平台的几个核心契约。动手前请先读:Model 与 Field · Command · 命令管道 · Permission · 插件清单 · 纯配置 Plugin · Page Designer。
落地一个像 hr-essentials 这样的 config 插件,步骤是:
- 写清单 ——
plugin.json声明pluginId/namespace(thr)/pluginType: config,并在resourceDirs注册 models / fields / commands / pages / dicts / permissions / menus / 等资源目录。详见 插件清单。 - 定义 model 与字段 ——
config/models.json+config/fields/,字段类型用平台dataType,枚举绑dictCode,引用字段在extension.refModelCode指目标 model。详见 Model 与 Field。 - 声明命令 —— 每个 model 一个
config/commands/<model>.json。状态流转用type: state_transition+stateField+fromStates/toState;新建用type: create+autoSetFields(本插件用它生成LV-{yyyyMMdd}-{seq}并固定初始状态pending);删除用type: delete+preconditions。每条命令在permissions绑权限码。详见 Command。 - 配 model-field binding ——
config/bindings/,并在resourceDirs.modelFieldBindings注册(见「典型错误」)。 - 设计页面 ——
config/pages/,每个 model 出 list / form / detail 三种页面 DSL,用 Page Designer。 - 字典、权限、菜单、命名查询 ——
config/dicts.json(假期类型 / 状态等枚举)/permissions.json/menus.json/named-queries.json(假期汇总聚合)。 - 打包导入 —— 用
auraCLI 的import-directory-sync(参数是目录 path)或平台导入接口;校验返回success:true才算导入成功。
6. 常见配置
- 新增假期类型:在
config/dicts.json的thr_leave_type字典里加一项(value/label/label:zh-CN/color),字段thr_lv_leave_type自动可选。不要在字段里硬编码枚举值。 - 半天假:
thr_lv_days是decimal,直接填 0.5 / 1.5,不需要额外配置。 - 假期统计/看板:命名查询
thr_leave_summary_by_employee按员工 × 假期类型聚合thr_lv_status = 'approved'的天数,支持year参数过滤;用它做 HR 看板或余额展示,而不是另建 model。 - 员工生命周期联动:员工自身也有状态机(
thr:on_leave/thr:return_from_leave/thr:resign),thr_em_status从active到on_leave再回active,或终态resigned;请假审批与员工状态是两条独立状态机,按业务需要用自动化规则联动。
7. 典型错误
- 命令码用点号:写成
hr.leave.approve或thr.leave.approve跑不通——真实是冒号 +动词_名词:thr:approve_leave。 - 前缀写成
hr_:这个插件命名空间是thr,model 是thr_leave_request、字段是thr_lv_status、权限是thr.leave.manage。写成hr_leave_request/hr_lv_status全部不存在。 - 以为有
draft状态:请假状态只有pending/approved/rejected/cancelled,默认pending,没有draft。新建即pending,直接进审批,没有「从草稿提交」这一步。 - 绕过命令直接改状态字段:直接 UPDATE
thr_lv_status会跳过状态机的fromStates守卫(只有pending能流转)和审计。一切状态变更都走命令,不要裸写数据表。 - bindingRules 写进 commands.json 内联:不会被导入;必须独立
bindingRules.json并在resourceDirs注册,否则报[S-EXT-HANDLER] references unregistered handler。详见 纯配置 Plugin。 - 命令执行 payload 结构:字段放在
{ "payload": { ... }, "operationType": ... },目标记录用targetRecordId(不是recordId)——放错位会出现「执行成功却字段为空」的迷惑性报错。