Administrator
3 days ago 5c3737c9718ad01a4d2a4a02c09548fcc3966438
src/main/java/com/xcong/excoin/modules/gateApi/wsHandler/AbstractPrivateChannelHandler.java
@@ -3,7 +3,6 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xcong.excoin.modules.gateApi.GateGridTradeService;
import com.xcong.excoin.modules.gateApi.wsHandler.GateChannelHandler;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
@@ -12,7 +11,22 @@
import java.nio.charset.StandardCharsets;
/**
 * 私有频道的抽象基类,封装 HMAC-SHA512 签名和认证请求构建。
 * 私有频道处理器的抽象基类。
 *
 * <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}
 *
 * @author Administrator
 */
@@ -39,44 +53,57 @@
    }
    @Override
    public String getChannelName() {
        return channelName;
    }
    public String getChannelName() { return channelName; }
    /**
     * 发送带签名的订阅请求。
     * payload: [userId, contract],auth: {method:"api_key", KEY, SIGN}
     */
    @Override
    public void subscribe(WebSocketClient ws) {
        long timeSec = System.currentTimeMillis() / 1000;
        JSONObject msg = buildAuthRequest("subscribe", buildUid(), timeSec);
        ws.send(msg.toJSONString());
        log.info("[{}] 已发送订阅请求(含认证),合约: {}", channelName, contract);
        log.info("[{}] 订阅成功, 合约:{}", channelName, contract);
    }
    /**
     * 发送带签名的取消订阅请求,与 subscribe 对称。
     */
    @Override
    public void unsubscribe(WebSocketClient ws) {
        JSONObject unsubscribeMsg = new JSONObject();
        unsubscribeMsg.put("time", System.currentTimeMillis() / 1000);
        unsubscribeMsg.put("channel", channelName);
        unsubscribeMsg.put("event", "unsubscribe");
        long timeSec = System.currentTimeMillis() / 1000;
        JSONObject msg = new JSONObject();
        msg.put("id", timeSec * 1000000 + (System.currentTimeMillis() % 1000));
        msg.put("time", timeSec);
        msg.put("channel", channelName);
        msg.put("event", "unsubscribe");
        JSONArray payload = new JSONArray();
        payload.add(contract);
        unsubscribeMsg.put("payload", payload);
        ws.send(unsubscribeMsg.toJSONString());
        log.info("[{}] 已发送取消订阅请求,合约: {}", channelName, contract);
        msg.put("payload", payload);
        JSONObject auth = new JSONObject();
        auth.put("method", "api_key");
        auth.put("KEY", apiKey);
        auth.put("SIGN", hs512Sign("unsubscribe", timeSec));
        msg.put("auth", auth);
        ws.send(msg.toJSONString());
        log.info("[{}] 取消订阅成功, 合约:{}", channelName, contract);
    }
    protected GateGridTradeService getGridTradeService() {
        return gridTradeService;
    }
    protected GateGridTradeService getGridTradeService() { return gridTradeService; }
    protected String getContract() { return contract; }
    protected String getContract() {
        return contract;
    }
    /**
     * 从策略服务获取用户 ID,用于私有频道订阅的 payload[0]。
     */
    private String buildUid() {
        return gridTradeService != null && gridTradeService.getUserId() != null
                ? String.valueOf(gridTradeService.getUserId()) : "";
    }
    /**
     * 构建认证请求 JSON。包含 id、time、channel、event、payload[auth_user_id, contract]、auth 字段。
     */
    private JSONObject buildAuthRequest(String event, String uid, long timeSec) {
        JSONObject msg = new JSONObject();
        msg.put("id", timeSec * 1000000 + (System.currentTimeMillis() % 1000));
@@ -95,6 +122,9 @@
        return msg;
    }
    /**
     * HMAC-SHA512 签名,使用 UTF-8 编码。
     */
    private String hs512Sign(String event, long timeSec) {
        try {
            String message = "channel=" + channelName + "&event=" + event + "&time=" + timeSec;