跳转到内容

案例 26:订单防刷单风控校验

本案例对应 JQuick-Java 仓库 USE-CASES 场景 26「订单防刷单风控校验」,属于电商风控领域。在秒杀、满减、发券等营销活动期间,对每笔活动订单做实时防刷单决策:六个维度独立评分汇总为 0-100 的风险总分,再叠加 Java 侧两条硬规则前置短路,最终输出放行(PASS)、人工复核(REVIEW)或拦截(REJECT)。

大促期间黑产常使用批量注册的新号、设备池/IP 池聚集账户、秒杀脚本高频下单等手段套取优惠券、补贴与限量商品。平台需要在订单链路上对每笔营销活动订单实时采集风险要素,并在毫秒内给出处置结论。

评分口径拆成六个互相独立的维度,各有分值上限:账户龄风险(0-20)、下单频次风险(0-25,含 1 小时档与 24 小时档两段叠加)、设备/IP 聚集风险(0-20,设备档与 IP 档叠加)、身份与地址风险(0-15)、券单与空包风险(0-10,用券订单档与历史空包率档叠加)、客单价偏离风险(0-10)。六维得分相加并在 Service 侧封顶到 100 分。

除软评分外,Service 还在 Java 侧维护两条不可被分数稀释的硬规则:未实名账户且 24 小时内同设备关联账户数达到 5 个,判定为「黑产设备池」;1 小时内下单达到 5 单且本单客单价偏离历史水平达到 400%,判定为「秒杀脚本特征」。命中任一硬规则,或总分达到 70 分则拦截;40 至 69 分转人工复核;其余放行。

决策输出物为 CampaignFraudDecision:风险总分、处置动作、是否命中硬规则、六个维度的逐条得分明细(scoreDetails),以及按要素阈值收集的风险标签(riskTags,如「注册未满 7 天」「同设备关联多账户」「历史空包率高」等),供风控运营复核与对客解释使用。

  • 大促攻防以周甚至天为单位变化,黑产换一批账号与设备特征,评分档位和硬规则阈值若硬编码在交易系统里,每次调参都要走全量发版,赶不上黑产节奏。
  • 六个风险维度需要独立调参、灰度甚至按活动差异化配置(如券单维度在发券日临时收紧),写在同一段 if-else 中无法单独维护与回溯。
  • 硬否决与软评分必须分层:秒杀脚本哪怕只有 30 分也必须拦截,传统把所有条件混在一张决策表里的做法,很难解释「分数不高为什么也被拦」。
  • 商家或用户申诉时,需要逐维度还原得分依据与命中标签,黑盒式风控结论无法审计、无法举证。
  • 老交易中台多运行在 JDK 8 与信创配套 JDK 上,难以引入需要独立部署、带重型仓储与控制台的规则平台。
  • 设备指纹、IP 聚集、空包率等信号来自不同数据域,聚合后要求在下单链路毫秒级返回,不能承受跨进程规则服务的网络开销。

本案例采用「入参模型 + 决策模型 + 规则接口 + XML 规则脚本 + Service 编排 + JUnit 测试」六件套结构:规则接口只声明六维评分口径,阈值分档全部外置在 XML,Service 负责汇总、封顶、硬规则短路与标签组装。

层次 职责 对应文件
领域模型层 承载营销订单刷单风险要素,输出分数、动作、明细与风险标签 CampaignOrder.java、CampaignFraudDecision.java
规则接口层 声明六个评分方法,入参以 @Param 按名绑定 CampaignFraudRules.java
规则脚本层 六维阈值分档与分值全部外置,支持热更新 campaign-fraud.xml
编排与测试层 汇总评分、100 分封顶、硬规则前置短路、标签装配;6 组订单形态回归 CampaignFraudService.java、CampaignFraudServiceTest.java
规则方法 口径 分值/输出
scoreAccountAge 按账户注册天分档:不满 1 天 20 分;不满 7 天 14 分;不满 30 天 8 分;不满 90 天 3 分;其余 0 分 0-20
scoreFrequency 1 小时档(≥5 单加 18、≥3 单加 12、≥1 单加 5)与 24 小时档(≥15 单加 7、≥8 单加 4、≥3 单加 2)相加 0-25
scoreDevice 同设备账户档(≥5 加 14、≥3 加 8、≥2 加 4)与同 IP 账户档(≥10 加 6、≥5 加 4、≥3 加 2)相加 0-20
scoreIdentity 未实名加 8 分;付款人与下单人不一致加 4 分;虚拟/集中代收地址加 3 分 0-15
scoreCoupon 7 天用券订单档(≥10 加 6、≥5 加 4、≥2 加 2)与历史空包率档(≥50% 加 4、≥20% 加 2)相加 0-10
scoreDeviation 客单价偏离率 ≥400% 加 10、≥200% 加 6、≥100% 加 3,否则 0 0-10

将下列文件按原始包名放入 JQuick-Java 测试工程:两个模型放入 src/test/java/com/github/paohaijiao/demo/model/,规则接口与测试类放入 src/test/java/com/github/paohaijiao/demo/,Service 放入 src/test/java/com/github/paohaijiao/demo/service/,XML 脚本放入 src/test/resources/demo/。在工程根目录执行 mvn test -Dtest=CampaignFraudServiceTest 即可运行。

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 营销活动订单的刷单风险要素
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public 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;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 营销刷单风控决策
*/
@Data
public 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);
}
}
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);
}
<?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>
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("客单价严重偏离历史水平");
}
}
}
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;
}
}

在工程根目录执行:

Terminal window
mvn test -Dtest=CampaignFraudServiceTest

测试覆盖形态(共 6 个 @Test 方法):

测试方法 业务形态 关键断言/期望决策
shouldPassNormalLoyalCustomer 400 天资深实名老客、24 小时仅 1 单,行为平稳 riskScore=0、action=PASS、hardRuleHit=false
shouldReviewNewAccountAtScoreForty 注册 3 天新号、1 小时 4 单、24 小时 10 单、设备 2 账户、5 张券、偏离 50% riskScore=40、action=REVIEW、未命中硬规则;明细含「账户龄: 14 / 20」;标签含「注册未满 7 天」
shouldRejectFullFraudProfile 0 天新号、8 单/小时、设备 6 账户、IP 12 账户、12 张券、空包率 60%、偏离 500%、未实名/不一致/虚拟地址 riskScore=100、action=REJECT、hardRuleHit=true;含「黑产设备池」「同设备关联多账户」「历史空包率高」标签
shouldRejectScriptPatternByHardRule 其余特征干净(500 天老客),仅 1 小时 6 单且客单偏离 450% riskScore=30 但 action=REJECT、hardRuleHit=true;标签含「秒杀脚本特征:1 小时高频下单且客单价严重偏离」
shouldReviewCouponAbuseAtScoreFiftyFour 注册 5 天、设备 3 账户、IP 5 账户、5 张券、空包率 25%、偏离 220% riskScore=54、action=REVIEW;明细含「设备与IP聚集: 12 / 20」「券单与空包: 6 / 10」
shouldRejectAtExactScoreSeventy 恰好 70 分但不满足任何硬规则组合 action=REJECT、hardRuleHit=false,验证纯评分边界;标签含「付款人与下单人不一致」「虚拟/集中代收收货地址」
  • 规则外置热更新:六维阈值档位全部写在 campaign-fraud.xml,大促期间调分值、加档位只需改 XML 重建代理,交易系统无需重新发版。
  • 一方法一口径、可独立调参与审计:六个评分方法各管一个维度,Service 把每维得分写入 scoreDetails(如「设备与IP聚集: 12 / 20」),决策链路逐维可回溯。
  • Java 侧封顶与硬否决前置:总分 clamp 到 100;黑产设备池、秒杀脚本两条硬规则在评分之外短路,30 分也能直接 REJECT,硬否决与软评分分层清晰。
  • 双档交叉相乘/叠加口径:频次(1 小时档 + 24 小时档)、设备聚集(设备档 + IP 档)、券单(券单档 + 空包档)均在一个方法内两段独立判档后相加,复合风险信号不丢档。
  • 正向分支规避取反:scoreIdentity 脚本以「命中正向条件走空分支、else 加分」的写法表达未实名/不一致扣分,避免在脚本中书写 ! 取反,口径直白不易写错。
  • @Param 按名绑定 + 代理复用:参数名即 XML 形参名,签名顺序与脚本解耦;规则接口由 ASM 动态代理实现,无独立进程与重型依赖,JDK 8 遗留系统与信创环境可直接嵌入。