Case 26: Order anti-fraud check
This case corresponds to scenario 26 “Order anti-fraud check” in the JQuick-Java repository USE-CASES, in the e-commerce risk control domain. During marketing campaigns such as flash sales, discounts and coupon giveaways, every campaign order gets a real-time anti-fraud decision: six independent scoring dimensions are aggregated into a 0-100 total risk score, overlaid with two Java-side hard rules that short-circuit first, finally producing PASS, manual REVIEW or REJECT.
Industry scenario
Section titled “Industry scenario”During major promotions, black-market actors commonly use batch-registered new accounts, device pools / IP pools of clustered accounts, and flash-sale scripts placing high-frequency orders to grab coupons, subsidies and limited-quantity goods. The platform must collect risk signals for every campaign order in the order pipeline and return a decision within milliseconds.
The scoring model is split into six mutually independent dimensions, each with its own cap: account age risk (0-20), order frequency risk (0-25, a 1-hour tier plus a 24-hour tier added together), device/IP clustering risk (0-20, device tier plus IP tier), identity and address risk (0-15), coupon-order and empty-package risk (0-10, coupon-order tier plus historical empty-package-rate tier), and order-amount deviation risk (0-10). The six scores are added up and capped at 100 on the Service side.
Beyond the soft scoring, the Service also maintains two Java-side hard rules that cannot be diluted by scores: an account that is not real-name verified while the number of accounts associated with the same device within 24 hours reaches 5 is judged a “black-market device pool”; placing 5 orders within 1 hour while the order amount deviates from the historical level by 400% is judged a “flash-sale script pattern”. Hitting either hard rule, or reaching a total score of 70, results in rejection; 40 to 69 routes to manual review; everything else passes.
The decision output is CampaignFraudDecision: total risk score, action, whether a hard rule was hit, per-dimension score details (scoreDetails), and risk tags collected by element thresholds (riskTags, such as “registered less than 7 days”, “multiple accounts on the same device”, “high historical empty-package rate”), used for risk-operations review and customer-facing explanations.
Business pain points
Section titled “Business pain points”- Promotion attack-defense changes weekly or even daily. When black-market actors switch account and device features, if scoring tiers and hard-rule thresholds are hard-coded in the trading system, every tuning requires a full release, which cannot keep up with the adversary’s pace.
- The six risk dimensions need independent tuning, gray rollout, and even per-campaign differentiation (e.g. temporarily tightening the coupon dimension on coupon-issue days). Written in one block of
if-else, they cannot be maintained or traced separately. - Hard veto and soft scoring must be layered: a flash-sale script must be rejected even at only 30 points. The traditional approach of mixing all conditions into one decision table makes it hard to explain “why was this rejected despite a low score”.
- When merchants or users appeal, the scoring basis and hit tags must be reconstructed dimension by dimension; a black-box risk conclusion cannot be audited or used as evidence.
- Legacy trading middle platforms often run on JDK 8 and domestic-innovation JDKs, making it hard to introduce rule platforms that require standalone deployment with heavy storage and consoles.
- Signals such as device fingerprint, IP clustering and empty-package rate come from different data domains; after aggregation the decision must return within milliseconds on the ordering path, so it cannot afford the network overhead of a cross-process rule service.
Solution architecture
Section titled “Solution architecture”This case uses the six-part structure “input model + decision model + rule interface + XML rule script + Service orchestration + JUnit tests”: the rule interface declares only the six scoring calibers, all threshold tiers are externalized in XML, and the Service handles aggregation, capping, hard-rule short-circuiting and tag assembly.
| Layer | Responsibility | Files |
|---|---|---|
| Domain model layer | Carries campaign-order fraud risk elements; outputs score, action, details and risk tags | CampaignOrder.java, CampaignFraudDecision.java |
| Rule interface layer | Declares six scoring methods; inputs bound by name via @Param |
CampaignFraudRules.java |
| Rule script layer | All six-dimension threshold tiers and scores externalized; supports hot update | campaign-fraud.xml |
| Orchestration & test layer | Score aggregation, 100-point cap, hard-rule pre-short-circuit, tag assembly; regression over 6 order profiles | CampaignFraudService.java, CampaignFraudServiceTest.java |
Rule method breakdown
Section titled “Rule method breakdown”| Rule method | Caliber | Score/output |
|---|---|---|
| scoreAccountAge | Tiered by account registration days: less than 1 day = 20; less than 7 days = 14; less than 30 days = 8; less than 90 days = 3; otherwise 0 | 0-20 |
| scoreFrequency | Sum of the 1-hour tier (5+ orders +18, 3+ +12, 1+ +5) and the 24-hour tier (15+ +7, 8+ +4, 3+ +2) | 0-25 |
| scoreDevice | Sum of the same-device account tier (5+ +14, 3+ +8, 2+ +4) and the same-IP account tier (10+ +6, 5+ +4, 3+ +2) | 0-20 |
| scoreIdentity | Not real-name +8; payer inconsistent with buyer +4; virtual/collection-point address +3 | 0-15 |
| scoreCoupon | Sum of the 7-day coupon-order tier (10+ +6, 5+ +4, 2+ +2) and the historical empty-package-rate tier (50%+ +4, 20%+ +2) | 0-10 |
| scoreDeviation | Amount deviation rate 400%+ = 10, 200%+ = 6, 100%+ = 3, otherwise 0 | 0-10 |
Code implementation (complete & runnable)
Section titled “Code implementation (complete & runnable)”Place the following files into the JQuick-Java test project under their original packages: the two models into src/test/java/com/github/paohaijiao/demo/model/, the rule interface and test class into src/test/java/com/github/paohaijiao/demo/, the Service into src/test/java/com/github/paohaijiao/demo/service/, and the XML script into src/test/resources/demo/. Run mvn test -Dtest=CampaignFraudServiceTest at the project root to execute.
CampaignOrder.java
Section titled “CampaignOrder.java”package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;
/** * 营销活动订单的刷单风险要素 */@Data@NoArgsConstructor@AllArgsConstructorpublic class CampaignOrder {
/** * 订单号 */ private String orderId;
/** * 下单用户 */ private String userId;
/** * 活动编号 */ private String campaignId;
/** * 账户注册天数 */ private int accountAgeDays;
/** * 近 1 小时下单数 */ private int orderCount1h;
/** * 近 24 小时下单数 */ private int orderCount24h;
/** * 24 小时内同设备关联账户数 */ private int deviceAccountCount24h;
/** * 24 小时内同 IP 关联账户数 */ private int ipAccountCount24h;
/** * 7 天内使用优惠券的订单数 */ private int couponOrderCount7d;
/** * 历史空包率(整数百分比) */ private int emptyPackageRate;
/** * 本单客单价相对历史均单的偏离率(整数百分比,如 400 表示高出 4 倍) */ private int amountDeviationPercent;
/** * 是否已实名认证 */ private boolean realName;
/** * 付款人与下单人实名是否一致 */ private boolean paymentConsistent;
/** * 收货地址是否为虚拟/集中代收点 */ private boolean virtualAddress;}CampaignFraudDecision.java
Section titled “CampaignFraudDecision.java”package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;import java.util.List;
/** * 营销刷单风控决策 */@Datapublic class CampaignFraudDecision {
/** * 订单号 */ private String orderId;
/** * 风险总分(0-100,越高越危险) */ private int riskScore;
/** * 处置动作:PASS 放行 / REVIEW 复核 / REJECT 拦截 */ private String action;
/** * 是否命中硬规则 */ private boolean hardRuleHit;
/** * 各维度得分明细 */ private List<String> scoreDetails = new ArrayList<String>();
/** * 命中的风险标签 */ private List<String> riskTags = new ArrayList<String>();
public void addDetail(String detail) { this.scoreDetails.add(detail); }
public void addRiskTag(String tag) { this.riskTags.add(tag); }}CampaignFraudRules.java
Section titled “CampaignFraudRules.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/** * 场景 26:营销活动订单防刷单六维风险评分 */public interface CampaignFraudRules {
/** * 账户龄风险(0-20) */ int scoreAccountAge(@Param("days") int days);
/** * 下单频次风险(0-25):1 小时档 + 24 小时档 */ int scoreFrequency(@Param("count1h") int count1h, @Param("count24h") int count24h);
/** * 设备/IP 聚集风险(0-20) */ int scoreDevice(@Param("deviceAccounts") int deviceAccounts, @Param("ipAccounts") int ipAccounts);
/** * 身份与地址风险(0-15) */ int scoreIdentity(@Param("realName") boolean realName, @Param("paymentConsistent") boolean paymentConsistent, @Param("virtualAddress") boolean virtualAddress);
/** * 券单与空包风险(0-10) */ int scoreCoupon(@Param("couponOrders") int couponOrders, @Param("emptyRate") int emptyRate);
/** * 客单价偏离风险(0-10) */ int scoreDeviation(@Param("deviationPercent") int deviationPercent);}campaign-fraud.xml
Section titled “campaign-fraud.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.CampaignFraudRules">
<java name="scoreAccountAge" returnClass="int"> <![CDATA[ int def scoreAccountAge(int:days) { if (days < 1) { return 20; } else if (days < 7) { return 14; } else if (days < 30) { return 8; } else if (days < 90) { return 3; } else { return 0; } } ]]> </java>
<java name="scoreFrequency" returnClass="int"> <![CDATA[ int def scoreFrequency(int:count1h, int:count24h) { int score = 0; if (count1h >= 5) { score = score + 18; } else if (count1h >= 3) { score = score + 12; } else if (count1h >= 1) { score = score + 5; } if (count24h >= 15) { score = score + 7; } else if (count24h >= 8) { score = score + 4; } else if (count24h >= 3) { score = score + 2; } return score; } ]]> </java>
<java name="scoreDevice" returnClass="int"> <![CDATA[ int def scoreDevice(int:deviceAccounts, int:ipAccounts) { int score = 0; if (deviceAccounts >= 5) { score = score + 14; } else if (deviceAccounts >= 3) { score = score + 8; } else if (deviceAccounts >= 2) { score = score + 4; } if (ipAccounts >= 10) { score = score + 6; } else if (ipAccounts >= 5) { score = score + 4; } else if (ipAccounts >= 3) { score = score + 2; } return score; } ]]> </java>
<java name="scoreIdentity" returnClass="int"> <![CDATA[ int def scoreIdentity(boolean:realName, boolean:paymentConsistent, boolean:virtualAddress) { int score = 0; if (realName) { } else { score = score + 8; } if (paymentConsistent) { } else { score = score + 4; } if (virtualAddress) { score = score + 3; } return score; } ]]> </java>
<java name="scoreCoupon" returnClass="int"> <![CDATA[ int def scoreCoupon(int:couponOrders, int:emptyRate) { int score = 0; if (couponOrders >= 10) { score = score + 6; } else if (couponOrders >= 5) { score = score + 4; } else if (couponOrders >= 2) { score = score + 2; } if (emptyRate >= 50) { score = score + 4; } else if (emptyRate >= 20) { score = score + 2; } return score; } ]]> </java>
<java name="scoreDeviation" returnClass="int"> <![CDATA[ int def scoreDeviation(int:deviationPercent) { if (deviationPercent >= 400) { return 10; } else if (deviationPercent >= 200) { return 6; } else if (deviationPercent >= 100) { return 3; } else { return 0; } } ]]> </java>
</javas>CampaignFraudService.java
Section titled “CampaignFraudService.java”package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.CampaignFraudRules;import com.github.paohaijiao.demo.model.CampaignFraudDecision;import com.github.paohaijiao.demo.model.CampaignOrder;
/** * 场景 26:营销活动订单防刷单风控 * <p> * 六维风险评分(账户龄/频次/设备聚集/身份/券单空包/客单偏离)叠加硬规则拦截。 */public class CampaignFraudService {
private final CampaignFraudRules rules;
public CampaignFraudService(CampaignFraudRules rules) { this.rules = rules; }
public CampaignFraudDecision check(CampaignOrder order) { CampaignFraudDecision d = new CampaignFraudDecision(); d.setOrderId(order.getOrderId());
int ageScore = rules.scoreAccountAge(order.getAccountAgeDays()); int freqScore = rules.scoreFrequency(order.getOrderCount1h(), order.getOrderCount24h()); int deviceScore = rules.scoreDevice(order.getDeviceAccountCount24h(), order.getIpAccountCount24h()); int identityScore = rules.scoreIdentity(order.isRealName(), order.isPaymentConsistent(), order.isVirtualAddress()); int couponScore = rules.scoreCoupon(order.getCouponOrderCount7d(), order.getEmptyPackageRate()); int deviationScore = rules.scoreDeviation(order.getAmountDeviationPercent());
d.addDetail("账户龄: " + ageScore + " / 20"); d.addDetail("下单频次: " + freqScore + " / 25"); d.addDetail("设备与IP聚集: " + deviceScore + " / 20"); d.addDetail("身份与地址: " + identityScore + " / 15"); d.addDetail("券单与空包: " + couponScore + " / 10"); d.addDetail("客单价偏离: " + deviationScore + " / 10");
int total = ageScore + freqScore + deviceScore + identityScore + couponScore + deviationScore; if (total > 100) { total = 100; }
boolean devicePoolHit = !order.isRealName() && order.getDeviceAccountCount24h() >= 5; boolean scriptHit = order.getOrderCount1h() >= 5 && order.getAmountDeviationPercent() >= 400; boolean hardHit = devicePoolHit || scriptHit; d.setHardRuleHit(hardHit); if (devicePoolHit) { d.addRiskTag("黑产设备池:未实名账户聚集同一设备"); } if (scriptHit) { d.addRiskTag("秒杀脚本特征:1 小时高频下单且客单价严重偏离"); }
collectSoftTags(d, order);
if (hardHit || total >= 70) { d.setAction("REJECT"); } else if (total >= 40) { d.setAction("REVIEW"); } else { d.setAction("PASS"); } d.setRiskScore(total); return d; }
private void collectSoftTags(CampaignFraudDecision d, CampaignOrder o) { if (o.getAccountAgeDays() < 1) { d.addRiskTag("新注册账户(不满 1 天)"); } else if (o.getAccountAgeDays() < 7) { d.addRiskTag("注册未满 7 天"); } if (o.getOrderCount1h() >= 5) { d.addRiskTag("1 小时内高频下单"); } if (o.getOrderCount24h() >= 15) { d.addRiskTag("24 小时下单频次异常"); } if (o.getDeviceAccountCount24h() >= 5) { d.addRiskTag("同设备关联多账户"); } if (o.getIpAccountCount24h() >= 10) { d.addRiskTag("同 IP 关联多账户"); } if (!o.isRealName()) { d.addRiskTag("账户未实名认证"); } if (!o.isPaymentConsistent()) { d.addRiskTag("付款人与下单人不一致"); } if (o.isVirtualAddress()) { d.addRiskTag("虚拟/集中代收收货地址"); } if (o.getCouponOrderCount7d() >= 10) { d.addRiskTag("7 天用券订单异常集中"); } if (o.getEmptyPackageRate() >= 50) { d.addRiskTag("历史空包率高"); } if (o.getAmountDeviationPercent() >= 400) { d.addRiskTag("客单价严重偏离历史水平"); } }}CampaignFraudServiceTest.java
Section titled “CampaignFraudServiceTest.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.CampaignFraudDecision;import com.github.paohaijiao.demo.model.CampaignOrder;import com.github.paohaijiao.demo.service.CampaignFraudService;import com.github.paohaijiao.xml.JQuickJava;import org.junit.Assert;import org.junit.Before;import org.junit.Test;
/** * 场景 26:营销活动订单防刷单 * <p> * 覆盖 6 组订单形态:正常老客放行、新号高频临界 40 分复核、黑产满分拦截、 * 秒杀脚本硬规则拦截、疑似刷券 54 分复核、70 分纯评分拦截边界。 */public class CampaignFraudServiceTest {
private CampaignFraudService service;
@Before public void setUp() { CampaignFraudRules rules = JQuickJava.create() .createApi(CampaignFraudRules.class, "demo/campaign-fraud.xml"); this.service = new CampaignFraudService(rules); }
/** * 资深实名老客、行为平稳 → 0 分放行 */ @Test public void shouldPassNormalLoyalCustomer() { CampaignOrder o = base("ORD001"); o.setAccountAgeDays(400); o.setOrderCount24h(1);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(0, d.getRiskScore()); Assert.assertEquals("PASS", d.getAction()); Assert.assertFalse(d.isHardRuleHit()); }
/** * 注册 3 天新号、4 单/小时、10 单/日、设备 2 账户、3 张券 → 恰好 40 分复核 */ @Test public void shouldReviewNewAccountAtScoreForty() { CampaignOrder o = base("ORD002"); o.setAccountAgeDays(3); o.setOrderCount1h(4); o.setOrderCount24h(10); o.setDeviceAccountCount24h(2); o.setIpAccountCount24h(4); o.setCouponOrderCount7d(5); o.setAmountDeviationPercent(50);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(40, d.getRiskScore()); Assert.assertEquals("REVIEW", d.getAction()); Assert.assertFalse(d.isHardRuleHit()); Assert.assertTrue(d.getScoreDetails().contains("账户龄: 14 / 20")); Assert.assertTrue(d.getRiskTags().contains("注册未满 7 天")); }
/** * 黑产设备池全套特征 → 100 分并命中硬规则拦截 */ @Test public void shouldRejectFullFraudProfile() { CampaignOrder o = base("ORD003"); o.setAccountAgeDays(0); o.setOrderCount1h(8); o.setOrderCount24h(20); o.setDeviceAccountCount24h(6); o.setIpAccountCount24h(12); o.setCouponOrderCount7d(12); o.setEmptyPackageRate(60); o.setAmountDeviationPercent(500); o.setRealName(false); o.setPaymentConsistent(false); o.setVirtualAddress(true);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(100, d.getRiskScore()); Assert.assertEquals("REJECT", d.getAction()); Assert.assertTrue(d.isHardRuleHit()); Assert.assertTrue(d.getRiskTags().contains("黑产设备池:未实名账户聚集同一设备")); Assert.assertTrue(d.getRiskTags().contains("同设备关联多账户")); Assert.assertTrue(d.getRiskTags().contains("历史空包率高")); }
/** * 其余特征干净,但 1 小时 6 单且客单偏离 450% → 秒杀脚本硬规则拦截 */ @Test public void shouldRejectScriptPatternByHardRule() { CampaignOrder o = base("ORD004"); o.setAccountAgeDays(500); o.setOrderCount1h(6); o.setOrderCount24h(6); o.setAmountDeviationPercent(450);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(30, d.getRiskScore()); Assert.assertEquals("REJECT", d.getAction()); Assert.assertTrue(d.isHardRuleHit()); Assert.assertTrue(d.getRiskTags().contains("秒杀脚本特征:1 小时高频下单且客单价严重偏离")); }
/** * 注册 5 天、设备 3 账户 IP 5 个、5 张券空包率 25%、偏离 220% → 54 分复核 */ @Test public void shouldReviewCouponAbuseAtScoreFiftyFour() { CampaignOrder o = base("ORD005"); o.setAccountAgeDays(5); o.setOrderCount1h(3); o.setOrderCount24h(9); o.setDeviceAccountCount24h(3); o.setIpAccountCount24h(5); o.setCouponOrderCount7d(5); o.setEmptyPackageRate(25); o.setAmountDeviationPercent(220);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(54, d.getRiskScore()); Assert.assertEquals("REVIEW", d.getAction()); Assert.assertTrue(d.getScoreDetails().contains("设备与IP聚集: 12 / 20")); Assert.assertTrue(d.getScoreDetails().contains("券单与空包: 6 / 10")); }
/** * 恰好 70 分:未命中任何硬规则,纯评分线拦截 */ @Test public void shouldRejectAtExactScoreSeventy() { CampaignOrder o = base("ORD006"); o.setAccountAgeDays(5); o.setOrderCount1h(6); o.setOrderCount24h(20); o.setDeviceAccountCount24h(2); o.setIpAccountCount24h(4); o.setCouponOrderCount7d(5); o.setEmptyPackageRate(0); o.setAmountDeviationPercent(220); o.setRealName(false); o.setPaymentConsistent(false); o.setVirtualAddress(true);
CampaignFraudDecision d = service.check(o);
Assert.assertEquals(70, d.getRiskScore()); Assert.assertEquals("REJECT", d.getAction()); Assert.assertFalse(d.isHardRuleHit()); Assert.assertTrue(d.getRiskTags().contains("付款人与下单人不一致")); Assert.assertTrue(d.getRiskTags().contains("虚拟/集中代收收货地址")); }
private CampaignOrder base(String orderId) { CampaignOrder o = new CampaignOrder(); o.setOrderId(orderId); o.setUserId("U10001"); o.setCampaignId("CAMP2026"); o.setAccountAgeDays(365); o.setOrderCount1h(0); o.setOrderCount24h(0); o.setDeviceAccountCount24h(1); o.setIpAccountCount24h(1); o.setCouponOrderCount7d(0); o.setEmptyPackageRate(0); o.setAmountDeviationPercent(0); o.setRealName(true); o.setPaymentConsistent(true); o.setVirtualAddress(false); return o; }}Run & verified results
Section titled “Run & verified results”Execute at the project root:
mvn test -Dtest=CampaignFraudServiceTestTest coverage (6 @Test methods in total):
| Test method | Business profile | Key assertions / expected decision |
|---|---|---|
| shouldPassNormalLoyalCustomer | 400-day real-name loyal customer, only 1 order in 24h, stable behavior | riskScore=0, action=PASS, hardRuleHit=false |
| shouldReviewNewAccountAtScoreForty | 3-day-old account, 4 orders/hour, 10 orders/24h, 2 accounts on device, 5 coupons, 50% deviation | riskScore=40, action=REVIEW, no hard rule hit; details contain “账户龄: 14 / 20”; tags contain “注册未满 7 天” |
| shouldRejectFullFraudProfile | 0-day account, 8 orders/hour, 6 device accounts, 12 IP accounts, 12 coupons, 60% empty-package rate, 500% deviation, not real-name / inconsistent / virtual address | riskScore=100, action=REJECT, hardRuleHit=true; contains “黑产设备池”, “同设备关联多账户”, “历史空包率高” tags |
| shouldRejectScriptPatternByHardRule | Otherwise clean profile (500-day customer), only 6 orders in 1 hour and 450% amount deviation | riskScore=30 but action=REJECT, hardRuleHit=true; tag contains “秒杀脚本特征:1 小时高频下单且客单价严重偏离” |
| shouldReviewCouponAbuseAtScoreFiftyFour | 5-day registration, 3 device accounts, 5 IP accounts, 5 coupons, 25% empty-package rate, 220% deviation | riskScore=54, action=REVIEW; details contain “设备与IP聚集: 12 / 20” and “券单与空包: 6 / 10” |
| shouldRejectAtExactScoreSeventy | Exactly 70 points but no hard-rule combination satisfied | action=REJECT, hardRuleHit=false, verifying the pure-score boundary; tags contain “付款人与下单人不一致” and “虚拟/集中代收收货地址” |
Technical highlights
Section titled “Technical highlights”- Externalized rules with hot update: all six-dimension threshold tiers live in campaign-fraud.xml. Tuning scores or adding tiers during promotions only requires editing the XML and rebuilding the proxy; the trading system needs no re-release.
- One method per caliber, independently tunable and auditable: the six scoring methods each own one dimension, and the Service writes each dimension’s score into scoreDetails (e.g. “设备与IP聚集: 12 / 20”), making the decision chain traceable dimension by dimension.
- Java-side capping and upfront hard veto: the total is clamped to 100; the device-pool and flash-sale-script hard rules short-circuit outside the scoring, so even 30 points can go straight to REJECT — hard veto and soft scoring are cleanly layered.
- Two-tier crossed calibers: frequency (1-hour tier + 24-hour tier), device clustering (device tier + IP tier) and coupon (coupon tier + empty-package tier) each evaluate two independent tiers within one method and add them, so composite risk signals never lose a tier.
- Positive branches avoid negation: the scoreIdentity script expresses non-real-name/inconsistency penalties with “positive condition takes the empty branch, else adds points”, avoiding
!negation in the script — the caliber is straightforward and hard to get wrong. @Paramname binding + proxy reuse: parameter names are the XML formal parameter names, decoupling signature order from the script; the rule interface is implemented via an ASM dynamic proxy, with no standalone process or heavy dependencies, embeddable directly into JDK 8 legacy systems and domestic-innovation environments.