HR 请假管理

本页是一篇完整方案指南:以开源模板插件 hr-essentials(pluginId com.auraboot.template.hr-essentials,命名空间 thr)为例,从用户场景一路讲到开发实施和典型错误。它是 config 型插件——3 个 model、命令、页面、字典、菜单全部是 DSL JSON 声明,没有一行后端 Java;下面每个标识符你都能在 plugins/hr-essentials/config/ 里 grep 到。

源码:hr-essentials on GitHub ↗

这是一个 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_hintcmd_risk_level,能被自动化规则或 AI agent 调用——所以 AI 助手能帮员工预填申请、能列出待审批任务,详见 Agent 就绪性

4. 功能设计

4.1 数据模型(3 个)

插件 config/models.json 声明 3 个 model:

Model用途生命周期
thr_employee员工主档:姓名、部门、职位、入职日期、状态thr_em_status:activeon_leave(↔ active)→ resigned
thr_attendance考勤记录:每日打卡、考勤分类(无状态机)分类字段 thr_at_type:normal / late / early_leave / absent
thr_leave_request请假申请,承载审批工作流thr_lv_status:pendingapproved / rejected / cancelled

没有独立的「假期余额」model。余额/用量靠命名查询 thr_leave_summary_by_employee 聚合 thr_leave_request 得出。

thr_leave_request 的字段(config/fields/thr_leave_request.json):

字段类型(dataType)说明
thr_lv_codestring请假编号,创建时自动生成 LV-{yyyyMMdd}-{seq},只读
thr_lv_employee_idreference引用 thr_employee(refModelCode),显示员工姓名
thr_lv_leave_typeenum(字典 thr_leave_type)annual / sick / personal / maternity / other
thr_lv_start_datedate开始日期,必填
thr_lv_end_datedate结束日期,必填
thr_lv_daysdecimal天数,支持半天(0.5 / 1.5),必填
thr_lv_statusenum(字典 thr_leave_status)pending / approved / rejected / cancelled,默认 pending
thr_lv_reasontext请假原因,可选,最长 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_requestcreate新建申请,自动编号 + 状态固定 pending
thr:update_leave_requestupdate编辑申请(状态不可改,走状态命令)
thr:approve_leavestate_transition pending → approved批准
thr:reject_leavestate_transition pending → rejected拒绝
thr:cancel_leavestate_transition pending → cancelled取消
thr:delete_leave_requestdelete(前置:状态须 pending)删除
thr:detail_leave_requestquery查看单条
thr:list_leave_requestsquery分页/筛选列表

三条状态命令(批准 / 拒绝 / 取消)的 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.jsonresourceDirs 里没有 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 插件,步骤是:

  1. 写清单 —— plugin.json 声明 pluginId / namespace(thr)/ pluginType: config,并在 resourceDirs 注册 models / fields / commands / pages / dicts / permissions / menus / 等资源目录。详见 插件清单
  2. 定义 model 与字段 —— config/models.json + config/fields/,字段类型用平台 dataType,枚举绑 dictCode,引用字段在 extension.refModelCode 指目标 model。详见 Model 与 Field
  3. 声明命令 —— 每个 model 一个 config/commands/<model>.json。状态流转用 type: state_transition + stateField + fromStates / toState;新建用 type: create + autoSetFields(本插件用它生成 LV-{yyyyMMdd}-{seq} 并固定初始状态 pending);删除用 type: delete + preconditions。每条命令在 permissions 绑权限码。详见 Command
  4. 配 model-field binding —— config/bindings/,并在 resourceDirs.modelFieldBindings 注册(见「典型错误」)。
  5. 设计页面 —— config/pages/,每个 model 出 list / form / detail 三种页面 DSL,用 Page Designer
  6. 字典、权限、菜单、命名查询 —— config/dicts.json(假期类型 / 状态等枚举)/ permissions.json / menus.json / named-queries.json(假期汇总聚合)。
  7. 打包导入 —— 用 aura CLI 的 import-directory-sync(参数是目录 path)或平台导入接口;校验返回 success:true 才算导入成功。

6. 常见配置

  • 新增假期类型:在 config/dicts.jsonthr_leave_type 字典里加一项(value / label / label:zh-CN / color),字段 thr_lv_leave_type 自动可选。不要在字段里硬编码枚举值。
  • 半天假:thr_lv_daysdecimal,直接填 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_statusactiveon_leave 再回 active,或终态 resigned;请假审批与员工状态是两条独立状态机,按业务需要用自动化规则联动。

7. 典型错误

  • 命令码用点号:写成 hr.leave.approvethr.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)——放错位会出现「执行成功却字段为空」的迷惑性报错。

下一步

  • 系统总览 —— 插件、命令与运行时如何拼到一起
  • 命令管道 —— 上面每条命令都走的执行契约
  • 权限 —— 上面权限码背后的五层模型
  • 插件清单 —— plugin.jsonresourceDirs 如何声明资源
  • Agent 就绪性 —— 如何为安全执行设计 agent_hintcmd_risk_level