尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

SpringBoot3+Vue3 人力假期余额设计:预占、扣减、回滚与重复回调怎么保证一致

发布时间:2026/9/26 12:12:52

资讯中心
01
ARTICLE

SpringBoot3+Vue3 人力假期余额设计:预占、扣减、回滚与重复回调怎么保证一致

SpringBoot3+Vue3 人力假期余额设计:预占、扣减、回滚与重复回调怎么保证一致
SpringBoot3Vue3 人力假期余额设计预占、扣减、回滚与重复回调怎么保证一致文档地址https://ruoyioffice.com 文章底部获取源码和演示地址 17156169080获取产品咨询员工看到“年假还剩 6 天”背后其实不是一道减法。审批中的 3 天算不算可用流程拒绝要不要自动退请了 5 天只休 3 天怎么返还同一条 Flowable 回调执行两次会不会再扣一次真正可靠的假期余额需要按账户系统来设计。▲ 规则负责算额度账户保存当前值流水解释每次变化请假单只通过余额服务改变账户引言一个 remainingDays 字段为什么撑不住真实请假最初版假勤系统往往只有员工、假种和剩余天数employee_id | leave_type | remaining_days它能支持“提交后减 1 天”却回答不了下面的问题两张审批中的请假单能否同时占用同一份余额审批拒绝、员工撤回、管理员取消是不是同一种退回流程通过后预占怎样变成正式使用销假实际天数变少差额回到哪一个年度账户工龄调整导致年假额度变化为什么从 10 天变成 12 天消息重试让回调重复执行时怎样避免重复扣减。所以假期余额的正确抽象是一套带业务来源、状态迁移和审计流水的额度账户。一、员工余额由三套配置合成余额不从账户表凭空产生。系统先合成假种、部门方案和员工参数再计算某员工某年度应得额度。1. 假种决定是否管理余额年假、调休假、福利假通常需要余额事假、婚假等可能只校验条件不做账户扣减。假种层应明确是否启用余额、默认额度、最小请假单位、是否允许透支和有效期。2. 部门方案承载企业差异总部、制造基地和项目团队可能适用不同方案。系统按员工部门查找生效方案必要时向上继承而不是在请假表单里写死规则。3. 员工参数处理个体差异员工入职日期、工龄起算日和奖励年假会改变最终额度。规则更新后不应直接覆盖旧数字而应生成一条调整流水。▲ HR 配的是规则员工拿到的是规则在某个年度计算后的账户结果核心边界是规则是计算依据账户是计算结果。历史账户不能随着规则页面每次打开而漂移否则系统无法解释余额变化。二、账户看当前流水看原因年度账户建议至少保留五个口径口径含义什么时候变化发放额度本年度累计获得年度发放、人工调整、工龄重算可用余额还能发起多少预占时减少释放或退回时增加预占余额已提交、尚未最终通过提交增加通过或拒绝减少已用额度已确认消耗审批通过时增加销假时减少结转额度来自上一周期年度初始化或结转任务TableName(hrm_leave_account)publicclassLeaveAccountDOextendsTenantBaseDO{TableIdprivateLongid;privateLongemployeeId;privateIntegerleaveType;privateIntegergrantYear;privateBigDecimalgrantedDays;privateBigDecimalusedDays;privateBigDecimalreservedDays;privateBigDecimalavailableDays;privateBigDecimalcarryForwardDays;privateLocalDateexpireDate;}账户只回答“现在是多少”。每一次变化还要写入hrm_leave_ledger记录变动类型、前后余额、业务类型、业务 ID 和备注。这样 HR 可以从“剩 6 天”继续追到“年度发放 10 天、请假确认 3 天、审批中预占 1 天”。三、提交时先预占不要等通过后才扣员工有 5 天年假同时提交两张各 4 天的申请。如果系统等审批通过才扣余额两张单都能通过提交校验最后必然透支。更稳妥的状态迁移是提交available - daysreserved days 通过reserved - daysused days 拒绝/撤回reserved - daysavailable days 销假used - returnedDaysavailable returnedDays▲ 预占解决并发申请透支确认、释放和销假退回分别对应不同业务结果预占动作必须和请假单进入审批状态处于同一事务边界。后端不能只相信前端展示的余额而要重新读取账户并完成原子扣减Transactional(rollbackForException.class)publicvoidreserveForBill(LongbillId,LongemployeeId,IntegerleaveType,BigDecimaldays){LeaveAccountDOaccountgetOrCreateAccount(employeeId,leaveType);if(account.getAvailableDays().compareTo(days)0){throwexception(LEAVE_BALANCE_NOT_ENOUGH);}BigDecimalbeforeaccount.getAvailableDays();account.setAvailableDays(before.subtract(days));account.setReservedDays(account.getReservedDays().add(days));leaveAccountMapper.updateById(account);createLedger(account,RESERVE,days.negate(),before,account.getAvailableDays(),billId,HRM_LEAVE_CANCEL_BILL);}高并发场景还应增加乐观锁版本号或带余额条件的原子更新让“余额足够”和“完成扣减”成为不可分割的数据库动作。四、审批回调只做状态迁移而且必须幂等Flowable 审批通过后不应再次从可用余额扣减而是把预占转成已用Transactional(rollbackForException.class)publicvoidconfirmForBill(LongbillId){if(leaveLedgerMapper.existsByBizAndType(HRM_LEAVE_CANCEL_BILL,billId,CONFIRM_USE)){return;}LeaveBillDObillvalidateBillExists(billId);LeaveAccountDOaccountgetAccount(bill);BigDecimaldaysbill.getExpectedDays();account.setReservedDays(account.getReservedDays().subtract(days));account.setUsedDays(account.getUsedDays().add(days));leaveAccountMapper.updateById(account);createLedger(account,CONFIRM_USE,BigDecimal.ZERO,account.getAvailableDays(),account.getAvailableDays(),billId,HRM_LEAVE_CANCEL_BILL);}确认使用时可用余额变化为 0因为额度在提交时已经预占。确认动作改变的是reservedDays和usedDays的归属。幂等不能只靠“当前流程状态已经通过”。更可靠的做法是用business_type business_id change_type定义业务幂等键数据库增加唯一约束或在落流水前检查账户更新与流水写入同事务重复调用直接返回已有结果异常补偿只追加新流水不修改历史流水。五、拒绝、撤回与取消都释放预占但保留不同原因从余额数学上看审批拒绝和员工撤回都执行reserved - days available days但审计含义不同。流水变动类型可以统一为RELEASE_RESERVE备注和业务状态要保留“审批拒绝”“发起人撤回”“管理员取消”的真实原因。释放时还要防止reservedDays变成负数。如果旧数据没有成功预占系统应先核对流水再决定拒绝补偿还是记录告警不能盲目加回余额。六、销假不是撤销原单而是按实际使用退差额员工原申请 5 天实际休 3 天销假通过后应退回 2 天原请假单仍保留“批准 5 天”的审批事实销假单记录实际使用 3 天年度账户usedDays减 2availableDays加 2新增CANCEL_RETURN流水并关联销假业务。▲ 表单在提交前展示余额审批后的实际天数由销假动作修正不回头篡改历史审批事实BigDecimalreturnedDaysbill.getExpectedDays().subtract(bill.getActualDays());if(returnedDays.signum()0){return;}account.setUsedDays(account.getUsedDays().subtract(returnedDays));account.setAvailableDays(account.getAvailableDays().add(returnedDays));如果实际天数大于预计天数不能静默把余额扣成负数应走补充申请、变更流程或明确的透支规则。七、前端负责解释余额不负责决定余额Vue3 表单在员工选择假种和预计天数后调用摘要接口展示本年度发放、已使用、审批中预占、当前可用、本次申请和提交后预计剩余。这能把“余额不足”从提交后的报错提前变成员工填表过程中的反馈。但前端数值只用于体验最终校验仍由后端事务完成。▲ PC 与移动端共享同一余额摘要接口避免两端各算一套口径▲ 移动端负责快速发起与查看状态余额变动仍统一由后端服务和流程回调驱动八、年度发放、工龄重算和到期清零都要走流水自动任务最容易被写成直接update available_days 0这会让系统失去解释能力。正确做法是把定时任务也当作业务来源年度发放GRANT工龄或奖励变化ADJUST_ADD/ADJUST_SUB上年度结转CARRY_FORWARD到期清零EXPIRE_CLEAR。任务应按员工、假种、年度建立幂等键。重复跑一次年度发放任务不能再发一份年假清零任务失败重试也不能生成多条相同清零流水。九、这套模型的边界在哪里假期余额账户解决的是额度一致性不会自动解决所有假勤问题工作日和请假时长仍要读取班次、节假日方案跨年度请假需要按日期拆分到不同年度账户调休额度可能来自加班审批不一定由年度任务发放小时假与天数假要统一换算精度多租户环境下账户、流水和规则都必须保留租户边界。余额服务应保持单一职责接收已经算好的变动额度执行可靠的账户迁移与流水留痕日历、班次和劳动政策由上游规则服务负责。十、上线前建议验证的 10 个场景余额 5 天同时提交两张 4 天申请第二张必须失败提交 3 天后可用减少 3 天、预占增加 3 天审批通过后可用不再减少预占转为已用同一通过回调执行两次账户只变化一次审批拒绝后预占释放且生成原因清晰的流水发起人撤回后与拒绝使用相同数学动作但不同备注请 5 天、销假实际 3 天退回 2 天工龄重算额度增加 2 天保留调整前后余额到期清零任务重复运行不重复清零或重复写流水PC 与 App 查询到的可用、预占、已用口径一致。结语企业假期余额的难点不在字段多而在业务动作多、审批时间长、回调可能重复、员工权益必须能解释。一套可靠设计可以归纳为四句话规则算额度账户存当前流水讲原因流程做迁移。再补上事务、并发控制和业务幂等假期余额才不会在流程重试、销假和年度任务中越算越乱。这套账户模型也可以复用到调休、福利额度、培训学时、补贴余额和会员积分等场景。区别只在发放规则可靠性骨架是相通的。如果这篇对你有用点个「在看」或收藏。演示地址https://ruoyioffice.com/webGitHub 源码https://github.com/yuqing2026/ruoyi-officeGitee 源码https://gitee.com/yqzy1688/ruoyi-office微信17156169080获取产品咨询打开演示地址直接查看系统。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。