案例 6:绩效等级与奖金系数
本案例对应 JQuick-Java 仓库 USE-CASES.md 的场景 6:绩效等级与奖金系数,属于人力资源领域,解决企业考核周期内「四维加权评分 → S/A/B/C/D 等级映射 → 奖金系数修正 → 奖金金额/晋升资格/PIP 判定」的一体化决策问题。
在年度或季度绩效考核周期中,HR 需要先汇总员工的四类考核得分:KPI 得分、项目得分、团队协作得分与考勤得分(均为 0-100 分),按 KPI 50%、项目 20%、协作 15%、考勤 15% 的固定权重加权出综合分。
综合分映射为 S/A/B/C/D 五档绩效等级,每档对应一个基准奖金系数:S 为 1.8、A 为 1.4、B 为 1.1、C 为 0.8、D 为 0.5。在基准系数之上,还要叠加三类调节:管理者职责加成 +0.2、销售激励(S/A 级且年度销售额达到 50 万元加 0.2,销售额达到 20 万元加 0.1)、违规扣减 -0.3,最终系数乘以月度基本工资得到奖金金额。
同一条决策链路上还需要并行产出:晋升资格(总分不低于 85 且无违规记录)、是否强制进入 PIP 绩效改进计划、考勤档位(按考勤得分 95/90/80 分为 3/2/1/0 档),以及面向员工的评语清单(评优入池、违规扣减说明、考勤预警、销售激励说明等)。
这些输出物直接服务于奖金发放、年度晋升池筛选与绩效改进计划跟进。对应场景 6 的 6 组真实员工形态:优秀销售(综合 92.65、S 级、系数 2.0)、低绩效且违规(51.25、D 级、系数 0.2)、管理者 A 级(系数 1.7)、中等 B 级(系数 1.2)、不达标 D 级(系数 0.5)、优秀但违规被取消晋升。
- 考核权重 50%/20%/15%/15%、S/A/B/C/D 分数线、1.8/1.4/1.1/0.8/0.5 基准系数散落在薪资模块、Excel 模板与硬编码中,年度政策一调整就要协调多处重新发版。
- 管理者加成、销售激励(50 万/20 万两档)、违规扣减三类调节相互缠绕在同一段
if-else里,改一个档位容易漏改联动逻辑。 - 晋升资格「总分 85 且无违规」与 PIP 入口和奖金核算耦合在业务代码中,HR 部门无法独立调参与试算。
- 系数叠加理论上可能出现负值,各系统缺少统一的下限保护;金额小数位口径(保留两位、四舍五入)也不一致。
- 决策结果往往只给一个奖金数字,员工质疑时难以回溯综合分、等级、各项加成与扣减的完整依据。
- 不少 eHR 与薪资系统仍运行在 JDK 8 或国产化信创环境,难以引入 Drools 等重型规则平台。
本案例采用标准「六件套」:入参模型与决策结果模型承载数据,规则接口声明口径,XML 脚本外置全部阈值与系数,Service 负责汇总聚合与下限保护,JUnit 以 6 组员工形态回归全部决策路径。
| 层次 | 职责 | 对应文件 |
|---|---|---|
| 领域模型 | 绩效考核快照、奖金决策结果 | PerformanceRecord.java、BonusDecision.java |
| 规则集与 XML | 加权、等级、系数、三项调节、晋升与 PIP 共 10 个规则方法 | PerformanceRules.java、performance.xml |
| 编排服务 | 系数聚合与下限保护、金额保留两位小数、晋升线复核、评语组装 | PerformanceService.java |
| 测试 | 6 组员工形态(优秀/低绩效违规/管理者/中等/不达标/优秀违规) | PerformanceServiceTest.java |
规则方法拆分
Section titled “规则方法拆分”| 规则方法 | 口径 | 分值/输出 |
|---|---|---|
weightedScore |
KPI×50% + 项目×20% + 协作×15% + 考勤×15% | 0-100 综合分 |
grade |
≥90 S / ≥80 A / ≥70 B / ≥60 C / 否则 D | S/A/B/C/D |
baseCoefficient |
S 1.8 / A 1.4 / B 1.1 / C 0.8 / D 0.5 | 基准系数 |
managerBonus |
管理者 +0.2,否则 0 | +0.2 / 0 |
salesBonus |
S/A 且销售额 ≥50 万 +0.2;销售额 ≥20 万 +0.1;否则 0 | +0.2 / +0.1 / 0 |
violationPenalty |
违规 -0.3,否则 0 | -0.3 / 0 |
promotionEligible |
总分 ≥85 且无违规才具备资格(违规前置一票否决) | true / false |
needPip |
总分低于 60,或低于 70 且有违规 → 进入 PIP | true / false |
attendanceLevel |
考勤 ≥95 为 3 / ≥90 为 2 / ≥80 为 1 / 否则 0 | 0-3 档 |
bonusAmount |
月度基本工资 × 最终奖金系数 | 奖金金额(元) |
代码实现(完整可运行)
Section titled “代码实现(完整可运行)”将六个文件按仓库原有包结构放入 Maven 工程:两个模型放入 src/test/java/com/github/paohaijiao/demo/model/,规则接口、编排服务与测试分别放入 com.github.paohaijiao.demo 与 com.github.paohaijiao.demo.service 包,XML 脚本放入 src/test/resources/demo/performance.xml,包名全部保持源码原样。工程根目录执行 mvn test -Dtest=PerformanceServiceTest 即可运行。
PerformanceRecord.java
Section titled “PerformanceRecord.java”package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;
/** * 绩效考核记录(绩效与奖金核算所需的考核快照) */@Data@NoArgsConstructor@AllArgsConstructorpublic class PerformanceRecord {
/** * 工号 */ private String employeeId;
/** * 姓名 */ private String employeeName;
/** * 部门 */ private String dept;
/** * KPI 得分(0-100) */ private double kpiScore;
/** * 项目得分(0-100) */ private double projectScore;
/** * 团队协作风得分(0-100) */ private double teamScore;
/** * 考勤得分(0-100) */ private double attendanceScore;
/** * 是否管理者 */ private boolean manager;
/** * 是否发生违规 */ private boolean hasViolation;
/** * 年度销售额(元) */ private double salesAmount;
/** * 月度基本工资(元) */ private double baseSalary;}BonusDecision.java
Section titled “BonusDecision.java”package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;import java.util.List;
/** * 绩效奖金决策结果 */@Datapublic class BonusDecision {
/** * 工号 */ private String employeeId;
/** * 加权总分(0-100) */ private double totalScore;
/** * 绩效等级:S / A / B / C / D */ private String grade;
/** * 最终奖金系数 */ private double bonusCoefficient;
/** * 奖金金额(元) */ private double bonusAmount;
/** * 考勤等级:0-3 */ private int attendanceLevel;
/** * 是否具备晋升资格 */ private boolean promotionEligible;
/** * 是否进入绩效改进计划 */ private boolean needPip;
/** * 说明与建议 */ private List<String> remarks = new ArrayList<String>();
public void addRemark(String remark) { this.remarks.add(remark); }}PerformanceRules.java
Section titled “PerformanceRules.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/** * 场景 6(生产级样板):绩效等级与奖金系数规则集 * <p> * 规则按「加权评分 → 等级映射 → 系数修正 → 奖金核算」拆分: * <ul> * <li>评分类:KPI / 项目 / 协作 / 考勤四维加权</li> * <li>修正类:管理者加成、销售激励、违规扣减</li> * <li>决策类:晋升资格、绩效改进、奖金金额</li> * </ul> * 规则脚本见 {@code src/test/resources/demo/performance.xml}。 */public interface PerformanceRules {
/** * 加权总分:KPI 50% + 项目 20% + 协作 15% + 考勤 15% */ double weightedScore(@Param("kpiScore") double kpiScore, @Param("projectScore") double projectScore, @Param("teamScore") double teamScore, @Param("attendanceScore") double attendanceScore);
/** * 绩效等级:S / A / B / C / D */ String grade(@Param("totalScore") double totalScore);
/** * 等级基准奖金系数 */ double baseCoefficient(@Param("grade") String grade);
/** * 管理者加成 */ double managerBonus(@Param("manager") boolean manager);
/** * 销售激励:高等级高销售额额外加成 */ double salesBonus(@Param("salesAmount") double salesAmount, @Param("grade") String grade);
/** * 违规扣减 */ double violationPenalty(@Param("hasViolation") boolean hasViolation);
/** * 是否具备晋升资格 */ boolean promotionEligible(@Param("totalScore") double totalScore, @Param("hasViolation") boolean hasViolation);
/** * 是否进入绩效改进计划 */ boolean needPip(@Param("totalScore") double totalScore, @Param("hasViolation") boolean hasViolation);
/** * 考勤等级:>=95 为 3,>=90 为 2,>=80 为 1,否则 0 */ int attendanceLevel(@Param("attendanceScore") double attendanceScore);
/** * 奖金金额 = 基本工资 × 最终系数 */ double bonusAmount(@Param("baseSalary") double baseSalary, @Param("bonusCoefficient") double bonusCoefficient);}performance.xml
Section titled “performance.xml”<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE javas PUBLIC "-//PAOHAIJIAO//DTD API JAVA 1.0//EN" "classpath:paohaijiao/dtd/Jquick-java.dtd"><javas namespace="com.github.paohaijiao.demo.PerformanceRules">
<!-- 加权总分 --> <java name="weightedScore" returnClass="double"> <![CDATA[ double def weightedScore(double:kpiScore, double:projectScore, double:teamScore, double:attendanceScore) { double total = kpiScore * 0.5 + projectScore * 0.2 + teamScore * 0.15 + attendanceScore * 0.15; return total; } ]]> </java>
<!-- 绩效等级 --> <java name="grade" returnClass="java.lang.String"> <![CDATA[ str def grade(double:totalScore) { if (totalScore >= 90.0) { return "S"; } else if (totalScore >= 80.0) { return "A"; } else if (totalScore >= 70.0) { return "B"; } else if (totalScore >= 60.0) { return "C"; } else { return "D"; } } ]]> </java>
<!-- 等级基准系数 --> <java name="baseCoefficient" returnClass="double"> <![CDATA[ double def baseCoefficient(str:grade) { if (grade == "S") { return 1.8; } else if (grade == "A") { return 1.4; } else if (grade == "B") { return 1.1; } else if (grade == "C") { return 0.8; } else { return 0.5; } } ]]> </java>
<!-- 管理者加成 --> <java name="managerBonus" returnClass="double"> <![CDATA[ double def managerBonus(boolean:manager) { if (manager) { return 0.2; } else { return 0.0; } } ]]> </java>
<!-- 销售激励 --> <java name="salesBonus" returnClass="double"> <![CDATA[ double def salesBonus(double:salesAmount, str:grade) { if ((grade == "S" || grade == "A") && salesAmount >= 500000.0) { return 0.2; } else if (salesAmount >= 200000.0) { return 0.1; } else { return 0.0; } } ]]> </java>
<!-- 违规扣减 --> <java name="violationPenalty" returnClass="double"> <![CDATA[ double def violationPenalty(boolean:hasViolation) { if (hasViolation) { return -0.3; } else { return 0.0; } } ]]> </java>
<!-- 晋升资格 --> <java name="promotionEligible" returnClass="boolean"> <![CDATA[ boolean def promotionEligible(double:totalScore, boolean:hasViolation) { if (hasViolation) { return false; } else if (totalScore >= 85.0) { return true; } else { return false; } } ]]> </java>
<!-- 绩效改进 --> <java name="needPip" returnClass="boolean"> <![CDATA[ boolean def needPip(double:totalScore, boolean:hasViolation) { if (totalScore < 60.0) { return true; } else if (totalScore < 70.0 && hasViolation) { return true; } else { return false; } } ]]> </java>
<!-- 考勤等级 --> <java name="attendanceLevel" returnClass="int"> <![CDATA[ int def attendanceLevel(double:attendanceScore) { if (attendanceScore >= 95.0) { return 3; } else if (attendanceScore >= 90.0) { return 2; } else if (attendanceScore >= 80.0) { return 1; } else { return 0; } } ]]> </java>
<!-- 奖金金额 --> <java name="bonusAmount" returnClass="double"> <![CDATA[ double def bonusAmount(double:baseSalary, double:bonusCoefficient) { return baseSalary * bonusCoefficient; } ]]> </java>
</javas>PerformanceService.java
Section titled “PerformanceService.java”package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.PerformanceRules;import com.github.paohaijiao.demo.model.BonusDecision;import com.github.paohaijiao.demo.model.PerformanceRecord;
import java.math.BigDecimal;import java.math.RoundingMode;
/** * 场景 6:绩效等级与奖金核算服务 * <p> * 职责划分: * <ul> * <li><b>Java 侧</b>:四维加权汇总、系数聚合与下限保护、晋升与改进判定、评语组装</li> * <li><b>规则侧</b>:权重、等级阈值、基准系数、各类加成与扣减,全部外置在 XML 中(可随年度政策调整)</li> * </ul> */public class PerformanceService {
/** * 晋升资格总分线 */ private static final double PROMOTION_SCORE_LINE = 85.0;
private final PerformanceRules rules;
public PerformanceService(PerformanceRules rules) { this.rules = rules; }
/** * 核算绩效与奖金 * * @param record 考核快照 * @return 绩效奖金决策 */ public BonusDecision evaluate(PerformanceRecord record) { BonusDecision decision = new BonusDecision(); decision.setEmployeeId(record.getEmployeeId());
double totalScore = scale2(rules.weightedScore(record.getKpiScore(), record.getProjectScore(), record.getTeamScore(), record.getAttendanceScore())); String grade = rules.grade(totalScore);
double coefficient = rules.baseCoefficient(grade) + rules.managerBonus(record.isManager()) + rules.salesBonus(record.getSalesAmount(), grade) + rules.violationPenalty(record.isHasViolation()); if (coefficient < 0.0) { coefficient = 0.0; } coefficient = scale2(coefficient);
decision.setTotalScore(totalScore); decision.setGrade(grade); decision.setBonusCoefficient(coefficient); decision.setBonusAmount(scale2(rules.bonusAmount(record.getBaseSalary(), coefficient))); decision.setAttendanceLevel(rules.attendanceLevel(record.getAttendanceScore())); decision.setPromotionEligible(rules.promotionEligible(totalScore, record.isHasViolation())); decision.setNeedPip(rules.needPip(totalScore, record.isHasViolation()));
buildRemarks(record, decision); return decision; }
private void buildRemarks(PerformanceRecord record, BonusDecision decision) { if ("S".equals(decision.getGrade())) { decision.addRemark("绩效等级 S,优先纳入年度评优与晋升池"); } if (decision.isPromotionEligible()) { decision.addRemark("总分不低于 " + (int) PROMOTION_SCORE_LINE + " 且无违规,具备晋升资格"); } if (record.isHasViolation()) { decision.addRemark("存在违规记录,奖金系数已扣减 0.3"); } if (decision.isNeedPip()) { decision.addRemark("绩效不达标,需进入绩效改进计划(PIP)"); } if (decision.getAttendanceLevel() == 0) { decision.addRemark("考勤得分低于 80,请关注出勤情况"); } if (record.getSalesAmount() >= 500000.0 && ("S".equals(decision.getGrade()) || "A".equals(decision.getGrade()))) { decision.addRemark("销售额达标且绩效优秀,享受销售激励加成"); } }
private double scale2(double value) { return BigDecimal.valueOf(value).setScale(2, RoundingMode.HALF_UP).doubleValue(); }}PerformanceServiceTest.java
Section titled “PerformanceServiceTest.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.BonusDecision;import com.github.paohaijiao.demo.model.PerformanceRecord;import com.github.paohaijiao.demo.service.PerformanceService;import com.github.paohaijiao.xml.JQuickJava;import org.junit.Assert;import org.junit.Before;import org.junit.Test;
/** * 场景 6 生产级样板:绩效等级与奖金核算 * <p> * 覆盖 6 组绩效形态:优秀员工、低绩效违规、管理者达标、中等绩效、 * 不达标待改进、优秀但有违规。 */public class PerformanceServiceTest {
private PerformanceService service;
@Before public void setUp() { PerformanceRules rules = JQuickJava.create() .createApi(PerformanceRules.class, "demo/performance.xml"); this.service = new PerformanceService(rules); }
/** * 优秀员工:四维均优、销售额达标 → S 级、系数 2.0、奖金翻倍 */ @Test public void shouldRewardTopPerformer() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP001"); record.setEmployeeName("张伟"); record.setDept("销售部"); record.setKpiScore(95.0); record.setProjectScore(90.0); record.setTeamScore(85.0); record.setAttendanceScore(96.0); record.setManager(false); record.setHasViolation(false); record.setSalesAmount(600000.0); record.setBaseSalary(20000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(92.65, decision.getTotalScore(), 0.001); Assert.assertEquals("S", decision.getGrade()); Assert.assertEquals(2.0, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(40000.0, decision.getBonusAmount(), 0.001); Assert.assertEquals(3, decision.getAttendanceLevel()); Assert.assertTrue(decision.isPromotionEligible()); Assert.assertFalse(decision.isNeedPip()); Assert.assertTrue(decision.getRemarks().contains("绩效等级 S,优先纳入年度评优与晋升池")); }
/** * 低绩效违规:总分 51.25(D 级)、违规扣减 0.3 → 系数仅 0.2、进入 PIP */ @Test public void shouldPenalizeLowPerformerWithViolation() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP002"); record.setEmployeeName("李强"); record.setDept("运营部"); record.setKpiScore(45.0); record.setProjectScore(50.0); record.setTeamScore(55.0); record.setAttendanceScore(70.0); record.setManager(false); record.setHasViolation(true); record.setSalesAmount(100000.0); record.setBaseSalary(10000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(51.25, decision.getTotalScore(), 0.001); Assert.assertEquals("D", decision.getGrade()); Assert.assertEquals(0.2, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(2000.0, decision.getBonusAmount(), 0.001); Assert.assertEquals(0, decision.getAttendanceLevel()); Assert.assertFalse(decision.isPromotionEligible()); Assert.assertTrue(decision.isNeedPip()); }
/** * 管理者达标:总分 85.0(A 级)、管理加成 0.2、销售激励 0.1 → 系数 1.7 */ @Test public void shouldApplyManagerAndSalesBonus() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP003"); record.setEmployeeName("王芳"); record.setDept("销售部"); record.setKpiScore(82.0); record.setProjectScore(85.0); record.setTeamScore(88.0); record.setAttendanceScore(92.0); record.setManager(true); record.setHasViolation(false); record.setSalesAmount(300000.0); record.setBaseSalary(30000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(85.0, decision.getTotalScore(), 0.001); Assert.assertEquals("A", decision.getGrade()); Assert.assertEquals(1.7, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(51000.0, decision.getBonusAmount(), 0.001); Assert.assertEquals(2, decision.getAttendanceLevel()); Assert.assertTrue(decision.isPromotionEligible()); Assert.assertFalse(decision.isNeedPip()); }
/** * 中等绩效:总分 72.35(B 级)、销售激励 0.1 → 系数 1.2,无晋升无 PIP */ @Test public void shouldSettleAveragePerformer() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP004"); record.setEmployeeName("赵敏"); record.setDept("市场部"); record.setKpiScore(70.0); record.setProjectScore(72.0); record.setTeamScore(68.0); record.setAttendanceScore(85.0); record.setManager(false); record.setHasViolation(false); record.setSalesAmount(250000.0); record.setBaseSalary(15000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(72.35, decision.getTotalScore(), 0.001); Assert.assertEquals("B", decision.getGrade()); Assert.assertEquals(1.2, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(18000.0, decision.getBonusAmount(), 0.001); Assert.assertEquals(1, decision.getAttendanceLevel()); Assert.assertFalse(decision.isPromotionEligible()); Assert.assertFalse(decision.isNeedPip()); }
/** * 不达标待改进:总分 56.95(D 级)→ 系数 0.5、进入 PIP、考勤预警 */ @Test public void shouldRouteUnderperformerToPip() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP005"); record.setEmployeeName("陈静"); record.setDept("客服部"); record.setKpiScore(52.0); record.setProjectScore(55.0); record.setTeamScore(58.0); record.setAttendanceScore(75.0); record.setManager(false); record.setHasViolation(false); record.setSalesAmount(60000.0); record.setBaseSalary(12000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(56.95, decision.getTotalScore(), 0.001); Assert.assertEquals("D", decision.getGrade()); Assert.assertEquals(0.5, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(6000.0, decision.getBonusAmount(), 0.001); Assert.assertEquals(0, decision.getAttendanceLevel()); Assert.assertTrue(decision.isNeedPip()); Assert.assertTrue(decision.getRemarks().contains("考勤得分低于 80,请关注出勤情况")); }
/** * 优秀但有违规:总分 87.3(A 级)但违规 → 无晋升资格、系数被扣减 */ @Test public void shouldBlockPromotionForViolation() { PerformanceRecord record = new PerformanceRecord(); record.setEmployeeId("EMP006"); record.setEmployeeName("周涛"); record.setDept("技术部"); record.setKpiScore(88.0); record.setProjectScore(86.0); record.setTeamScore(84.0); record.setAttendanceScore(90.0); record.setManager(false); record.setHasViolation(true); record.setSalesAmount(520000.0); record.setBaseSalary(25000.0);
BonusDecision decision = service.evaluate(record);
Assert.assertEquals(87.3, decision.getTotalScore(), 0.001); Assert.assertEquals("A", decision.getGrade()); Assert.assertEquals(1.3, decision.getBonusCoefficient(), 0.001); Assert.assertEquals(32500.0, decision.getBonusAmount(), 0.001); Assert.assertFalse(decision.isPromotionEligible()); Assert.assertTrue(decision.getRemarks().contains("存在违规记录,奖金系数已扣减 0.3")); }}在工程根目录执行:
mvn test -Dtest=PerformanceServiceTest测试覆盖形态
Section titled “测试覆盖形态”| 测试方法 | 构造的业务形态 | 关键断言/期望决策 |
|---|---|---|
shouldRewardTopPerformer |
优秀销售:四维 95/90/85/96、销售额 60 万、非管理者 | 综合分 92.65、S 级、系数 2.0、奖金 40000.0、考勤档 3、可晋升、无 PIP、含 S 级评优评语 |
shouldPenalizeLowPerformerWithViolation |
低绩效违规:四维 45/50/55/70、有违规 | 综合分 51.25、D 级、系数 0.2、奖金 2000.0、考勤档 0、不可晋升、需 PIP |
shouldApplyManagerAndSalesBonus |
管理者:四维 82/85/88/92、管理者、销售额 30 万 | 综合分 85.0、A 级、管理 +0.2 与销售 +0.1 叠加后系数 1.7、奖金 51000.0、考勤档 2、可晋升 |
shouldSettleAveragePerformer |
中等绩效:四维 70/72/68/85、销售额 25 万 | 综合分 72.35、B 级、系数 1.2、奖金 18000.0、考勤档 1、不可晋升、不进 PIP |
shouldRouteUnderperformerToPip |
不达标:四维 52/55/58/75、无违规 | 综合分 56.95、D 级、系数 0.5、奖金 6000.0、考勤档 0、需 PIP、含考勤低于 80 预警评语 |
shouldBlockPromotionForViolation |
优秀但违规:四维 88/86/84/90、销售额 52 万、有违规 | 综合分 87.3、A 级、系数 1.3、奖金 32500.0、不可晋升、含违规扣减 0.3 评语 |
USE-CASES.md 场景 6 收录的两组实测输出:
优秀销售(综合 92.65、S 级、销售额达标):
等级: S 综合分: 92.65 奖金系数: 2.0 奖金金额: 40000.0晋升资格: true PIP: false低绩效且违规(综合 51.25、D 级):
等级: D 综合分: 51.25 奖金系数: 0.2 PIP: true- 权重 0.5/0.2/0.15/0.15、五档分数线、基准系数与三类调节值全部外置在 XML 中,年度考核政策调整改脚本即可热更新,业务系统无需重新发版。
- 10 个规则方法一个方法一个口径:加权、定级、系数、管理/销售/违规调节、晋升、PIP、考勤档、金额核算彼此独立,可单独调参与审计。
- Service 在 Java 侧做系数聚合后的下限保护(叠加为负时归零)与
BigDecimalHALF_UP两位小数收口,保证金额口径统一,晋升线在 Java 侧复核。 - 脚本不支持
!取反,promotionEligible以正向if先判违规提前return false,干净地实现「违规一票否决」。 - 规则接口通过
@Param按参数名绑定(如salesBonus(salesAmount, grade)),脚本方法签名用double:/str:/boolean:声明类型,代理实例创建后可跨考核周期复用,底层为 ASM 调用链。 - Java 8 即可运行、无重型规则平台依赖,能直接嵌入仍在 JDK 8 的 eHR/薪资系统与信创配套 JDK 环境。