| | |
| | | import java.nio.charset.StandardCharsets; |
| | | |
| | | /** |
| | | * 私有频道处理器的抽象基类。 |
| | | * |
| | | * <h3>封装内容</h3> |
| | | * <ul> |
| | | * <li>HMAC-SHA512 签名计算(UTF-8 编码)</li> |
| | | * <li>认证请求 JSON 构建(id/time/channel/payload/auth)</li> |
| | | * <li>subscribe / unsubscribe 的默认实现(含签名)</li> |
| | | * <li>用户 ID 获取(从 {@link GateGridTradeService#getUserId()})</li> |
| | | * </ul> |
| | | * |
| | | * <h3>签名算法</h3> |
| | | * {@code SIGN = Hex(HmacSHA512(secret_utf8, "channel={channel}&event={event}&time={timeSec}"_utf8))} |
| | | * |
| | | * <h3>子类</h3> |
| | | * {@link com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionsChannelHandler}、 |
| | | * {@link com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionClosesChannelHandler} |
| | | * 私有频道 WS 处理器的抽象基类,封装 HMAC-SHA512 签名认证与订阅/取消订阅逻辑。 |
| | | * |
| | | * @author Administrator |
| | | */ |
| | |
| | | private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); |
| | | |
| | | private final String channelName; |
| | | private final String apiKey; |
| | | private final String apiSecret; |
| | | protected final String apiKey; |
| | | protected final String apiSecret; |
| | | private final String contract; |
| | | private final GateGridTradeService gridTradeService; |
| | | |
| | | private volatile boolean subscribed = false; |
| | | |
| | | public AbstractPrivateChannelHandler(String channelName, |
| | | String apiKey, String apiSecret, |
| | |
| | | this.gridTradeService = gridTradeService; |
| | | } |
| | | |
| | | /** @return 频道名称(如 "futures.positions") */ |
| | | @Override |
| | | public String getChannelName() { return channelName; } |
| | | |
| | | /** |
| | | * 发送带签名的订阅请求。 |
| | | * payload: [userId, contract],auth: {method:"api_key", KEY, SIGN} |
| | | * |
| | | * <h3>请求格式</h3> |
| | | * <pre> |
| | | * { |
| | | * "id": <唯一请求ID>, |
| | | * "time": <unix时间戳(秒)>, |
| | | * "channel":"futures.positions", |
| | | * "event": "subscribe", |
| | | * "payload":[userId, contract], |
| | | * "auth": {"method":"api_key", "KEY":<APIKEY>, "SIGN":<HMAC-SHA512签名>} |
| | | * } |
| | | * </pre> |
| | | */ |
| | | @Override |
| | | public void subscribe(WebSocketClient ws) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 发送带签名的取消订阅请求,与 subscribe 对称。 |
| | | * 发送带签名的取消订阅请求,与 subscribe 结构一致。 |
| | | * payload: [contract],无 userId(取消订阅不需要用户ID)。 |
| | | */ |
| | | @Override |
| | | public void unsubscribe(WebSocketClient ws) { |
| | |
| | | log.info("[{}] 取消订阅成功, 合约:{}", channelName, contract); |
| | | } |
| | | |
| | | /** @return 网格交易服务实例 */ |
| | | protected GateGridTradeService getGridTradeService() { return gridTradeService; } |
| | | /** @return 当前订阅的合约名称 */ |
| | | protected String getContract() { return contract; } |
| | | |
| | | /** |
| | | * 从策略服务获取用户 ID,用于私有频道订阅的 payload[0]。 |
| | | * |
| | | * @return 用户 ID 字符串,获取失败返回空字符串 |
| | | */ |
| | | private String buildUid() { |
| | | return gridTradeService != null && gridTradeService.getUserId() != null |
| | |
| | | } |
| | | |
| | | /** |
| | | * 构建认证请求 JSON。包含 id、time、channel、event、payload[auth_user_id, contract]、auth 字段。 |
| | | * 构建认证请求 JSON。 |
| | | * 包含 id、time、channel、event、payload[userId, contract]、auth 字段。 |
| | | * |
| | | * @param event 事件类型("subscribe" / "unsubscribe") |
| | | * @param uid 认证用户 ID |
| | | * @param timeSec unix 时间戳(秒) |
| | | * @return 完整的认证请求 JSONObject |
| | | */ |
| | | private JSONObject buildAuthRequest(String event, String uid, long timeSec) { |
| | | JSONObject msg = new JSONObject(); |
| | |
| | | } |
| | | |
| | | /** |
| | | * HMAC-SHA512 签名,使用 UTF-8 编码。 |
| | | * HMAC-SHA512 签名计算。 |
| | | * |
| | | * <h3>签名算法</h3> |
| | | * <pre> |
| | | * message = "channel={channelName}&event={event}&time={timeSec}" |
| | | * SIGN = Hex(HmacSHA512(apiSecret(UTF-8), message(UTF-8))) |
| | | * </pre> |
| | | * |
| | | * <h3>错误处理</h3> |
| | | * 签名计算失败时返回空字符串(日志记录错误),不抛异常, |
| | | * 避免阻塞 WebSocket 回调线程。 |
| | | * |
| | | * @param event 事件类型 |
| | | * @param timeSec unix 时间戳(秒) |
| | | * @return 十六进制签名字符串,失败返回 "" |
| | | */ |
| | | private String hs512Sign(String event, long timeSec) { |
| | | protected String hs512Sign(String event, long timeSec) { |
| | | try { |
| | | String message = "channel=" + channelName + "&event=" + event + "&time=" + timeSec; |
| | | Mac mac = Mac.getInstance("HmacSHA512"); |
| | |
| | | return ""; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean isSubscribed() { return subscribed; } |
| | | |
| | | @Override |
| | | public void setSubscribed(boolean subscribed) { this.subscribed = subscribed; } |
| | | } |