add binance sdk
This commit is contained in:
parent
46da669271
commit
cc8a2a6c3c
@ -114,6 +114,21 @@
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jfree</groupId>
|
||||
<artifactId>jfreechart</artifactId>
|
||||
<version>1.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.binance</groupId>
|
||||
<artifactId>binance-connector-java</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package top.baogutang.admin.config;
|
||||
|
||||
import com.binance.connector.client.SpotClient;
|
||||
import com.binance.connector.client.WebSocketStreamClient;
|
||||
import com.binance.connector.client.impl.SpotClientImpl;
|
||||
import com.binance.connector.client.impl.WebSocketStreamClientImpl;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import top.baogutang.common.properties.MarketCandlersProperties;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/23 : 15:45
|
||||
*/
|
||||
@Configuration
|
||||
public class SpotClientConfiguration {
|
||||
|
||||
@Resource
|
||||
private MarketCandlersProperties marketCandlersProperties;
|
||||
|
||||
@Bean(name = "spotClient")
|
||||
public SpotClient spotClient() {
|
||||
return new SpotClientImpl(marketCandlersProperties.getApiKey(),
|
||||
marketCandlersProperties.getApiSecret());
|
||||
}
|
||||
|
||||
@Bean(name = "webSocketStreamClient")
|
||||
public WebSocketStreamClient webSocketStreamClient() {
|
||||
return new WebSocketStreamClientImpl();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package top.baogutang.admin.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
import top.baogutang.admin.handlers.KlineWebSocketHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Resource
|
||||
private KlineWebSocketHandler klineWebSocketHandler;
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(klineWebSocketHandler, "/ws")
|
||||
// 配置 WebSocket 端点,并允许跨域
|
||||
.setAllowedOrigins("*");
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,6 @@ public class StaticController {
|
||||
@RequestMapping
|
||||
public String viewJsonParseHtml() {
|
||||
// 这里返回的字符串是HTML文件名(不包括扩展名)
|
||||
return "json-parse";
|
||||
return "coin";
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package top.baogutang.admin.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.baogutang.admin.domain.res.MarketCandlesRes;
|
||||
import top.baogutang.admin.services.IVirtualCoinService;
|
||||
import top.baogutang.common.domain.Results;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/23 : 17:29
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin/virtualCoin")
|
||||
public class VirtualCoinController {
|
||||
|
||||
@Resource
|
||||
private IVirtualCoinService virtualCoinService;
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@GetMapping("/uiKline")
|
||||
public Results<List<String[]>> uiKline(@RequestParam(name = "symbol") String symbol,
|
||||
@RequestParam(name = "interval") String interval) {
|
||||
return Results.ok(virtualCoinService.uiKline(symbol, interval));
|
||||
}
|
||||
|
||||
@GetMapping("/uiKlineData")
|
||||
public Results<List<MarketCandlesRes.KLineData>> uiKlineData(@RequestParam(name = "symbol") String symbol,
|
||||
@RequestParam(name = "interval") String interval) {
|
||||
List<MarketCandlesRes.KLineData> kLineDataList = virtualCoinService.uiKline(symbol, interval).stream()
|
||||
.sorted(Collections.reverseOrder(Comparator.comparingLong(data -> Long.parseLong(data[0]))))
|
||||
.limit(5)
|
||||
.map(data -> MarketCandlesRes.KLineData.newInstance(data, DATE_FORMAT))
|
||||
.collect(Collectors.toList());
|
||||
return Results.ok(kLineDataList);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package top.baogutang.admin.domain.res;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/22 : 10:06
|
||||
*/
|
||||
@Data
|
||||
public class MarketCandlesRes implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3375737475097909682L;
|
||||
|
||||
@JsonProperty("from")
|
||||
private String from;
|
||||
|
||||
@JsonProperty("klines")
|
||||
private List<KLineData> kLineDataList;
|
||||
|
||||
|
||||
@Data
|
||||
public static class KLineData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 522558542163537197L;
|
||||
|
||||
|
||||
// 开盘价格
|
||||
@JsonProperty("开盘")
|
||||
private BigDecimal openPrice;
|
||||
|
||||
// 最高价格
|
||||
@JsonProperty("最高")
|
||||
private BigDecimal highestPrice;
|
||||
|
||||
// 最低价格
|
||||
@JsonProperty("最低")
|
||||
private BigDecimal lowestPrice;
|
||||
|
||||
// 收盘价格
|
||||
@JsonProperty("收盘")
|
||||
private BigDecimal closePrice;
|
||||
|
||||
// K线
|
||||
@JsonProperty("timestamp")
|
||||
private Long timestamp;
|
||||
|
||||
@JsonProperty("时间")
|
||||
private String date;
|
||||
|
||||
public static KLineData newInstance(String[] data, SimpleDateFormat dateFormat) {
|
||||
KLineData kLineData = new KLineData();
|
||||
kLineData.setOpenPrice(new BigDecimal(data[1]));
|
||||
kLineData.setHighestPrice(new BigDecimal(data[2]));
|
||||
kLineData.setLowestPrice(new BigDecimal(data[3]));
|
||||
kLineData.setClosePrice(new BigDecimal(data[4]));
|
||||
kLineData.setDate(dateFormat.format(new Date(Long.parseLong(data[0]))));
|
||||
return kLineData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package top.baogutang.admin.handlers;
|
||||
|
||||
import com.binance.connector.client.WebSocketStreamClient;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.socket.*;
|
||||
import top.baogutang.common.utils.JacksonUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class KlineWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
@Resource
|
||||
private WebSocketStreamClient webSocketStreamClient;
|
||||
|
||||
private final Map<String, WebSocketSession> webSocketMap = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile Integer connectionId;
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
|
||||
//连接成功时调用该方法
|
||||
log.info(">>>>>>>>>>WebSocket connected:{}<<<<<<<<<<", webSocketSession.getId());
|
||||
webSocketMap.put(webSocketSession.getId(), webSocketSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
|
||||
// 获取客户端发送的消息
|
||||
log.info(">>>>>>>>>>客户端ID:{} 发送消息:{}<<<<<<<<<<", webSocketSession.getId(), webSocketMessage.getPayload());
|
||||
KlineMessage message = JacksonUtil.fromJson(webSocketMessage.getPayload().toString(), KlineMessage.class);
|
||||
if (Objects.nonNull(connectionId)) {
|
||||
webSocketStreamClient.closeConnection(connectionId);
|
||||
}
|
||||
// 使用 Binance Connector 开启 WebSocket 连接
|
||||
connectionId = webSocketStreamClient.klineStream(message.getSymbol(), message.getInterval(), event -> {
|
||||
log.info(">>>>>>>>>>binance event:{}<<<<<<<<<<", event);
|
||||
// 将从 Binance 接收到的数据转发给前端
|
||||
TextMessage textMessage = new TextMessage(event);
|
||||
|
||||
// 推送消息给所有连接的客户端
|
||||
webSocketMap.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> entry.getValue().isOpen())
|
||||
.forEach(entry -> {
|
||||
try {
|
||||
entry.getValue().sendMessage(textMessage);
|
||||
} catch (IOException e) {
|
||||
log.error(">>>>>>>>>>消息发送错误:{}<<<<<<<<<<", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
});
|
||||
log.info(">>>>>>>>>启动了 Binance WebSocket 连接: {} , 监控交易对: {} 时间间隔: {}<<<<<<<<", connectionId, message.getSymbol(), message.getInterval());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
|
||||
//发生错误时调用该方法
|
||||
log.error(">>>>>>>>>>WebSocket error: {}<<<<<<<<<<", throwable.getMessage(), throwable);
|
||||
webSocketSession.close(CloseStatus.SERVER_ERROR);
|
||||
webSocketMap.remove(webSocketSession.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
|
||||
//连接关闭时调用该方法
|
||||
log.info(">>>>>>>>>>WebSocket closed:{}<<<<<<<<<<", webSocketSession.getId());
|
||||
webSocketMap.remove(webSocketSession.getId());
|
||||
if (CollectionUtils.isEmpty(webSocketMap)) {
|
||||
log.info(">>>>>>>>>>client session all closed!<<<<<<<<<<");
|
||||
webSocketStreamClient.closeAllConnections();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPartialMessages() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class KlineMessage implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3288071423561766084L;
|
||||
|
||||
private String symbol;
|
||||
|
||||
private String interval;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
package top.baogutang.admin.schedule;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.binance.connector.client.SpotClient;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.IJobHandler;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.baogutang.admin.utils.DingTalkMsgPushUtils;
|
||||
import top.baogutang.admin.utils.OkCoinKLineUtil;
|
||||
import top.baogutang.common.domain.BinanceResults;
|
||||
import top.baogutang.common.utils.JacksonUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @description: OKCOIN K线数据
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/22 : 09:52
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RefreshScope
|
||||
public class OkCoinMarketCandlesHandler extends IJobHandler {
|
||||
|
||||
@Resource
|
||||
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
|
||||
|
||||
@Resource
|
||||
private SpotClient spotClient;
|
||||
|
||||
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
|
||||
|
||||
@Override
|
||||
@XxlJob("marketCandlesHandler")
|
||||
public ReturnT<String> execute(String params) throws Exception {
|
||||
|
||||
|
||||
if (StringUtils.isBlank(params)) {
|
||||
log.error(">>>>>>>>>>job params is null!<<<<<<<<<<");
|
||||
return ReturnT.FAIL;
|
||||
}
|
||||
JobParams jobParams = JacksonUtil.fromJson(params, JobParams.class);
|
||||
if (Objects.isNull(jobParams)) {
|
||||
log.error(">>>>>>>>>>job params is null!<<<<<<<<<<");
|
||||
return ReturnT.FAIL;
|
||||
}
|
||||
String interval = jobParams.getInterval();
|
||||
Integer limit = jobParams.getLimit();
|
||||
Arrays.stream(jobParams.getSymbolList().split(","))
|
||||
.forEach(symbol -> {
|
||||
KLinesRequestParameters kLinesRequestParameters = new KLinesRequestParameters(symbol, interval, limit);
|
||||
Map<String, Object> parameters = BeanUtil.beanToMap(kLinesRequestParameters);
|
||||
String resultStr = spotClient.createMarket().uiKlines(parameters);
|
||||
if (StringUtils.isBlank(resultStr)) {
|
||||
log.error(">>>>>>>>>>request result is null!<<<<<<<<<<");
|
||||
return;
|
||||
}
|
||||
List<String[]> kLineDataList = JacksonUtil.fromJson(resultStr, new TypeReference<List<String[]>>() {
|
||||
});
|
||||
String[] kLineData = kLineDataList.get(0);
|
||||
log.info(">>>>>>>>>>当前:【{}】 开盘价:【{}】 最高价:【{}】最低价:【{}】收盘价:【{}】<<<<<<<<<<", DATE_FORMAT.format(new Date(Long.parseLong(kLineData[0]))), kLineData[1], kLineData[2], kLineData[3], kLineData[4]);
|
||||
kLineDataList = kLineDataList.stream()
|
||||
.sorted(Comparator.comparing(data -> data[0], Comparator.reverseOrder()))
|
||||
.limit(5)
|
||||
.collect(Collectors.toList());
|
||||
String markdownContent = OkCoinKLineUtil.getCoinMarkdownContent(symbol, kLineDataList);
|
||||
dingTalkMsgPushUtils.robotMarkdownMsg("交易产品K线数据", markdownContent);
|
||||
|
||||
});
|
||||
|
||||
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class JobParams {
|
||||
|
||||
// BTCUSDT,ETHUSDT
|
||||
private String symbolList;
|
||||
|
||||
// 时间粒度
|
||||
// 1s, 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
|
||||
private String interval;
|
||||
|
||||
// 默认 500; 最大值 1000
|
||||
private Integer limit;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class KLinesRequestParameters {
|
||||
|
||||
// BTCUSDT,ETHUSDT
|
||||
private String symbol;
|
||||
|
||||
// 1s, 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
|
||||
private String interval;
|
||||
|
||||
// 默认 500; 最大值 1000
|
||||
private Integer limit;
|
||||
|
||||
public KLinesRequestParameters(String symbol, String interval, Integer limit) {
|
||||
this.symbol = symbol;
|
||||
this.interval = interval;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public KLinesRequestParameters(String symbol, String interval) {
|
||||
this.symbol = symbol;
|
||||
this.interval = interval;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package top.baogutang.admin.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/23 : 17:50
|
||||
*/
|
||||
public interface IVirtualCoinService {
|
||||
|
||||
List<String[]> uiKline(String symbol, String interval);
|
||||
|
||||
// void startWebSocket(String symbol, String interval);
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package top.baogutang.admin.services.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.binance.connector.client.SpotClient;
|
||||
import com.binance.connector.client.WebSocketStreamClient;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.baogutang.admin.schedule.OkCoinMarketCandlesHandler;
|
||||
import top.baogutang.admin.services.IVirtualCoinService;
|
||||
import top.baogutang.common.utils.JacksonUtil;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/23 : 17:50
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class VirtualCoinServiceImpl implements IVirtualCoinService {
|
||||
|
||||
@Resource
|
||||
private SpotClient spotClient;
|
||||
|
||||
@Resource
|
||||
private WebSocketStreamClient webSocketStreamClient;
|
||||
|
||||
|
||||
@Override
|
||||
public List<String[]> uiKline(String symbol, String interval) {
|
||||
OkCoinMarketCandlesHandler.KLinesRequestParameters kLinesRequestParameters = new OkCoinMarketCandlesHandler.KLinesRequestParameters(symbol, interval);
|
||||
Map<String, Object> parameters = BeanUtil.beanToMap(kLinesRequestParameters, Boolean.FALSE, Boolean.TRUE);
|
||||
String resultStr = spotClient.createMarket().uiKlines(parameters);
|
||||
if (StringUtils.isBlank(resultStr)) {
|
||||
log.error(">>>>>>>>>>request result is null!<<<<<<<<<<");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return JacksonUtil.fromJson(resultStr, new TypeReference<List<String[]>>() {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -161,6 +161,30 @@ public class DingTalkMsgPushUtils {
|
||||
log.info(">>>>>>>>>>robot msg send request:{}, response:{}<<<<<<<<<<", JacksonUtil.toJson(request), JacksonUtil.toJson(response));
|
||||
}
|
||||
|
||||
public void robotMarkdownMsg(String markdownTitle, String markdownContent) {
|
||||
// 计算签名
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String sign = this.sign(timestamp);
|
||||
String url = robotWebhookUrl + "×tamp=" + timestamp + "&sign=" + sign;
|
||||
DingTalkClient client = new DefaultDingTalkClient(url);
|
||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
||||
at.setIsAtAll(false);
|
||||
request.setAt(at);
|
||||
request.setMsgtype(DingTalkMsgTypeEnum.MARKDOWN.getType());
|
||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
||||
markdown.setTitle(markdownTitle);
|
||||
markdown.setText(markdownContent);
|
||||
request.setMarkdown(markdown);
|
||||
OapiRobotSendResponse response = null;
|
||||
try {
|
||||
response = client.execute(request);
|
||||
} catch (Exception e) {
|
||||
log.error(">>>>>>>>>>robot msg send error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||
}
|
||||
log.info(">>>>>>>>>>robot msg send request:{}, response:{}<<<<<<<<<<", JacksonUtil.toJson(request), JacksonUtil.toJson(response));
|
||||
}
|
||||
|
||||
private String sign(Long timestamp) {
|
||||
try {
|
||||
String stringToSign = timestamp + "\n" + secret;
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package top.baogutang.admin.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/22 : 10:42
|
||||
*/
|
||||
@Slf4j
|
||||
public class OkCoinKLineUtil {
|
||||
|
||||
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public static String getCoinMarkdownContent(String instId, List<String[]> candleDataList) {
|
||||
if (CollectionUtils.isEmpty(candleDataList)) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder markdownBuilder = new StringBuilder();
|
||||
|
||||
// 添加标题
|
||||
markdownBuilder.append("## ")
|
||||
.append(instId)
|
||||
.append("市场价格数据\n\n");
|
||||
|
||||
// 添加表头
|
||||
markdownBuilder.append("| 时间 | 开盘价 | 最高价 | 最低价 | 收盘价 |\n");
|
||||
markdownBuilder.append("|--------------------------------|----------------------------|---------------------------|---------------------------|---------------------------|\n");
|
||||
|
||||
// 添加每一行数据
|
||||
for (String[] candle : candleDataList) {
|
||||
markdownBuilder.append("| ")
|
||||
.append(DATE_FORMAT.format(new Date(Long.parseLong(candle[0])))).append(" | ")
|
||||
.append(String.format("%-30f", Double.parseDouble(candle[1]))).append(" | ")
|
||||
.append(String.format("%-24f", Double.parseDouble(candle[2]))).append(" | ")
|
||||
.append(String.format("%-24f", Double.parseDouble(candle[3]))).append(" | ")
|
||||
.append(String.format("%-24f", Double.parseDouble(candle[4]))).append(" |\n");
|
||||
}
|
||||
|
||||
return markdownBuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -11,7 +11,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||
@ -27,11 +27,11 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="asyncFileAppender" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<queueSize>10000</queueSize>
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<appender-ref ref="file"/>
|
||||
</appender>
|
||||
<!-- <appender name="asyncFileAppender" class="ch.qos.logback.classic.AsyncAppender">-->
|
||||
<!-- <queueSize>10000</queueSize>-->
|
||||
<!-- <discardingThreshold>0</discardingThreshold>-->
|
||||
<!-- <appender-ref ref="file"/>-->
|
||||
<!-- </appender>-->
|
||||
|
||||
<!-- 读取配置文件信息(交由各项目指定) -->
|
||||
<property name="active" value="${spring.profiles.active:-test}" />
|
||||
@ -43,42 +43,6 @@
|
||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
||||
|
||||
<!-- 外网 -->
|
||||
<appender name="ONLINE-OUT" class="com.aliyun.openservices.log.logback.LoghubAppender">
|
||||
<!--必选项 -->
|
||||
<endpoint>cn-shanghai.log.aliyuncs.com</endpoint>
|
||||
<accessKeyId>LTAI5tRN9T5Tz1QExcSpUaBc</accessKeyId>
|
||||
<accessKey>LniIMK15XEOc6Nn5mOrtX399FkVfQd</accessKey>
|
||||
<projectName>baogutang</projectName>
|
||||
<logstore>${appEnv}</logstore>
|
||||
|
||||
<!-- 可选项 -->
|
||||
<topic>${appId}</topic>
|
||||
<!-- <source>source1</source> -->
|
||||
|
||||
<!-- 可选项 详见 '参数说明' -->
|
||||
<packageTimeoutInMS>3000</packageTimeoutInMS>
|
||||
<logsCountPerPackage>4096</logsCountPerPackage>
|
||||
<logsBytesPerPackage>3145728</logsBytesPerPackage>
|
||||
<memPoolSizeInByte>104857600</memPoolSizeInByte>
|
||||
<retryTimes>3</retryTimes>
|
||||
<maxIOThreadSizeInPool>8</maxIOThreadSizeInPool>
|
||||
|
||||
<!-- 可选项 设置时区 -->
|
||||
<timeZone>Asia/Shanghai</timeZone>
|
||||
<!-- 可选项 设置时间格式 -->
|
||||
<timeFormat>yyyy-MM-dd HH:mm:ss.SSS</timeFormat>
|
||||
<!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
|
||||
<encoder>
|
||||
<!-- <pattern>[${appId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{0}: %msg</pattern> -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{X-Request-Id}] [%thread] %logger{0}: %msg</pattern>
|
||||
</encoder>
|
||||
|
||||
<!-- 指定级别的日志(INFO,WARN,ERROR) -->
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<logger name="org.hibernate" level="ERROR" />
|
||||
<logger name="org.apache" level="ERROR" />
|
||||
@ -100,14 +64,14 @@
|
||||
<springProfile name="test,prod">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
<appender-ref ref="localFile"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="local">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
<appender-ref ref="localFile"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
|
||||
219
baogutang-admin/src/main/resources/templates/coin.html
Normal file
219
baogutang-admin/src/main/resources/templates/coin.html
Normal file
@ -0,0 +1,219 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>K-LINE</title>
|
||||
<style>
|
||||
/* 样式美化 */
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #1b1b1b;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 20px 0;
|
||||
color: #f0b90b;
|
||||
}
|
||||
|
||||
select {
|
||||
margin: 0 10px;
|
||||
padding: 10px;
|
||||
background-color: #2c2c2c;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#controls {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#kLineChart {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
<!-- 引入 Lightweight Charts 库 -->
|
||||
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2>K-LINE</h2>
|
||||
|
||||
<!-- 控制区 -->
|
||||
<div id="controls">
|
||||
<!-- 交易对选择 -->
|
||||
<label for="symbol">选择交易对:</label>
|
||||
<select id="symbol" onchange="updateChart()">
|
||||
<option value="BTCUSDT">BTC/USDT</option>
|
||||
<option value="ETHUSDT">ETH/USDT</option>
|
||||
</select>
|
||||
|
||||
<!-- 时间间隔选择 -->
|
||||
<label for="timeInterval">选择时间间隔:</label>
|
||||
<select id="timeInterval" onchange="updateChart()">
|
||||
<option value="1m">1 分钟</option>
|
||||
<option value="3m">3 分钟</option>
|
||||
<option value="5m">5 分钟</option>
|
||||
<option value="15m">15 分钟</option>
|
||||
<option value="1h">1 小时</option>
|
||||
<option value="3h">3 小时</option>
|
||||
<option value="1d">1 天</option>
|
||||
<option value="3d">3 天</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- K线图容器 -->
|
||||
<div id="kLineChart"></div>
|
||||
|
||||
<script>
|
||||
let chart, candlestickSeries, socket;
|
||||
let lastKLineTime = 0; // 用于记录最后一个K线的时间
|
||||
|
||||
// 初始化 Lightweight Charts
|
||||
function initChart() {
|
||||
chart = LightweightCharts.createChart(document.getElementById('kLineChart'), {
|
||||
width: document.getElementById('kLineChart').clientWidth,
|
||||
height: 600,
|
||||
layout: {
|
||||
backgroundColor: '#131722',
|
||||
textColor: '#d1d4dc',
|
||||
},
|
||||
grid: {
|
||||
vertLines: {
|
||||
color: '#2B2B43',
|
||||
},
|
||||
horzLines: {
|
||||
color: '#363C4E',
|
||||
},
|
||||
},
|
||||
crosshair: {
|
||||
mode: LightweightCharts.CrosshairMode.Normal,
|
||||
},
|
||||
priceScale: {
|
||||
borderColor: '#485c7b',
|
||||
},
|
||||
timeScale: {
|
||||
borderColor: '#485c7b',
|
||||
timeVisible: true,
|
||||
secondsVisible: true,
|
||||
localization: {
|
||||
dateFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
timeZone: 'Asia/Shanghai',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
candlestickSeries = chart.addCandlestickSeries({
|
||||
upColor: '#4CAF50',
|
||||
downColor: '#F44336',
|
||||
borderDownColor: '#F44336',
|
||||
borderUpColor: '#4CAF50',
|
||||
wickDownColor: '#F44336',
|
||||
wickUpColor: '#4CAF50',
|
||||
});
|
||||
}
|
||||
|
||||
// 连接 WebSocket
|
||||
function connectWebSocket() {
|
||||
socket = new WebSocket("ws://localhost:8102/ws");
|
||||
|
||||
socket.onopen = function (event) {
|
||||
console.log('WebSocket connection established.');
|
||||
// Send initial data request to WebSocket
|
||||
updateWebSocket();
|
||||
};
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
const data = JSON.parse(event.data);
|
||||
const kline = data.k;
|
||||
if (kline.t > lastKLineTime) {
|
||||
// 如果是新K线,先更新 lastKLineTime
|
||||
lastKLineTime = kline.t;
|
||||
}
|
||||
const formattedData = {
|
||||
time: lastKLineTime / 1000, // 时间戳(秒)
|
||||
open: parseFloat(kline.o), // 开盘价
|
||||
high: parseFloat(kline.h), // 最高价
|
||||
low: parseFloat(kline.l), // 最低价
|
||||
close: parseFloat(kline.c) // 收盘价
|
||||
};
|
||||
|
||||
// 更新图表
|
||||
candlestickSeries.update(formattedData);
|
||||
};
|
||||
|
||||
socket.onclose = function (event) {
|
||||
console.log('WebSocket is closed now.');
|
||||
};
|
||||
|
||||
socket.onerror = function (error) {
|
||||
console.error('WebSocket error observed:', error);
|
||||
};
|
||||
}
|
||||
|
||||
// 更新 WebSocket 连接
|
||||
function updateWebSocket() {
|
||||
const symbol = document.getElementById('symbol').value;
|
||||
const interval = document.getElementById('timeInterval').value;
|
||||
|
||||
const payload = JSON.stringify({symbol: symbol, interval: interval});
|
||||
socket.send(payload);
|
||||
}
|
||||
|
||||
// 更新图表
|
||||
function updateChart() {
|
||||
const symbol = document.getElementById('symbol').value;
|
||||
const interval = document.getElementById('timeInterval').value;
|
||||
|
||||
// 获取接口数据并更新图表
|
||||
fetch(`http://localhost:8102/api/v1/admin/virtualCoin/uiKline?symbol=${symbol}&interval=${interval}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const formattedData = data.data.map(item => ({
|
||||
time: item[0] / 1000, // 时间戳(秒)
|
||||
open: parseFloat(item[1]), // 开盘价
|
||||
high: parseFloat(item[2]), // 最高价
|
||||
low: parseFloat(item[3]), // 最低价
|
||||
close: parseFloat(item[4]) // 收盘价
|
||||
}));
|
||||
// 对数据进行排序(按时间升序)
|
||||
formattedData.sort((a, b) => a.time - b.time);
|
||||
|
||||
// 设置初始的 lastKLineTime 为最后一条数据的时间戳(毫秒)
|
||||
lastKLineTime = formattedData[formattedData.length - 1].time * 1000;
|
||||
|
||||
// 更新图表
|
||||
candlestickSeries.setData(formattedData);
|
||||
})
|
||||
.catch(error => console.error('Error fetching data:', error));
|
||||
|
||||
// 更新 WebSocket 连接
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
updateWebSocket();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化图表和 WebSocket 连接
|
||||
initChart();
|
||||
connectWebSocket();
|
||||
|
||||
// 监听窗口大小变化,调整图表尺寸
|
||||
window.addEventListener('resize', () => {
|
||||
chart.resize(document.getElementById('kLineChart').clientWidth, 600);
|
||||
});
|
||||
|
||||
// 页面加载时更新图表
|
||||
updateChart();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,28 @@
|
||||
package top.baogutang.business.admin.job;
|
||||
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import top.baogutang.admin.schedule.OkCoinMarketCandlesHandler;
|
||||
import top.baogutang.business.admin.BaoGuTangAdminAbstractTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static com.xxl.job.core.biz.model.ReturnT.SUCCESS_CODE;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/22 : 11:18
|
||||
*/
|
||||
public class MarketCandlesTest extends BaoGuTangAdminAbstractTest {
|
||||
|
||||
@Resource
|
||||
private OkCoinMarketCandlesHandler okCoinMarketCandlesHandler;
|
||||
|
||||
@Test
|
||||
public void testMarketCandles() throws Exception {
|
||||
ReturnT<String> returnT = okCoinMarketCandlesHandler.execute("{\"onlyKey\":\"Bitcoin\",\"bar\":\"kline_1m\",\"sign\":\"marketCap\"}");
|
||||
Assert.assertEquals(SUCCESS_CODE, returnT.getCode());
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||
@ -43,43 +43,6 @@
|
||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
||||
|
||||
<!-- 外网 -->
|
||||
<appender name="ONLINE-OUT" class="com.aliyun.openservices.log.logback.LoghubAppender">
|
||||
<!--必选项 -->
|
||||
<endpoint>cn-shanghai.log.aliyuncs.com</endpoint>
|
||||
<accessKeyId>LTAI5tRN9T5Tz1QExcSpUaBc</accessKeyId>
|
||||
<accessKey>LniIMK15XEOc6Nn5mOrtX399FkVfQd</accessKey>
|
||||
<projectName>baogutang</projectName>
|
||||
<logstore>${appEnv}</logstore>
|
||||
|
||||
<!-- 可选项 -->
|
||||
<topic>${appId}</topic>
|
||||
<!-- <source>source1</source> -->
|
||||
|
||||
<!-- 可选项 详见 '参数说明' -->
|
||||
<packageTimeoutInMS>3000</packageTimeoutInMS>
|
||||
<logsCountPerPackage>4096</logsCountPerPackage>
|
||||
<logsBytesPerPackage>3145728</logsBytesPerPackage>
|
||||
<memPoolSizeInByte>104857600</memPoolSizeInByte>
|
||||
<retryTimes>3</retryTimes>
|
||||
<maxIOThreadSizeInPool>8</maxIOThreadSizeInPool>
|
||||
|
||||
<!-- 可选项 设置时区 -->
|
||||
<timeZone>Asia/Shanghai</timeZone>
|
||||
<!-- 可选项 设置时间格式 -->
|
||||
<timeFormat>yyyy-MM-dd HH:mm:ss.SSS</timeFormat>
|
||||
<!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
|
||||
<encoder>
|
||||
<!-- <pattern>[${appId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{0}: %msg</pattern> -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{X-Request-Id}] [%thread] %logger{0}: %msg</pattern>
|
||||
</encoder>
|
||||
|
||||
<!-- 指定级别的日志(INFO,WARN,ERROR) -->
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<logger name="org.hibernate" level="ERROR" />
|
||||
<logger name="org.apache" level="ERROR" />
|
||||
<logger name="ch.qos.logback" level="WARN" />
|
||||
@ -100,14 +63,13 @@
|
||||
<springProfile name="test,prod">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
<appender-ref ref="localFile"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="local">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
|
||||
@ -170,6 +170,9 @@
|
||||
<version>1.4.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -20,7 +20,7 @@ public class GlobalCorsConfig {
|
||||
//重写父类提供的跨域请求处理的接口
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
//添加映射路径
|
||||
registry.addMapping("/api")
|
||||
registry.addMapping("/api/**")
|
||||
//放行哪些原始域
|
||||
.allowedOrigins("*")
|
||||
//是否发送Cookie信息
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package top.baogutang.common.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/23 : 16:00
|
||||
*/
|
||||
@Data
|
||||
public class BinanceResults<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4314677378175006552L;
|
||||
|
||||
public static final int SUCCESS_CODE = 200;
|
||||
|
||||
private String id;
|
||||
|
||||
private Integer status;
|
||||
|
||||
private T result;
|
||||
}
|
||||
@ -1,8 +1,12 @@
|
||||
package top.baogutang.common.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
@ -18,6 +22,23 @@ public class PageParam implements Serializable {
|
||||
|
||||
private Integer pageSize;
|
||||
|
||||
@ApiModelProperty("排序条件")
|
||||
private List<OrderBy> orders = new ArrayList<>();
|
||||
|
||||
@ApiModel
|
||||
@Data
|
||||
public static class OrderBy implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -2936335557980068706L;
|
||||
|
||||
@ApiModelProperty("排序列名")
|
||||
private String columnName;
|
||||
|
||||
@ApiModelProperty("是否降序")
|
||||
private boolean desc;
|
||||
|
||||
}
|
||||
|
||||
public Integer getPageNum() {
|
||||
pageNum = pageNum == null ? 1 : pageNum;
|
||||
return pageNum;
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package top.baogutang.common.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: nikooh
|
||||
* @date: 2024/08/22 : 09:57
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@RefreshScope
|
||||
@ConfigurationProperties(prefix = "baogutang.coin")
|
||||
public class MarketCandlersProperties {
|
||||
|
||||
private String okCoinMarketCandlesUrl = "https://i.bicoin.com.cn/data/getKlineByOnlyKey";
|
||||
|
||||
private String apiKey = "xQIw2QFYG817KN9QiRQ76E1ThrBS2vAKJZTyGE5O3acX47Dr21I6QFHNMZbHWzE9";
|
||||
|
||||
private String apiSecret = "mJvcuTz57Qu4hHjZQLwMJhXASInosPXM4181x6UnofCdLlS3ROrFCRw9GLNucSTt";
|
||||
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
package top.baogutang.common.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.baogutang.common.domain.PageParam;
|
||||
@ -42,4 +46,53 @@ public class MyBatisPlusPageUtil {
|
||||
return new PageUtil<>(pageRes.getCurrent(), pageRes.getSize(), pageRes.getTotal(), pageRes.getPages(), rPageRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认排序
|
||||
*
|
||||
* @param orders 排序条件
|
||||
* @param queryWrapper 查询条件
|
||||
* @param <T> T
|
||||
*/
|
||||
public static <T> void defaultOrders(List<PageParam.OrderBy> orders, QueryWrapper<T> queryWrapper) {
|
||||
defaultOrder(orders, queryWrapper, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认排序,可以指定默认排序字段
|
||||
*
|
||||
* @param orders 排序条件
|
||||
* @param queryWrapper 查询条件
|
||||
* @param defaultColumnName 指定默认排序字段列
|
||||
* @param <T> T
|
||||
*/
|
||||
public static <T, R> void defaultOrder(List<PageParam.OrderBy> orders, QueryWrapper<T> queryWrapper, SFunction<T, R> defaultColumnName) {
|
||||
if (CollUtil.isEmpty(orders)) {
|
||||
defaultOrderProcess(queryWrapper, defaultColumnName);
|
||||
return;
|
||||
}
|
||||
orders.forEach(orderBy -> {
|
||||
//默认时间倒序
|
||||
if (StringUtils.isEmpty(orderBy.getColumnName())) {
|
||||
defaultOrderProcess(queryWrapper, defaultColumnName);
|
||||
} else {
|
||||
if (orderBy.isDesc()) {
|
||||
queryWrapper.orderByDesc(orderBy.getColumnName());
|
||||
} else {
|
||||
queryWrapper.orderByAsc(orderBy.getColumnName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param queryWrapper 查询条件
|
||||
* @param defaultColumnName 默认排序列名
|
||||
* @param <T> t
|
||||
* @param <R> r
|
||||
*/
|
||||
private static <T, R> void defaultOrderProcess(QueryWrapper<T> queryWrapper, SFunction<T, R> defaultColumnName) {
|
||||
if (defaultColumnName != null) {
|
||||
queryWrapper.lambda().orderByDesc(defaultColumnName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||
@ -43,43 +43,6 @@
|
||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
||||
|
||||
<!-- 外网 -->
|
||||
<appender name="ONLINE-OUT" class="com.aliyun.openservices.log.logback.LoghubAppender">
|
||||
<!--必选项 -->
|
||||
<endpoint>cn-shanghai.log.aliyuncs.com</endpoint>
|
||||
<accessKeyId>LTAI5tRN9T5Tz1QExcSpUaBc</accessKeyId>
|
||||
<accessKey>LniIMK15XEOc6Nn5mOrtX399FkVfQd</accessKey>
|
||||
<projectName>baogutang</projectName>
|
||||
<logstore>${appEnv}</logstore>
|
||||
|
||||
<!-- 可选项 -->
|
||||
<topic>${appId}</topic>
|
||||
<!-- <source>source1</source> -->
|
||||
|
||||
<!-- 可选项 详见 '参数说明' -->
|
||||
<packageTimeoutInMS>3000</packageTimeoutInMS>
|
||||
<logsCountPerPackage>4096</logsCountPerPackage>
|
||||
<logsBytesPerPackage>3145728</logsBytesPerPackage>
|
||||
<memPoolSizeInByte>104857600</memPoolSizeInByte>
|
||||
<retryTimes>3</retryTimes>
|
||||
<maxIOThreadSizeInPool>8</maxIOThreadSizeInPool>
|
||||
|
||||
<!-- 可选项 设置时区 -->
|
||||
<timeZone>Asia/Shanghai</timeZone>
|
||||
<!-- 可选项 设置时间格式 -->
|
||||
<timeFormat>yyyy-MM-dd HH:mm:ss.SSS</timeFormat>
|
||||
<!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
|
||||
<encoder>
|
||||
<!-- <pattern>[${appId}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{0}: %msg</pattern> -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{X-Request-Id}] [%thread] %logger{0}: %msg</pattern>
|
||||
</encoder>
|
||||
|
||||
<!-- 指定级别的日志(INFO,WARN,ERROR) -->
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<logger name="org.hibernate" level="ERROR" />
|
||||
<logger name="org.apache" level="ERROR" />
|
||||
<logger name="ch.qos.logback" level="WARN" />
|
||||
@ -100,14 +63,13 @@
|
||||
<springProfile name="test,prod">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
<appender-ref ref="localFile"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<springProfile name="local">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="ONLINE-OUT"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user