From 319517af6acf49a0ddd5d84db5e8d89c29d68a6f Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 08 Jul 2026 11:49:37 +0800
Subject: [PATCH] fix(gateApi): 统一返回成功状态的数据格式
---
src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java | 135 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 135 insertions(+), 0 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
new file mode 100644
index 0000000..480b87c
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
@@ -0,0 +1,135 @@
+package com.xcong.excoin.modules.gateApi;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Gate 配置持久化服务 — 按 apiKey 分文件存储。
+ * 文件路径: config/gate-config-{md5(apiKey)}.json
+ */
+@Slf4j
+@Service
+public class GateConfigPersistenceService {
+
+ private static final String CONFIG_DIR = "config";
+ private static final String CONFIG_PREFIX = "gate-config-";
+ private static final String JSON_SUFFIX = ".json";
+
+ private final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
+
+ @PostConstruct
+ public void ensureConfigDir() {
+ File dir = new File(CONFIG_DIR);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+
+ /**
+ * 确保指定 apiKey 的配置文件存在,不存在则用默认值创建。
+ */
+ public GateConfigDTO ensureExists(String apiKey, GateConfigDTO defaults) {
+ GateConfigDTO existing = load(apiKey);
+ if (existing != null) {
+ return existing;
+ }
+ save(apiKey, defaults);
+ log.info("[GateConfig] 为 apiKey={} 创建默认配置文件", maskKey(apiKey));
+ return defaults;
+ }
+
+ /**
+ * 保存配置。
+ */
+ public void save(String apiKey, GateConfigDTO dto) {
+ try {
+ mapper.writeValue(configFile(apiKey), dto);
+ log.info("[GateConfig] 配置已保存, apiKey={}", maskKey(apiKey));
+ } catch (IOException e) {
+ log.error("[GateConfig] 保存配置失败, apiKey={}", maskKey(apiKey), e);
+ throw new RuntimeException("保存配置失败", e);
+ }
+ }
+
+ /**
+ * 加载配置,文件不存在返回 null。
+ */
+ public GateConfigDTO load(String apiKey) {
+ File file = configFile(apiKey);
+ if (!file.exists()) {
+ return null;
+ }
+ try {
+ return mapper.readValue(file, GateConfigDTO.class);
+ } catch (IOException e) {
+ log.error("[GateConfig] 读取配置失败, apiKey={}", maskKey(apiKey), e);
+ return null;
+ }
+ }
+
+ /**
+ * 从 DTO 构建 GateConfig(补齐 apiSecret)。
+ */
+ public GateConfig buildFromDTO(GateConfigDTO dto, String apiKey) {
+ return GateConfig.builder()
+ .apiKey(apiKey)
+ .apiSecret(GateWebSocketClientManager.DEFAULT_API_SECRET)
+ // 不可调参数 — 参考 GateWebSocketClientManager 顶部常量
+ .contract(GateWebSocketClientManager.DEFAULT_CONTRACT)
+ .leverage(GateWebSocketClientManager.DEFAULT_LEVERAGE)
+ .marginMode(GateWebSocketClientManager.DEFAULT_MARGIN_MODE)
+ .positionMode(GateWebSocketClientManager.DEFAULT_POSITION_MODE)
+ .restartGridSpan(GateWebSocketClientManager.DEFAULT_RESTART_GRID_SPAN)
+ .priceScale(GateWebSocketClientManager.DEFAULT_PRICE_SCALE)
+ .contractMultiplier(new BigDecimal(GateWebSocketClientManager.DEFAULT_CONTRACT_MULTIPLIER))
+ .unrealizedPnlPriceMode(GateConfig.PnLPriceMode.valueOf(GateWebSocketClientManager.DEFAULT_PNL_MODE))
+ .isProduction(true)
+ .reopenMaxRetries(GateWebSocketClientManager.DEFAULT_REOPEN_MAX_RETRIES)
+ // 可调参数 — 从 DTO 读取
+ .gridRate(nvl(dto.getGridRate(), new BigDecimal("0.005")))
+ .expectedProfit(nvl(dto.getExpectedProfit(), new BigDecimal("0.15")))
+ .maxLoss(nvl(dto.getMaxLoss(), new BigDecimal("1.5")))
+ .baseQuantity(nvl(dto.getBaseQuantity(), "2"))
+ .quantity(nvl(dto.getQuantity(), "2"))
+ .maxPositionSize(dto.getMaxPositionSize())
+ .stopLossCount(dto.getStopLossCount())
+ .build();
+ }
+
+ private File configFile(String apiKey) {
+ return new File(CONFIG_DIR, CONFIG_PREFIX + md5(apiKey) + JSON_SUFFIX);
+ }
+
+ private static String md5(String input) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
+ StringBuilder sb = new StringBuilder();
+ for (byte b : digest) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
+ } catch (NoSuchAlgorithmException e) {
+ return Integer.toHexString(input.hashCode());
+ }
+ }
+
+ private static String maskKey(String key) {
+ if (key == null || key.length() <= 8) return "***";
+ return key.substring(0, 4) + "****" + key.substring(key.length() - 4);
+ }
+
+ private static <T> T nvl(T value, T fallback) {
+ return value != null ? value : fallback;
+ }
+}
--
Gitblit v1.9.1