拓冰建站拓冰建站
首页 / 资讯中心 / 正文

SpringBoot3+Vue3 企业算薪引擎:SalaryCalcContext 驱动规则、公式与数据源解析

SpringBoot3Vue3 企业算薪引擎SalaryCalcContext 驱动规则、公式与数据源解析文档地址https://ruoyioffice.com源码1·GitHubhttps://github.com/yuqing2026/ruoyi-office源码2·GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office源码3·Giteehttps://gitee.com/yqzy1688/ruoyi-office微信17156169080备注「RuoYi Office」一句话算薪引擎要验收的不是「能不能加出总数」而是每一次试算能不能说清楚每个数字从哪来。RuoYi Office 把档案、岗位、绩效、考勤先装进SalaryCalcContext规则只认三种calcType项目金额按编码回填整份上下文打成 JSON 快照。▲ 先装上下文再算应发考勤按应出勤日入账社保个税夹紧基数最后按方案项目回填并打异常标签引言配置页配完了真正难的是「算这一跳」薪酬模块很容易被拆成三堆菜单方案怎么配、个税怎么累计、工资条怎么发。这三件事都重要但现场真正炸掉的往往是中间那一跳——点「试算」之后引擎按什么顺序取数、公式引用哪个键、缺勤天数从哪来、算完的数怎么对回项目编码。现场原话引擎必须回答「基本工资到底取档案还是取岗位」规则dataSourceConfig是哪个键「绩效没出结果工资还发不发」正式结果 → 岗位绩效基数 → 规则固定值哪一层兜底「这个月没打卡为什么应发被扣光」有没有考勤规则没有规则则扣款为 0「公式里写了『基本工资*0.1』为什么是 0」上下文里这个键当时有没有已经 put 进去方案工作台、累计预扣个税、发薪发条这三件事已有专文拆过。本文只写试算引擎本身上下文、三种计算方式、考勤入账、项目回填与快照。技术栈锚点2026Spring Boot 3.5 Java 17PC 为 Vue3 方案工作台配置规则试算入口在薪资批次。一、SalaryCalcContext 是什么结论SalaryCalcContext是一次员工试算的只读工作台规则、公式、取数全部问它不允许再去翻 Mapper。它不是一个独立 Spring Bean而是SalaryBatchServiceImpl里的内部类。构造时一次性塞进成员来源试算时干什么batch薪资批次提供期间年、月archive/version员工薪资档案及生效版本固定薪、补贴、社保个税城市employee员工档案部门、是否已生成系统用户positionRate岗位薪资规则最优匹配岗位工资、绩效基数performanceResult已发布且期间相交的绩效结果金额 / 分数 / 系数attendanceSummary打卡记录 部门考勤配置应出勤、缺勤、迟到早退缺卡amounts引擎自己边算边 put公式和系统取数都从这张表读键名是中英双语都写一份的。basicSalary和「基本工资」指向同一个数这样公式既可以写basicSalary*0.1也可以写基本工资*0.1。privateclassSalaryCalcContext{privatefinalMapString,BigDecimalamountsnewHashMap();privateSalaryCalcContext(...,AttendanceSummaryattendanceSummary){putAmount(periodYear,newBigDecimal(batch.getPeriodYear()));putAmount(periodMonth,newBigDecimal(batch.getPeriodMonth()));putAmount(performance.result_score,scoreOrZero(performanceResult));putAmount(performance.result_coefficient,coefOrZero(performanceResult));putAmount(performance.result_amount,amountOrZero(performanceResult));refreshAttendanceAmounts();}privatevoidrefreshAttendanceAmounts(){putAmount(attendance.work_days,attendanceSummary.workDays);putAmount(attendance.absent_days,attendanceSummary.absentDays);putAmount(attendance.absence_deduction,attendanceSummary.absenceDeduct);// 迟到 / 早退 / 缺卡次数同样 put 进表供公式引用}}顺序约束应发四项基本、岗位、绩效、补贴必须先算完并putAmount考勤扣款才能用「日薪 应发合计 / 应出勤日」。考勤汇总刷新过一次之后社保个人、公积金、个税再 put。公式如果提前引用还没 put 的键得到的是0不是报错——这是设计取舍试算不能因为一条自定义公式写错就把整批人打挂。二、三种 calcType固定值、公式、系统取数结论计薪规则只有三种算法。系统取数只认单个键名加减乘除必须改走公式。前端工作台把这三种算法展示给 HR▲ 深圳研发中心月薪方案配置完整度六项打勾「基本工资」计算方式是系统取数来源说明指向薪资档案固定薪资。高级配置里才改公式或条件对应后端SalaryRuleDO.calcTypecalcType页面文案求值1固定值rule.fixedValue2公式计算中缀转后缀再按上下文取标识符3系统取数dataSourceConfig单个键条件表达式先于求值conditionExpression不满足该项直接0不再跑公式。上下限、小数位、舍入模式在算出裸值之后再夹。privateBigDecimalcalculateByRule(SalaryRuleDOrule,SalaryCalcContextcontext,BigDecimalfallback){if(rulenull){returnscale(fallback);}if(!matchesCondition(rule.getConditionExpression(),context)){returnBigDecimal.ZERO;}BigDecimalvalue;if(Objects.equals(rule.getCalcType(),1)){valuedefaultValue(rule.getFixedValue());}elseif(Objects.equals(rule.getCalcType(),2)){valueevaluateFormula(rule.getFormulaExpression(),context);}elseif(Objects.equals(rule.getCalcType(),3)){valueresolveDataSource(rule.getDataSourceConfig(),context,fallback);}else{valueresolveDataSource(rule.getDataSourceConfig(),context,fallback);}returnapplyRuleBoundsAndRound(value,rule);}前端有一句硬提示系统取数不能写archive.fixed_salaryarchive.allowance_amount。键名解析是精确匹配字符串加号会被当成键名的一部分永远 miss结果落到 fallback。要「固定薪 补贴 − 缺勤」把calcType改成 2公式写成archive.fixed_salaryarchive.allowance_amount-attendance.absence_deduction。三、系统取数白名单键而不是随意拼 SQL结论系统取数是一张白名单。引擎不会按字符串去反射档案对象更不会拼 SQL。resolveDataSource认这些键与工作台下拉SALARY_DATA_SOURCE_OPTIONS对齐键含义miss 时archive.fixed_salary档案固定薪兼看转正薪、试用薪0archive.allowance_amount档案固定补贴0position.post_salary岗位匹配结果岗位未命中则 0performance.base正式绩效金额否则岗位绩效基数0performance.result_amount/result_score/result_coefficient已发布绩效无结果则 0attendance.absence_deduction已按考勤规则算出的缺勤扣款刷新前是 0认不出的字符串退回context.amount(source)也就是中间金额表。所以自定义项目可以把键写成基本工资或payableAmount前提是引擎已经 put 过。开发库里现有规则绝大多数是calcType3。公式和固定值是给「这个部门有特殊津贴」留的口子不是默认路径。四、公式中缀转后缀标识符允许汉字结论公式引擎是手写的调度场算法不是 Groovy、不是 Aviator。标识符允许字母、数字、下划线、点号和汉字。这是为了让 HR 能写基本工资*0.08而不必先学英文变量。全角括号会先替换成半角。除数为 0 时结果是 0不抛异常。比较运算符挂在条件表达式上、、、!、、左右两边各自再走一遍公式求值。没有运算符时公式结果非 0 视为条件成立。privateBigDecimalparseFormulaValue(Stringtoken,SalaryCalcContextcontext){try{returnnewBigDecimal(token);}catch(NumberFormatExceptionignored){returncontext.amount(token);}}privateBigDecimalcalculate(BigDecimalleft,BigDecimalright,charoperator){returnswitch(operator){case-left.add(right);case--left.subtract(right);case*-left.multiply(right);case/-right.compareTo(BigDecimal.ZERO)0?BigDecimal.ZERO:left.divide(right,8,RoundingMode.HALF_UP);default-BigDecimal.ZERO;};}边界要讲清楚没有函数、没有IF()、没有日期。复杂分支用conditionExpression拆成多条规则而不是在一条公式里写脚本。这和「把 Excel 整张表贴进系统」不是一条路。五、考勤入账先数应出勤日再谈扣款结论缺勤天数不是「日历 30 天 − 打卡天数」是考勤规则引擎逐日判定的应出勤日减去有进或出记录的工作日。buildAttendanceSummary做了三件容易被忽略的事按员工部门取考勤配置沿部门树向上继承跟打卡页是同一套getAttendanceConfigByDeptId。用isWorkDay逐日数应出勤接入大小周、节假日、夏冬令时休息日加班打卡不计入出勤天数。员工还没生成系统用户汇总标记availablefalse后面扣款直接为 0并打异常标签而不是按全月缺勤把应发扣光。扣款公式在AttendanceSummary.calculateDeduct。方案如果没配考勤规则扣款为 0——这是有意的避免「规则没配等于按默认比例扣光」。▲ 深圳研发考勤扣款规则缺勤按日薪比例 1 扣迟到/早退每次 20缺卡每次 50。这些数进入试算上下文的attendance.absence_deductionprivatevoidcalculateDeduct(BigDecimalpayableBase,SalaryAttendanceRuleDOattendanceRule){if(!available||attendanceRulenull){absenceDeductBigDecimal.ZERO;return;}BigDecimaldailySalaryworkDays.compareTo(BigDecimal.ZERO)0?BigDecimal.ZERO:payableBase.divide(workDays,8,RoundingMode.HALF_UP);BigDecimalabsentDeductObjects.equals(attendanceRule.getAbsenceDeductType(),2)?absenceDayAmount.multiply(absentDays):dailySalary.multiply(absentDays).multiply(absenceDayRatio);BigDecimallateDeductnewBigDecimal(lateCount).multiply(lateAmount);BigDecimalearlyLeaveDeductnewBigDecimal(earlyLeaveCount).multiply(earlyAmount);BigDecimalmissingDeductnewBigDecimal(missingPunchCount).multiply(missingAmount);absenceDeductscale(absentDeduct.add(lateDeduct).add(earlyLeaveDeduct).add(missingDeduct));}缺勤类型 1 是「日薪 × 缺勤天数 × 比例」类型 2 是「固定额 × 缺勤天数」。迟到早退缺卡是按次加。全部加完才putAmount(attendance.absence_deduction)缺勤项目用系统取数去读这个键。六、按方案项目回填并留下快照结论引擎内部先算「应发合计 / 应扣合计 / 实发 / 人工成本」四个汇总再按方案勾选的每一个薪资项目把金额填回去。工资条上看到的每一行都能对上itemCode。resolveItemAmount用编码关键字认标准项BASIC、POST、PERFORMANCE、ALLOW、ATTENDANCE、SOCIAL_PERSONAL、FUND_PERSONAL、TAX、NET、SOCIAL_COMPANY、FUND_COMPANY、LABOR_COST。认不出的自定义项再走一遍calculateByRule。每个员工一行结果写入hrm_salary_batch_employee分项写入hrm_salary_batch_employee_item整份上下文打进calc_snapshot_json批次号、档案版本、岗位匹配、绩效结果、考勤汇总、异常标签、个税累计口径。▲ 试算结果弹窗写明绩效工资的取值优先级黄标签是配置提示红标签是实发为负、缺方案、缺社保规则、员工公司与薪酬主体不一致这类硬异常社保基数按基本工资夹在上下限之间个税走累计预扣专文已写这里只强调引擎把taxAmountput 进上下文后个税项目按编码取走。非居民规则会打「未支持累计预扣」标签税额按 0 处理不假装算过。七、异常标签能继续算但必须看见结论试算默认不中断整批。缺配置用标签标出来HR 决定是补数据再跑还是带着提示送确认审批。标签含义期间无生效版本档案版本生效日晚于本期结束未匹配岗位工资规则方案勾了岗位项但岗位规则没命中绩效模块未接入期间没有已发布结果用了岗位基数或规则兜底无考勤数据缺勤扣款按全月缺勤测算有用户、有应出勤日、打卡行为 0缺少社保公积金规则 / 缺少个税规则城市规则 miss累计期初缺失2 月及以后首次跑历史台账为空实发为负数扣项大于应发硬异常硬异常在弹窗里用红色标。配置提示用黄色允许继续。这和「试算失败就整批回滚」不同企业发薪经常是 90% 的人没问题剩下的人缺一张社保城市HR 需要看见名单而不是整批重来。八、数据结构试算相关表角色hrm_salary_plan/hrm_salary_plan_item方案及勾选的薪资项目hrm_salary_rulecalcType、公式、取数键、条件、上下限hrm_salary_attendance_rule缺勤类型、迟到早退缺卡金额hrm_salary_social_fund_rule/hrm_salary_tax_rule按城市匹配hrm_salary_batch期间批次确认审批后禁止重试算hrm_salary_batch_employee人级应发应扣实发 calc_snapshot_jsonhrm_salary_batch_employee_item项目级金额、来源类型、备注设计要点快照是试算当时的事实发薪后档案再改不影响已确认批次来源备注会写成「来自打卡汇总缺勤 x 天迟到 y 次」对账时不用再反推。九、技术亮点设计要点实现价值一次装上下文内部类SalaryCalcContext规则不再各自查库三种 calcType固定值 / 公式 / 系统取数HR 能配工程师能审计取数白名单精确匹配键名禁止在取数框里写加减中文公式标识符含汉字基本工资*0.1可跑考勤按应出勤日复用打卡规则引擎大小周不会算错缺勤无规则不扣款attendanceRule null则 0避免无配置扣光应发绩效三层兜底正式结果 → 岗位基数 → 规则绩效晚出也能先发工资快照可对账JSON 含分项与异常工资条每一行有出处标签不中断整批黄提示 / 红硬异常少数人缺配置不影响多数人十、快速体验在线演示https://ruoyioffice.com/web/账号 admin / admin123建议按这条路径看引擎而不是只看方案列表人力 → 薪酬管理 → 薪酬方案打开「深圳研发中心月薪方案」工作台。在「项目与计薪」看BASIC_SALARY的计算方式是否为系统取数。点某一行「高级」看calcType、取数键、条件表达式。切到「考勤」看缺勤比例和迟到早退缺卡金额。打开薪资批次对已有批次点「查看结果」看绩效来源和异常色。若批次尚未确认可再点「试算」确认审批后会被后端拒绝重算。源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-office GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office Giteehttps://gitee.com/yqzy1688/ruoyi-office常见问题FAQ算薪引擎和薪酬方案工作台是一回事吗不是。工作台负责把项目、岗位、社保、个税、考勤配齐并生成默认规则引擎负责批次试算时按SalaryCalcContext求值。工作台配错引擎会算出带标签的结果而不是在配置页假装已经发薪。系统取数能不能写「固定薪加补贴」不能。系统取数只匹配单个键例如archive.fixed_salary。加减乘除把calcType改成公式计算。员工这个月完全没打卡会不会把应发扣成 0分两层。还没生成系统用户考勤availablefalse扣款为 0打标签。已经有用户但打卡记录为 0按应出勤日计全月缺勤再乘方案里的缺勤比例。方案若没配考勤规则扣款仍是 0。公式支持 Excel 那种 IF 吗不支持函数。分支用规则上的条件表达式拆开。标识符可以写中文键名四则运算和括号可以IF/SUM/VLOOKUP不行。试算过的批次还能改方案再算一遍吗发起确认审批、已确认、已发薪或已发布工资条后后端拒绝重试算。要改口径走新批次不要改已经送审的快照。结语企业算薪引擎的核心不是「把四则运算写进 Java」而是给每一次试算一个封闭的上下文档案版本、岗位匹配、绩效结果、考勤汇总进同一个SalaryCalcContext规则只暴露固定值、公式、系统取数三种算法项目按编码把金额领走算不清的用标签说出来而不是静默填 0 或整批失败。方案怎么配、个税怎么累计、工资条怎么发可以各自成文。中间这一跳如果讲不清前面配得再漂亮发下去的数也经不起对账。你们团队现在的算薪是脚本拍脑袋还是每次试算都能打开快照看每个项目的来源欢迎在评论里说说卡在哪一层。想要体验 RuoYi Office 的强大功能在线演示https://ruoyioffice.com/web/账号 admin / admin123源码仓库GitHubhttps://github.com/yuqing2026/ruoyi-office GitCodehttps://gitcode.com/zhouzhongyan/ruoyi-office Giteehttps://gitee.com/yqzy1688/ruoyi-office技术咨询添加微信17156169080备注「RuoYi Office」⭐如果觉得不错请给个 Star 支持一下
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门