跳转到内容

案例 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
规则方法 口径 分值/输出
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 月度基本工资 × 最终奖金系数 奖金金额(元)

将六个文件按仓库原有包结构放入 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 即可运行。

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 绩效考核记录(绩效与奖金核算所需的考核快照)
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public 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;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 绩效奖金决策结果
*/
@Data
public 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);
}
}
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);
}
<?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>
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();
}
}
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"));
}
}

在工程根目录执行:

Terminal window
mvn test -Dtest=PerformanceServiceTest
测试方法 构造的业务形态 关键断言/期望决策
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 侧做系数聚合后的下限保护(叠加为负时归零)与 BigDecimal HALF_UP 两位小数收口,保证金额口径统一,晋升线在 Java 侧复核。
  • 脚本不支持 ! 取反,promotionEligible 以正向 if 先判违规提前 return false,干净地实现「违规一票否决」。
  • 规则接口通过 @Param 按参数名绑定(如 salesBonus(salesAmount, grade)),脚本方法签名用 double:/str:/boolean: 声明类型,代理实例创建后可跨考核周期复用,底层为 ASM 调用链。
  • Java 8 即可运行、无重型规则平台依赖,能直接嵌入仍在 JDK 8 的 eHR/薪资系统与信创配套 JDK 环境。