Case 6: Performance grade & bonus coefficient
This case corresponds to Scenario 6: Performance grade & bonus coefficient in the JQuick-Java repository’s USE-CASES.md. It belongs to the human resources domain and solves the integrated decision chain inside an appraisal cycle: “four-dimension weighted scoring → S/A/B/C/D grade mapping → bonus coefficient adjustment → bonus amount / promotion eligibility / PIP determination”.
Industry scenario
Section titled “Industry scenario”In an annual or quarterly performance appraisal cycle, HR first aggregates four kinds of employee scores: KPI score, project score, teamwork score, and attendance score (all 0-100), weighted into a composite score with fixed weights of KPI 50%, project 20%, teamwork 15%, and attendance 15%.
The composite score maps to five performance grades S/A/B/C/D, each with a base bonus coefficient: S is 1.8, A is 1.4, B is 1.1, C is 0.8, D is 0.5. On top of the base coefficient, three adjustments are stacked: a manager responsibility bonus of +0.2, a sales incentive (grade S/A with annual sales reaching 500k adds 0.2; sales reaching 200k adds 0.1), and a violation deduction of -0.3. The final coefficient multiplied by the monthly base salary yields the bonus amount.
The same decision chain also produces in parallel: promotion eligibility (total score no less than 85 and no violation record), whether the employee is forced into a PIP performance improvement plan, the attendance tier (scores 95/90/80 map to tiers 3/2/1/0), and an employee-facing remark list (excellence pool entry, violation deduction explanation, attendance warning, sales incentive explanation, etc.).
These outputs directly drive bonus payout, annual promotion pool screening, and PIP follow-up. Scenario 6 covers 6 real employee profiles: excellent sales (composite 92.65, grade S, coefficient 2.0), low performer with violation (51.25, grade D, coefficient 0.2), manager grade A (coefficient 1.7), average grade B (coefficient 1.2), underperformer grade D (coefficient 0.5), and an excellent employee whose promotion is blocked by a violation.
Business pain points
Section titled “Business pain points”- Appraisal weights 50%/20%/15%/15%, S/A/B/C/D score lines, and base coefficients 1.8/1.4/1.1/0.8/0.5 are scattered across the payroll module, Excel templates, and hard-coded logic; every annual policy change requires coordinating multiple redeployments.
- Manager bonus, sales incentive (two tiers at 500k/200k), and violation deduction are entangled in the same
if-elseblock; changing one tier easily misses linked logic. - Promotion eligibility (“total score 85 and no violation”) and the PIP entry are coupled with bonus calculation in business code, so HR cannot tune parameters or run simulations independently.
- Stacked coefficients can theoretically go negative, and each system lacks a unified floor guard; decimal conventions for amounts (two digits, half-up rounding) are also inconsistent.
- Decisions often return only a bonus number; when employees challenge the result, it is hard to trace the composite score, grade, and every bonus or deduction.
- Many eHR and payroll systems still run on JDK 8 or domestic IT-innovation environments, making it hard to introduce heavy rule platforms such as Drools.
Solution architecture
Section titled “Solution architecture”This case uses the standard “six-piece set”: the input model and decision result model carry the data, the rule interface declares the calculation policies, the XML script externalizes all thresholds and coefficients, the Service handles aggregation and floor protection, and JUnit regresses all decision paths with 6 employee profiles.
| Layer | Responsibility | Files |
|---|---|---|
| Domain model | Performance appraisal snapshot, bonus decision result | PerformanceRecord.java, BonusDecision.java |
| Rule set & XML | 10 rule methods covering weighting, grading, coefficient, three adjustments, promotion, and PIP | PerformanceRules.java, performance.xml |
| Orchestration service | Coefficient aggregation with floor protection, two-decimal amounts, promotion line review, remark assembly | PerformanceService.java |
| Tests | 6 employee profiles (excellent / low performer with violation / manager / average / underperformer / excellent with violation) | PerformanceServiceTest.java |
Rule method breakdown
Section titled “Rule method breakdown”| Rule method | Policy | Score/output |
|---|---|---|
weightedScore |
KPI×50% + project×20% + teamwork×15% + attendance×15% | 0-100 composite score |
grade |
≥90 S / ≥80 A / ≥70 B / ≥60 C / otherwise D | S/A/B/C/D |
baseCoefficient |
S 1.8 / A 1.4 / B 1.1 / C 0.8 / D 0.5 | Base coefficient |
managerBonus |
Manager +0.2, otherwise 0 | +0.2 / 0 |
salesBonus |
S/A with sales ≥500k +0.2; sales ≥200k +0.1; otherwise 0 | +0.2 / +0.1 / 0 |
violationPenalty |
Violation -0.3, otherwise 0 | -0.3 / 0 |
promotionEligible |
Eligible only when total score ≥85 and no violation (violation veto first) | true / false |
needPip |
Total score below 60, or below 70 with violation → enter PIP | true / false |
attendanceLevel |
Attendance ≥95 → 3 / ≥90 → 2 / ≥80 → 1 / otherwise 0 | Tier 0-3 |
bonusAmount |
Monthly base salary × final bonus coefficient | Bonus amount (CNY) |
Code implementation (complete & runnable)
Section titled “Code implementation (complete & runnable)”Place the six files into a Maven project following the repository’s original package structure: the two models go into src/test/java/com/github/paohaijiao/demo/model/, the rule interface, orchestration service, and test go into the com.github.paohaijiao.demo and com.github.paohaijiao.demo.service packages, and the XML script goes into src/test/resources/demo/performance.xml; keep all package names exactly as in the source. Run mvn test -Dtest=PerformanceServiceTest at the project root to execute.
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")); }}Run & verified results
Section titled “Run & verified results”Run at the project root:
mvn test -Dtest=PerformanceServiceTestTest coverage
Section titled “Test coverage”| Test method | Business profile constructed | Key assertions / expected decision |
|---|---|---|
shouldRewardTopPerformer |
Excellent sales: four scores 95/90/85/96, sales 600k, non-manager | Composite 92.65, grade S, coefficient 2.0, bonus 40000.0, attendance tier 3, promotion eligible, no PIP, includes grade-S excellence remark |
shouldPenalizeLowPerformerWithViolation |
Low performer with violation: four scores 45/50/55/70, has violation | Composite 51.25, grade D, coefficient 0.2, bonus 2000.0, attendance tier 0, not promotion eligible, needs PIP |
shouldApplyManagerAndSalesBonus |
Manager: four scores 82/85/88/92, manager, sales 300k | Composite 85.0, grade A, coefficient 1.7 after stacking manager +0.2 and sales +0.1, bonus 51000.0, attendance tier 2, promotion eligible |
shouldSettleAveragePerformer |
Average performer: four scores 70/72/68/85, sales 250k | Composite 72.35, grade B, coefficient 1.2, bonus 18000.0, attendance tier 1, not promotion eligible, no PIP |
shouldRouteUnderperformerToPip |
Underperformer: four scores 52/55/58/75, no violation | Composite 56.95, grade D, coefficient 0.5, bonus 6000.0, attendance tier 0, needs PIP, includes attendance-below-80 warning remark |
shouldBlockPromotionForViolation |
Excellent but with violation: four scores 88/86/84/90, sales 520k, has violation | Composite 87.3, grade A, coefficient 1.3, bonus 32500.0, not promotion eligible, includes violation-deduction 0.3 remark |
Verified output
Section titled “Verified output”Two verified outputs recorded in USE-CASES.md Scenario 6:
Excellent sales (composite 92.65, grade S, sales target met):
等级: S 综合分: 92.65 奖金系数: 2.0 奖金金额: 40000.0晋升资格: true PIP: falseLow performer with violation (composite 51.25, grade D):
等级: D 综合分: 51.25 奖金系数: 0.2 PIP: trueTechnical highlights
Section titled “Technical highlights”- Weights 0.5/0.2/0.15/0.15, the five grade cutoffs, base coefficients, and the three adjustment values are all externalized in XML; annual appraisal policy changes only require editing the script for a hot update, with no redeployment of the business system.
- 10 rule methods, one policy per method: weighting, grading, coefficient, manager/sales/violation adjustments, promotion, PIP, attendance tier, and amount calculation are independent and can be tuned or audited separately.
- The Service performs floor protection on the aggregated coefficient in Java (resetting negatives to zero) and normalizes amounts with
BigDecimalHALF_UPat two decimal places, keeping amount conventions consistent; the promotion line is double-checked on the Java side. - The script does not support the
!negation operator, sopromotionEligibleuses a positiveifto check violation first andreturn falseearly, cleanly implementing the “violation veto”. - The rule interface binds parameters by name via
@Param(e.g.salesBonus(salesAmount, grade)), and script method signatures declare types withdouble:/str:/boolean:; once created, the proxy instance can be reused across appraisal cycles, backed by an ASM call chain. - Runs on Java 8 with no heavy rule platform dependency, and can be embedded directly into eHR/payroll systems still on JDK 8 and IT-innovation JDK environments.