Skip to content

HNEconomy 不只是玩家命令插件,它还提供了一层对外经济接口,给其他插件接入。


1. 当前 API 定位

当前对外接口的重点有四层:

  1. 基础经济接口:查余额、加钱、扣钱、设余额、转账
  2. 系统资金池接口:系统资金池入账 / 出账 / 三边结算
  3. 业务接入桥:商店、拍卖等业务桥接
  4. 统计与占位符桥:余额排行、手续费、系统资金池统计与 HNCore 占位符命名空间

也就是说,别的插件不应该直接改数据库,而应该通过 HNEconomy 的服务层完成经济操作。


2. 核心接口:EconomyApi

当前核心能力包括:

  • getBalance(...)
  • has(...)
  • deposit(...)
  • withdraw(...)
  • setBalance(...)
  • transfer(...)
  • playerPay(...)
  • settleWithTreasury(...)
  • treasuryDeposit(...)
  • treasuryWithdraw(...)
  • settleShop(...)
  • isPayReceiveEnabled(...)
  • setPayReceiveEnabled(...)
  • getCurrency(...)
  • getCurrencies()

设计重点

所有资金变更类接口都要求带:

  • TransactionContext

这不是多余参数,而是为了保证每次经济变更都具备:

  • 变更类型
  • 来源
  • 原因
  • 操作人
  • 业务 ID
  • 请求幂等标记

3. 统一入口:HNEconomyAPI

对外推荐通过:

java
HNEconomyAPI.getEconomyService()

来拿经济服务。

当前还额外暴露了:

  • HNEconomyAPI.getCurrencyRegistry()
  • HNEconomyAPI.getPayAnalyticsService()
  • HNEconomyAPI.getShopEconomyBridge()
  • HNEconomyAPI.getAuctionEconomyBridge()

这意味着:

  • 业务插件可以直接读统计
  • 也可以直接复用默认商店 / 拍卖接入实现
  • 还可以直接读运行时货币注册表

4. 最小接入示例:给玩家加钱

java
import com.github.hnplugins.hneconomy.api.HNEconomyAPI;
import com.github.hnplugins.hneconomy.model.TransactionContext;
import com.github.hnplugins.hneconomy.constant.OperatorTypes;

UUID playerId = ...;

var economy = HNEconomyAPI.getEconomyService();
if (economy == null) {
    return;
}

TransactionContext context = TransactionContext.builder()
        .changeType("ACTIVITY_REWARD")
        .reasonText("活动奖励")
        .sourceType("EVENT")
        .sourceId("spring-event")
        .businessId("spring-event-2026-0001")
        .operatorType(OperatorTypes.PLUGIN)
        .operatorId("MY_PLUGIN")
        .operatorName("MyPlugin")
        .serverId("survival-1")
        .build();

var result = economy.deposit(playerId, "gold", 100L, context);

这里最重要的不是 deposit(...) 这一步本身,而是:

  • changeType 写清楚
  • sourceType / sourceId 写清楚
  • businessId 尽量每笔业务唯一
  • reasonText 让运维看得懂

金额单位提醒

当前 API 金额参数是:

  • long
  • 按货币精度 scale 存储的内部值

例如:

  • scale: 0gold100L 就表示 100
  • scale: 2 的货币里,1234L 通常表示 12.34

也就是说,API 层传的不是展示文本,而是内部账务单位

如果你在接入时直接把玩家输入的 12.34 当成 12L 传进去,就会出现精度错误。


5. 为什么强制要求 TransactionContext

因为 HNEconomy 的核心原则是:

任何余额变化,都必须可追踪。

所以在集成层,最推荐的做法是:

  • 明确写 changeType
  • 明确写 sourceType
  • 明确写 sourceId
  • 明确写 businessId
  • 明确写 reasonText

例如:

  • 商店购买
  • 拍卖结算
  • 活动奖励
  • 管理员补偿

都应该有自己清晰的上下文。


6. 商店接入接口

当前提供了:

  • ShopEconomyBridge
  • DefaultShopEconomyBridge
  • ShopSettlementRequest
  • ShopSettlementResult

当前能力

  • 扣玩家钱
  • 给玩家发钱
  • 向系统资金池结算
  • 直接用 ShopSettlementRequest 做统一结算

典型适用场景

  • NPC 商店
  • 服务器商城
  • 活动商店

最小结算思路

如果你的商店只需要“买家扣款 + 系统资金池入账”,优先考虑直接走桥接接口, 而不是自己拆成多次调用。

示例:商店购买并结算到系统资金池

java
import com.github.hnplugins.hneconomy.api.HNEconomyAPI;
import com.github.hnplugins.hneconomy.api.business.BusinessContexts;
import com.github.hnplugins.hneconomy.api.business.ShopSettlementRequest;

UUID playerId = ...;

var bridge = HNEconomyAPI.getShopEconomyBridge();
if (bridge == null) {
    return;
}

var context = BusinessContexts.shop(
        "SHOP_BUY",
        "NPC 商店购买",
        "npc-shop",
        "shop-order-0001",
        "survival-1"
);

var result = bridge.settle(new ShopSettlementRequest(
        playerId,
        "gold",
        100L,
        true,
        context
));

这里的 settleToTreasury = true 表示:

  • 玩家被扣款
  • 对应金额进入系统资金池
  • 整体作为一笔统一结算处理

7. 拍卖接入接口

当前提供了:

  • AuctionEconomyBridge
  • DefaultAuctionEconomyBridge
  • AuctionSettlementRequest
  • AuctionSettlementResult

当前能力

  • 买家付款
  • 卖家到账
  • 税费进入系统资金池
  • 三边事务化结算

最关键的一点

拍卖接入的默认实现底层会走:

java
settleWithTreasury(...)

这意味着:

  • 不需要业务插件自己拆三步
  • 不容易出现“买家扣了,卖家没到,税也没收”的半成功状态

示例:拍卖成交并收取税费

java
import com.github.hnplugins.hneconomy.api.HNEconomyAPI;
import com.github.hnplugins.hneconomy.api.business.AuctionSettlementRequest;
import com.github.hnplugins.hneconomy.api.business.BusinessContexts;

UUID buyerId = ...;
UUID sellerId = ...;

var bridge = HNEconomyAPI.getAuctionEconomyBridge();
if (bridge == null) {
    return;
}

var context = BusinessContexts.auction(
        "AUCTION_SETTLEMENT",
        "拍卖成交",
        "auction-house",
        "auction-order-0001",
        "survival-1"
);

var result = bridge.settle(new AuctionSettlementRequest(
        buyerId,
        sellerId,
        "gold",
        1000L,
        50L,
        context
));

这个例子里:

  • 买家总支出由桥接逻辑处理
  • 卖家拿到成交金额
  • taxAmount 对应的税费进入系统资金池
  • 整个流程在统一结算逻辑里完成

8. BusinessContexts 的作用

当前还提供了:

  • BusinessContexts.shop(...)
  • BusinessContexts.auction(...)

这是为了统一构造:

  • sourceType = SHOP
  • sourceType = AUCTION
  • 统一的插件操作人信息

避免不同业务插件各写各的来源字符串,最后数据库里出现一堆风格不一致的来源类型。

例子

java
var context = BusinessContexts.shop(
        "SHOP_BUY",
        "商店购买",
        "npc-shop",
        "shop-order-0001",
        "survival-1"
);

9. Vault 集成

当前 HNEconomy 可以注册为 Vault 的经济服务实现。

主要特点

  • 默认映射主货币
  • Vault 的入账 / 扣款同样落流水
  • 来源会标记为 VAULT

当前定位

更适合:

  • 给其他只认 Vault 的旧插件提供兼容层

而不是替代 HNEconomy 自己的多货币能力。


10. HNCore 占位符桥

当前 HNEconomy 提供的是 HNCore 占位符命名空间 economy

这带来两种常见用法:

A. 在 HNCore 内部模板里使用

例如 GUI、消息模板:

text
{economy.balance_formatted}
{economy.currency_display}
{economy.pay_receive_text}

B. 在 PlaceholderAPI 外部环境中使用

通过 HNCore 占位符桥访问:

text
%hncore_economy_<params>%

例如:

text
%hncore_economy_balance_gold%
%hncore_economy_balance_formatted_point%
%hncore_economy_treasury_total_fee_gold%
%hncore_economy_top_balance_name_gold_1%

最关键的一点是:

多货币占位符按 currencyId 动态解析,不需要为每个货币单独新增一套固定占位符。

完整占位符清单、动态货币模板、回退规则与排查方法,见:


11. 集成时最推荐的原则

原则 1:不要自己写余额表

直接调 HNEconomy

原则 2:不要绕过服务层直接改库

否则你会绕过:

  • 事务
  • 流水
  • 系统资金池逻辑
  • 幂等逻辑
  • 统计与告警基础

原则 3:每笔业务都带 businessId

这样后续查账、补偿、回放都会轻松很多。

原则 4:来源字段尽量统一

能复用 BusinessContexts 的地方尽量复用, 不要让同一种业务在库里混出多套 sourceType 风格。


推荐阅读

HN 系列插件文档