Compare commits
No commits in common. "3ae12ece91d183a42494b175551c875a6e967a74" and "30e8b57530d6009b516c86b8bf2b6d591bb321c3" have entirely different histories.
3ae12ece91
...
30e8b57530
@ -112,22 +112,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.xuxueli</groupId>
|
<groupId>com.xuxueli</groupId>
|
||||||
<artifactId>xxl-job-core</artifactId>
|
<artifactId>xxl-job-core</artifactId>
|
||||||
<version>2.2.0</version>
|
<version>2.3.1</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>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -1,34 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
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("*");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,7 +3,6 @@ package top.baogutang.admin.config;
|
|||||||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
|
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@ -14,7 +13,6 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(value = "spring.profiles.active", havingValue = "prod")
|
|
||||||
public class XxlJobConfig {
|
public class XxlJobConfig {
|
||||||
|
|
||||||
@Value("${xxl.job.admin.addresses}")
|
@Value("${xxl.job.admin.addresses}")
|
||||||
@ -23,9 +21,6 @@ public class XxlJobConfig {
|
|||||||
@Value("${xxl.job.executor.appName}")
|
@Value("${xxl.job.executor.appName}")
|
||||||
private String appName;
|
private String appName;
|
||||||
|
|
||||||
@Value("${xxl.job.executor.accessKey}")
|
|
||||||
private String accessKey;
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public XxlJobSpringExecutor xxlJobExecutor() {
|
public XxlJobSpringExecutor xxlJobExecutor() {
|
||||||
@ -33,7 +28,6 @@ public class XxlJobConfig {
|
|||||||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
|
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
|
||||||
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
|
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
|
||||||
xxlJobSpringExecutor.setAppname(appName);
|
xxlJobSpringExecutor.setAppname(appName);
|
||||||
xxlJobSpringExecutor.setAccessToken(accessKey);
|
|
||||||
log.info(">>>>>>>>>>> xxl-job config init success <<<<<<<<<<");
|
log.info(">>>>>>>>>>> xxl-job config init success <<<<<<<<<<");
|
||||||
return xxlJobSpringExecutor;
|
return xxlJobSpringExecutor;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,10 +17,4 @@ public class StaticController {
|
|||||||
// 这里返回的字符串是HTML文件名(不包括扩展名)
|
// 这里返回的字符串是HTML文件名(不包括扩展名)
|
||||||
return "json-parse";
|
return "json-parse";
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/coin")
|
|
||||||
public String viewCoinHtml() {
|
|
||||||
// 这里返回的字符串是HTML文件名(不包括扩展名)
|
|
||||||
return "coin";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,13 +9,11 @@ import cn.hutool.http.HttpResponse;
|
|||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
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.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import top.baogutang.admin.domain.IphoneProductDto;
|
import top.baogutang.admin.domain.IphoneProductDto;
|
||||||
import top.baogutang.admin.utils.DingTalkMsgPushUtils;
|
import top.baogutang.admin.utils.DingTalkMsgPushUtils;
|
||||||
@ -36,7 +34,7 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RefreshScope
|
@RefreshScope
|
||||||
public class AppleInventoryScheduleHandler extends IJobHandler {
|
public class AppleInventorySchedule {
|
||||||
|
|
||||||
@Value("${baogutang.apple.country_code:cn}")
|
@Value("${baogutang.apple.country_code:cn}")
|
||||||
private String countryCode;
|
private String countryCode;
|
||||||
@ -59,6 +57,25 @@ public class AppleInventoryScheduleHandler extends IJobHandler {
|
|||||||
@Resource
|
@Resource
|
||||||
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
|
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0/30 * * * ? ")
|
||||||
|
public void appleInventoryMonitor() {
|
||||||
|
if (!Boolean.TRUE.equals(appleInventoryMonitorSwitch)) {
|
||||||
|
log.info(">>>>>>>>>>apple inventory monitor switch closed!<<<<<<<<<<");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取设备信息
|
||||||
|
List<IphoneProductDto> products = iphoneProductParserUtils.getProducts(deviceCode, countryCode);
|
||||||
|
//监视机型型号
|
||||||
|
products.forEach(product -> {
|
||||||
|
this.doMonitor(product);
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error(">>>>>>>>>>apple inventory monitor error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void doMonitor(IphoneProductDto product) {
|
private void doMonitor(IphoneProductDto product) {
|
||||||
|
|
||||||
|
|
||||||
@ -173,24 +190,4 @@ public class AppleInventoryScheduleHandler extends IJobHandler {
|
|||||||
return CharSequenceUtil.format(messageTemplate, twoLineAddress.replace("\n", " "), daytimePhone, location, distanceWithUnit);
|
return CharSequenceUtil.format(messageTemplate, twoLineAddress.replace("\n", " "), daytimePhone, location, distanceWithUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@XxlJob("appleInventoryScheduleHandler")
|
|
||||||
public ReturnT<String> execute(String params) throws Exception {
|
|
||||||
if (!Boolean.TRUE.equals(appleInventoryMonitorSwitch)) {
|
|
||||||
log.info(">>>>>>>>>>apple inventory monitor switch closed!<<<<<<<<<<");
|
|
||||||
return ReturnT.SUCCESS;
|
|
||||||
}
|
|
||||||
// 获取设备信息
|
|
||||||
List<IphoneProductDto> products = iphoneProductParserUtils.getProducts(deviceCode, countryCode);
|
|
||||||
//监视机型型号
|
|
||||||
products.forEach(product -> {
|
|
||||||
this.doMonitor(product);
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
log.error(">>>>>>>>>>apple inventory monitor error:{}<<<<<<<<<<", e.getMessage(), e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return ReturnT.SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
package top.baogutang.admin.schedule;
|
package top.baogutang.admin.schedule;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.xxl.job.core.biz.model.ReturnT;
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import com.xxl.job.core.handler.IJobHandler;
|
|
||||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
@ -15,7 +14,6 @@ import top.baogutang.admin.utils.DingTalkMsgPushUtils;
|
|||||||
import top.baogutang.common.constants.NoticeTypeEnum;
|
import top.baogutang.common.constants.NoticeTypeEnum;
|
||||||
import top.baogutang.common.domain.PageUtil;
|
import top.baogutang.common.domain.PageUtil;
|
||||||
import top.baogutang.common.domain.Results;
|
import top.baogutang.common.domain.Results;
|
||||||
import top.baogutang.common.utils.JacksonUtil;
|
|
||||||
import top.baogutang.common.utils.OkHttpUtil;
|
import top.baogutang.common.utils.OkHttpUtil;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -31,7 +29,7 @@ import java.util.Objects;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RefreshScope
|
@RefreshScope
|
||||||
public class NoticeScheduleHandler extends IJobHandler {
|
public class NoticeScheduleHandler {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
|
private DingTalkMsgPushUtils dingTalkMsgPushUtils;
|
||||||
@ -39,14 +37,14 @@ public class NoticeScheduleHandler extends IJobHandler {
|
|||||||
@Resource
|
@Resource
|
||||||
private INoticeLogService noticeLogService;
|
private INoticeLogService noticeLogService;
|
||||||
|
|
||||||
@Override
|
|
||||||
@XxlJob("noticeScheduleHandler")
|
@XxlJob("NoticeScheduleHandler")
|
||||||
public ReturnT<String> execute(String params) throws Exception {
|
public void noticeScheduleHandler() throws Exception {
|
||||||
Arrays.stream(NoticeTypeEnum.values())
|
Arrays.stream(NoticeTypeEnum.values())
|
||||||
.forEach(noticeType -> {
|
.forEach(noticeType -> {
|
||||||
Results<PageUtil<AnnouncementsDto>> results = OkHttpUtil.get(noticeType.getListUrl(), null, null, new TypeReference<Results<PageUtil<AnnouncementsDto>>>() {
|
Results<PageUtil<AnnouncementsDto>> results = OkHttpUtil.get(noticeType.getListUrl(), null, null, new TypeReference<Results<PageUtil<AnnouncementsDto>>>() {
|
||||||
});
|
});
|
||||||
log.info(">>>>>>>>>>请求获取:{}公告返回数据:{}<<<<<<<<<<", noticeType.getDesc(), JacksonUtil.toJson(results));
|
log.info(">>>>>>>>>>请求获取:{}公告返回数据:{}<<<<<<<<<<", noticeType.getDesc(), JSON.toJSONString(results));
|
||||||
if (Objects.isNull(results)) {
|
if (Objects.isNull(results)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -66,6 +64,5 @@ public class NoticeScheduleHandler extends IJobHandler {
|
|||||||
dingTalkMsgPushUtils.robotMarkdownMsgPush(announcementsDto.getTitle(), "查看详情", noticeType.getDetailUrl() + announcementsDto.getId(), content);
|
dingTalkMsgPushUtils.robotMarkdownMsgPush(announcementsDto.getTitle(), "查看详情", noticeType.getDetailUrl() + announcementsDto.getId(), content);
|
||||||
noticeLogService.saveNotice(noticeType, announcementsDto.getId(), announcementsDto.getType(), announcementsDto.getTitle(), announcementsDto.getCover(), noticeType.getDetailUrl() + announcementsDto.getId(), announcementsDto.getCreator(), announcementsDto.getCreatedAt());
|
noticeLogService.saveNotice(noticeType, announcementsDto.getId(), announcementsDto.getType(), announcementsDto.getTitle(), announcementsDto.getCover(), noticeType.getDetailUrl() + announcementsDto.getId(), announcementsDto.getCreator(), announcementsDto.getCreatedAt());
|
||||||
});
|
});
|
||||||
return ReturnT.SUCCESS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,122 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
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[]>>() {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package top.baogutang.admin.services.impl;
|
package top.baogutang.admin.services.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.zjiecode.wxpusher.client.WxPusher;
|
import com.zjiecode.wxpusher.client.WxPusher;
|
||||||
import com.zjiecode.wxpusher.client.bean.Message;
|
import com.zjiecode.wxpusher.client.bean.Message;
|
||||||
import com.zjiecode.wxpusher.client.bean.MessageResult;
|
import com.zjiecode.wxpusher.client.bean.MessageResult;
|
||||||
@ -13,7 +14,6 @@ import top.baogutang.admin.services.IWxMsgPushService;
|
|||||||
import com.zjiecode.wxpusher.client.bean.Page;
|
import com.zjiecode.wxpusher.client.bean.Page;
|
||||||
import top.baogutang.common.exceptions.BusinessException;
|
import top.baogutang.common.exceptions.BusinessException;
|
||||||
import top.baogutang.common.properties.WxMsgPushProperties;
|
import top.baogutang.common.properties.WxMsgPushProperties;
|
||||||
import top.baogutang.common.utils.JacksonUtil;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -54,7 +54,7 @@ public class WxMsgPushServiceImpl implements IWxMsgPushService {
|
|||||||
log.error(">>>>>>>>>>推送消息异常:{}<<<<<<<<<<", e.getMessage(), e);
|
log.error(">>>>>>>>>>推送消息异常:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
return Boolean.FALSE;
|
return Boolean.FALSE;
|
||||||
}
|
}
|
||||||
log.info(">>>>>>>>>>消息推送结果:{}<<<<<<<<<<", JacksonUtil.toJson(result));
|
log.info(">>>>>>>>>>消息推送结果:{}<<<<<<<<<<", JSON.toJSONString(result));
|
||||||
if (Objects.nonNull(result) && result.isSuccess()) {
|
if (Objects.nonNull(result) && result.isSuccess()) {
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ public class WxMsgPushServiceImpl implements IWxMsgPushService {
|
|||||||
log.error(">>>>>>>>>>查询用户信息失败:{}<<<<<<<<<<", e.getMessage(), e);
|
log.error(">>>>>>>>>>查询用户信息失败:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
throw new BusinessException("查询用户信息失败");
|
throw new BusinessException("查询用户信息失败");
|
||||||
}
|
}
|
||||||
log.info(">>>>>>>>>>查询用户列表结果:{}<<<<<<<<<<", JacksonUtil.toJson(wxUsersResult));
|
log.info(">>>>>>>>>>查询用户列表结果:{}<<<<<<<<<<", JSON.toJSONString(wxUsersResult));
|
||||||
if (Objects.nonNull(wxUsersResult) && Objects.nonNull(wxUsersResult.getData()) && CollectionUtils.isNotEmpty(wxUsersResult.getData().getRecords())) {
|
if (Objects.nonNull(wxUsersResult) && Objects.nonNull(wxUsersResult.getData()) && CollectionUtils.isNotEmpty(wxUsersResult.getData().getRecords())) {
|
||||||
return wxUsersResult.getData().getRecords().stream()
|
return wxUsersResult.getData().getRecords().stream()
|
||||||
.map(WxUser::getUid)
|
.map(WxUser::getUid)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package top.baogutang.admin.utils;
|
package top.baogutang.admin.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.dingtalk.api.DefaultDingTalkClient;
|
import com.dingtalk.api.DefaultDingTalkClient;
|
||||||
import com.dingtalk.api.DingTalkClient;
|
import com.dingtalk.api.DingTalkClient;
|
||||||
import com.dingtalk.api.request.OapiGettokenRequest;
|
import com.dingtalk.api.request.OapiGettokenRequest;
|
||||||
@ -19,7 +20,6 @@ import top.baogutang.common.constants.DingTalkMsgTypeEnum;
|
|||||||
import top.baogutang.common.exceptions.BusinessException;
|
import top.baogutang.common.exceptions.BusinessException;
|
||||||
import top.baogutang.common.properties.DingTalkConfigFactory;
|
import top.baogutang.common.properties.DingTalkConfigFactory;
|
||||||
import top.baogutang.common.properties.DingTalkConfigStrategy;
|
import top.baogutang.common.properties.DingTalkConfigStrategy;
|
||||||
import top.baogutang.common.utils.JacksonUtil;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
@ -90,7 +90,7 @@ public class DingTalkMsgPushUtils {
|
|||||||
OapiMessageCorpconversationAsyncsendV2Response response = null;
|
OapiMessageCorpconversationAsyncsendV2Response response = null;
|
||||||
try {
|
try {
|
||||||
response = client.execute(request, accessToken);
|
response = client.execute(request, accessToken);
|
||||||
log.info(">>>>>>>>>>request dingTalk send msg res:{}<<<<<<<<<<", JacksonUtil.toJson(response));
|
log.info(">>>>>>>>>>request dingTalk send msg res:{}<<<<<<<<<<", JSON.toJSONString(response));
|
||||||
return response.getTaskId();
|
return response.getTaskId();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(">>>>>>>>>>request dingTalk send msg error:{}<<<<<<<<<<", e.getMessage(), e);
|
log.error(">>>>>>>>>>request dingTalk send msg error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
@ -122,13 +122,13 @@ public class DingTalkMsgPushUtils {
|
|||||||
OapiGettokenResponse response;
|
OapiGettokenResponse response;
|
||||||
try {
|
try {
|
||||||
response = client.execute(request);
|
response = client.execute(request);
|
||||||
log.info(">>>>>>>>>>request dingTalk gene accessToken,request:[{}] response:[{}]<<<<<<<<<<", JacksonUtil.toJson(request), JacksonUtil.toJson(response));
|
log.info(">>>>>>>>>>request dingTalk gene accessToken,request:[{}] response:[{}]<<<<<<<<<<", JSON.toJSONString(request), JSON.toJSONString(response));
|
||||||
if (Objects.nonNull(response) && Objects.nonNull(response.getAccessToken())) {
|
if (Objects.nonNull(response) && Objects.nonNull(response.getAccessToken())) {
|
||||||
redisTemplate.opsForValue().set(cacheKey, response.getAccessToken(), 7000L, TimeUnit.SECONDS);
|
redisTemplate.opsForValue().set(cacheKey, response.getAccessToken(), 7000L, TimeUnit.SECONDS);
|
||||||
return response.getAccessToken();
|
return response.getAccessToken();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(">>>>>>>>>>request dingTalk gene accessToken error! request:{},errorMsg:{}<<<<<<<<<<", JacksonUtil.toJson(request), e.getMessage(), e);
|
log.error(">>>>>>>>>>request dingTalk gene accessToken error! request:{},errorMsg:{}<<<<<<<<<<", JSON.toJSONString(request), e.getMessage(), e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -158,31 +158,7 @@ public class DingTalkMsgPushUtils {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(">>>>>>>>>>robot msg send error:{}<<<<<<<<<<", e.getMessage(), e);
|
log.error(">>>>>>>>>>robot msg send error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
log.info(">>>>>>>>>>robot msg send request:{}, response:{}<<<<<<<<<<", JacksonUtil.toJson(request), JacksonUtil.toJson(response));
|
log.info(">>>>>>>>>>robot msg send request:{}, response:{}<<<<<<<<<<", JSON.toJSONString(request), JSON.toJSONString(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) {
|
private String sign(Long timestamp) {
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -7,7 +7,7 @@ spring:
|
|||||||
server-addr: 117.72.78.133:8848
|
server-addr: 117.72.78.133:8848
|
||||||
config:
|
config:
|
||||||
server-addr: 117.72.78.133:8848
|
server-addr: 117.72.78.133:8848
|
||||||
namespace: f17a91f4-6011-4e23-b537-d454711131c4
|
namespace: 622d03f1-aeac-430c-8361-e69a73a5c4db
|
||||||
file-extension: yml
|
file-extension: yml
|
||||||
refresh-enabled: true
|
refresh-enabled: true
|
||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
|
|||||||
@ -7,7 +7,7 @@ spring:
|
|||||||
server-addr: 127.0.0.1:8848
|
server-addr: 127.0.0.1:8848
|
||||||
config:
|
config:
|
||||||
server-addr: 127.0.0.1:8848
|
server-addr: 127.0.0.1:8848
|
||||||
namespace: 40b10af8-f553-41ae-8ff1-8cedf2272da0
|
namespace: 636cb425-50d7-4489-a462-fd4a5be6b87d
|
||||||
file-extension: yml
|
file-extension: yml
|
||||||
refresh-enabled: true
|
refresh-enabled: true
|
||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${log.path}/app.log</file>
|
<file>${log.path}/app.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||||
@ -27,11 +27,11 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- <appender name="asyncFileAppender" class="ch.qos.logback.classic.AsyncAppender">-->
|
<appender name="asyncFileAppender" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
<!-- <queueSize>10000</queueSize>-->
|
<queueSize>10000</queueSize>
|
||||||
<!-- <discardingThreshold>0</discardingThreshold>-->
|
<discardingThreshold>0</discardingThreshold>
|
||||||
<!-- <appender-ref ref="file"/>-->
|
<appender-ref ref="file"/>
|
||||||
<!-- </appender>-->
|
</appender>
|
||||||
|
|
||||||
<!-- 读取配置文件信息(交由各项目指定) -->
|
<!-- 读取配置文件信息(交由各项目指定) -->
|
||||||
<property name="active" value="${spring.profiles.active:-test}" />
|
<property name="active" value="${spring.profiles.active:-test}" />
|
||||||
@ -43,6 +43,42 @@
|
|||||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
<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.hibernate" level="ERROR" />
|
||||||
<logger name="org.apache" level="ERROR" />
|
<logger name="org.apache" level="ERROR" />
|
||||||
@ -64,14 +100,14 @@
|
|||||||
<springProfile name="test,prod">
|
<springProfile name="test,prod">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
<appender-ref ref="localFile"/>
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
<springProfile name="local">
|
<springProfile name="local">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
<appender-ref ref="localFile"/>
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
|
|||||||
@ -1,219 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,10 +4,10 @@ spring:
|
|||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 117.72.78.133:8848
|
server-addr: 180.97.221.51:8848
|
||||||
config:
|
config:
|
||||||
server-addr: 117.72.78.133:8848
|
server-addr: 180.97.221.51:8848
|
||||||
namespace: 622d03f1-aeac-430c-8361-e69a73a5c4db
|
namespace: 1877a228-6bff-46b9-a442-829473c3adc7
|
||||||
file-extension: yml
|
file-extension: yml
|
||||||
refresh-enabled: true
|
refresh-enabled: true
|
||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
|
|||||||
@ -4,10 +4,10 @@ spring:
|
|||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 127.0.0.1:8848
|
server-addr: 180.97.221.51:8848
|
||||||
config:
|
config:
|
||||||
server-addr: 127.0.0.1:8848
|
server-addr: 180.97.221.51:8848
|
||||||
namespace: 636cb425-50d7-4489-a462-fd4a5be6b87d
|
namespace: fe5388cc-76bc-44d6-ba20-034f97c567e5
|
||||||
file-extension: yml
|
file-extension: yml
|
||||||
refresh-enabled: true
|
refresh-enabled: true
|
||||||
group: DEFAULT_GROUP
|
group: DEFAULT_GROUP
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${log.path}/app.log</file>
|
<file>${log.path}/app.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||||
@ -43,6 +43,43 @@
|
|||||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
<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.hibernate" level="ERROR" />
|
||||||
<logger name="org.apache" level="ERROR" />
|
<logger name="org.apache" level="ERROR" />
|
||||||
<logger name="ch.qos.logback" level="WARN" />
|
<logger name="ch.qos.logback" level="WARN" />
|
||||||
@ -63,13 +100,14 @@
|
|||||||
<springProfile name="test,prod">
|
<springProfile name="test,prod">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
<appender-ref ref="localFile"/>
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
<springProfile name="local">
|
<springProfile name="local">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
|
|||||||
@ -48,10 +48,6 @@
|
|||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<artifactId>commons-pool2</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-context</artifactId>
|
<artifactId>spring-cloud-context</artifactId>
|
||||||
@ -164,15 +160,6 @@
|
|||||||
<artifactId>dingtalk</artifactId>
|
<artifactId>dingtalk</artifactId>
|
||||||
<version>2.0.14</version>
|
<version>2.0.14</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
|
||||||
<artifactId>nacos-api</artifactId>
|
|
||||||
<version>1.4.2</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package top.baogutang.common.aspect;
|
package top.baogutang.common.aspect;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@ -11,7 +13,6 @@ import org.springframework.web.context.request.RequestAttributes;
|
|||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
import top.baogutang.common.domain.Results;
|
import top.baogutang.common.domain.Results;
|
||||||
import top.baogutang.common.utils.JacksonUtil;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
@ -47,15 +48,15 @@ public class LogAspect {
|
|||||||
cost = System.currentTimeMillis() - startMills;
|
cost = System.currentTimeMillis() - startMills;
|
||||||
log.info("请求结束!本次请求耗时:{},url: {}, method: {}, params: {},user:{}, token: {}, 响应结果:{}",
|
log.info("请求结束!本次请求耗时:{},url: {}, method: {}, params: {},user:{}, token: {}, 响应结果:{}",
|
||||||
cost, request.getRequestURL().toString(),
|
cost, request.getRequestURL().toString(),
|
||||||
request.getMethod(), pjp.getArgs(), JacksonUtil.toJson(request.getAttribute("user")), request.getHeader("authorization"),
|
request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("authorization"),
|
||||||
JacksonUtil.toJson(result));
|
JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect));
|
||||||
if (result instanceof Results) {
|
if (result instanceof Results) {
|
||||||
Results r = (Results) result;
|
Results r = (Results) result;
|
||||||
r.setRid(requestId);
|
r.setRid(requestId);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("请求异常!!!本次请求耗时:{},error:{},url: {}, method: {}, params: {},user:{}, token: {}", cost, e, request.getRequestURL().toString(),
|
log.error("请求异常!!!本次请求耗时:{},error:{},url: {}, method: {}, params: {},user:{}, token: {}", cost, e, request.getRequestURL().toString(),
|
||||||
request.getMethod(), pjp.getArgs(), JacksonUtil.toJson(request.getAttribute("user")), request.getHeader("token"));
|
request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("token"));
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package top.baogutang.common.aspect;
|
package top.baogutang.common.aspect;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.aspectj.lang.annotation.After;
|
import org.aspectj.lang.annotation.After;
|
||||||
@ -16,7 +17,6 @@ import top.baogutang.common.constants.CacheConstant;
|
|||||||
import top.baogutang.common.domain.JwtBody;
|
import top.baogutang.common.domain.JwtBody;
|
||||||
import top.baogutang.common.domain.TokenCodeEnum;
|
import top.baogutang.common.domain.TokenCodeEnum;
|
||||||
import top.baogutang.common.exceptions.BusinessException;
|
import top.baogutang.common.exceptions.BusinessException;
|
||||||
import top.baogutang.common.utils.JacksonUtil;
|
|
||||||
import top.baogutang.common.utils.UserThreadLocal;
|
import top.baogutang.common.utils.UserThreadLocal;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@ -85,12 +85,12 @@ public class LoginRequiredAspect {
|
|||||||
if (!token.equals(redisToken)) {
|
if (!token.equals(redisToken)) {
|
||||||
JwtBody redisBody = tokenComponent.parseToken(redisToken);
|
JwtBody redisBody = tokenComponent.parseToken(redisToken);
|
||||||
if (body.getIat() > redisBody.getIat()) {
|
if (body.getIat() > redisBody.getIat()) {
|
||||||
log.info("newTokenCover,currentBody:{} redisBody:{}", JacksonUtil.toJson(body), JacksonUtil.toJson(redisBody));
|
log.info("newTokenCover,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody));
|
||||||
redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token);
|
redisTemplate.opsForValue().set(CacheConstant.TOKEN + userId, token);
|
||||||
} else {
|
} else {
|
||||||
log.info("FoundOldToken,currentBody:{} redisBody:{}", JacksonUtil.toJson(body), JacksonUtil.toJson(redisBody));
|
log.info("FoundOldToken,currentBody:{} redisBody:{}", JSON.toJSONString(body), JSON.toJSONString(redisBody));
|
||||||
throw new BusinessException(
|
throw new BusinessException(
|
||||||
TokenCodeEnum.AUTH_TIME_OUT.getCode(), TokenCodeEnum.AUTH_TIME_OUT.getMessage());
|
TokenCodeEnum.AUTH_FAILED.getCode(), TokenCodeEnum.AUTH_FAILED.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public class GlobalCorsConfig {
|
|||||||
//重写父类提供的跨域请求处理的接口
|
//重写父类提供的跨域请求处理的接口
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
//添加映射路径
|
//添加映射路径
|
||||||
registry.addMapping("/api/**")
|
registry.addMapping("/api")
|
||||||
//放行哪些原始域
|
//放行哪些原始域
|
||||||
.allowedOrigins("*")
|
.allowedOrigins("*")
|
||||||
//是否发送Cookie信息
|
//是否发送Cookie信息
|
||||||
|
|||||||
@ -1,62 +0,0 @@
|
|||||||
package top.baogutang.common.config;
|
|
||||||
|
|
||||||
import com.alibaba.nacos.api.NacosFactory;
|
|
||||||
import com.alibaba.nacos.api.PropertyKeyConst;
|
|
||||||
import com.alibaba.nacos.api.config.ConfigService;
|
|
||||||
import com.alibaba.nacos.api.config.listener.Listener;
|
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: nacos config listener
|
|
||||||
* @author: nikooh
|
|
||||||
* @date: 2024/07/02 : 18:37
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class NacosConfigExchangeListener {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private ContextRefresher contextRefresher;
|
|
||||||
|
|
||||||
@Value("${spring.cloud.nacos.config.server-addr}")
|
|
||||||
private String serverAddr;
|
|
||||||
|
|
||||||
@Value("${spring.application.name}")
|
|
||||||
private String dataId;
|
|
||||||
|
|
||||||
@Value("${spring.cloud.nacos.config.group}")
|
|
||||||
private String group;
|
|
||||||
|
|
||||||
@Value("${spring.cloud.nacos.config.namespace}")
|
|
||||||
private String namespace;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void addNacosConfigListener() throws NacosException {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
|
|
||||||
properties.put(PropertyKeyConst.NAMESPACE, namespace);
|
|
||||||
ConfigService configService = NacosFactory.createConfigService(properties);
|
|
||||||
configService.addListener(dataId, group, new Listener() {
|
|
||||||
@Override
|
|
||||||
public void receiveConfigInfo(String configInfo) {
|
|
||||||
log.info(">>>>>>>>>>receive config change! dataId:{},group:{}<<<<<<<<<<", dataId, group);
|
|
||||||
CompletableFuture.runAsync(() -> contextRefresher.refresh());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Executor getExecutor() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -33,7 +33,6 @@ public class RedisConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
|
||||||
log.info(">>>>>>>>>>start init redis config<<<<<<<<<<");
|
|
||||||
// 为了开发方便,直接使用<String, Object>
|
// 为了开发方便,直接使用<String, Object>
|
||||||
RedisTemplate<String, Object> template = new RedisTemplate();
|
RedisTemplate<String, Object> template = new RedisTemplate();
|
||||||
template.setConnectionFactory(redisConnectionFactory);
|
template.setConnectionFactory(redisConnectionFactory);
|
||||||
@ -59,7 +58,7 @@ public class RedisConfig {
|
|||||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||||
// 把所有的配置 set 进 template
|
// 把所有的配置 set 进 template
|
||||||
template.afterPropertiesSet();
|
template.afterPropertiesSet();
|
||||||
log.info(">>>>>>>>>>redis config init success<<<<<<<<<<");
|
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,6 @@ package top.baogutang.common.constants;
|
|||||||
*/
|
*/
|
||||||
public class CacheConstant {
|
public class CacheConstant {
|
||||||
|
|
||||||
private CacheConstant() {
|
|
||||||
// empty private constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实名认证完成
|
* 实名认证完成
|
||||||
*/
|
*/
|
||||||
@ -45,9 +41,4 @@ public class CacheConstant {
|
|||||||
public static final String PREFIX_DING_TALK_ACCESS_TOKEN = "top:baogutang:dingtalk:access_token:";
|
public static final String PREFIX_DING_TALK_ACCESS_TOKEN = "top:baogutang:dingtalk:access_token:";
|
||||||
|
|
||||||
public static final String PREFIX_APPLE_PRODUCT = "top:baogutang:apple:product:%s:%s:";
|
public static final String PREFIX_APPLE_PRODUCT = "top:baogutang:apple:product:%s:%s:";
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信小程序登陆缓存sessionId
|
|
||||||
*/
|
|
||||||
public static final String WX_SESSION_ID = "top:baogutang:weixin_app:wx_session_id:%s";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
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,12 +1,8 @@
|
|||||||
package top.baogutang.common.domain;
|
package top.baogutang.common.domain;
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:
|
* @description:
|
||||||
@ -22,23 +18,6 @@ public class PageParam implements Serializable {
|
|||||||
|
|
||||||
private Integer pageSize;
|
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() {
|
public Integer getPageNum() {
|
||||||
pageNum = pageNum == null ? 1 : pageNum;
|
pageNum = pageNum == null ? 1 : pageNum;
|
||||||
return pageNum;
|
return pageNum;
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
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,163 +0,0 @@
|
|||||||
package top.baogutang.common.utils;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: JacksonUtil
|
|
||||||
* @author: nikooh
|
|
||||||
* @date: 2024/08/06 : 10:50
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class JacksonUtil {
|
|
||||||
|
|
||||||
private JacksonUtil() {
|
|
||||||
// empty private constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
|
|
||||||
public static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
||||||
|
|
||||||
|
|
||||||
static {
|
|
||||||
// 所有的日期都统一用yyyy-MM-dd HH:mm:ss格式
|
|
||||||
OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_FORMAT));
|
|
||||||
// 忽略字符串存在,对象不存在的属性
|
|
||||||
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象转json字符串
|
|
||||||
*
|
|
||||||
* @param content 需要转换的对象
|
|
||||||
* @return json格式字符串
|
|
||||||
*/
|
|
||||||
public static String toJson(Object content) {
|
|
||||||
String result = StringUtils.EMPTY;
|
|
||||||
if (Objects.isNull(content)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
result = OBJECT_MAPPER.writeValueAsString(content);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse object to json fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字符串转自定义对象
|
|
||||||
*
|
|
||||||
* @param content 需要转换的字符串
|
|
||||||
* @param clazz 需要转换后的类型
|
|
||||||
* @param <T> 泛型
|
|
||||||
* @return 转换后对象
|
|
||||||
*/
|
|
||||||
public static <T> T fromJson(String content, Class<T> clazz) {
|
|
||||||
T result = null;
|
|
||||||
if (StringUtils.isBlank(content)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
result = OBJECT_MAPPER.readValue(content, clazz);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse json to object fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字符串转自定义对象
|
|
||||||
*
|
|
||||||
* @param content 需要转换的字符串
|
|
||||||
* @param type 需要转换后的类型
|
|
||||||
* @param <T> 泛型
|
|
||||||
* @return 转换后对象
|
|
||||||
*/
|
|
||||||
public static <T> T fromJson(String content, TypeReference<T> type) {
|
|
||||||
T result = null;
|
|
||||||
if (StringUtils.isBlank(content)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
result = OBJECT_MAPPER.readValue(content, type);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse json to object fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* json字符串转换为List
|
|
||||||
*
|
|
||||||
* @param content 需要转换的字符串
|
|
||||||
* @param clazz 需要转换后的类型
|
|
||||||
* @param <T> 泛型
|
|
||||||
* @return 转换后的集合
|
|
||||||
*/
|
|
||||||
public static <T> List<T> jsonToList(String content, Class<T> clazz) {
|
|
||||||
List<T> list = new ArrayList<>();
|
|
||||||
if (StringUtils.isBlank(content) || clazz == null) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
list = OBJECT_MAPPER.readValue(content, new TypeReference<List<T>>() {
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse json to list fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* json字符串转换为Map
|
|
||||||
*
|
|
||||||
* @param content 需要转换的字符串
|
|
||||||
* @param keyType 需要转换后的key类型
|
|
||||||
* @param valueType 需要转换后的value类型
|
|
||||||
* @param <K> Class<K>
|
|
||||||
* @param <V> Class<V>
|
|
||||||
* @return 转换后的Map
|
|
||||||
*/
|
|
||||||
public static <K, V> Map<K, V> jsonToMap(String content, Class<K> keyType, Class<V> valueType) {
|
|
||||||
Map<K, V> result = new HashMap<>();
|
|
||||||
if (StringUtils.isBlank(content) || Objects.isNull(keyType) || Objects.isNull(valueType)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
result = OBJECT_MAPPER.readValue(content, new TypeReference<Map<K, V>>() {
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse json to map fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* json字符串转换为JsonNode
|
|
||||||
*
|
|
||||||
* @param content 需要转换的字符串
|
|
||||||
* @return 转换后的JsonNode
|
|
||||||
*/
|
|
||||||
public static JsonNode jsonToNode(String content) {
|
|
||||||
JsonNode jsonNode = null;
|
|
||||||
if (StringUtils.isBlank(content)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
jsonNode = OBJECT_MAPPER.readTree(content);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(">>>>>>>>>>parse json to node fail:{}<<<<<<<<<<", e.getMessage());
|
|
||||||
}
|
|
||||||
return jsonNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +1,5 @@
|
|||||||
package top.baogutang.common.utils;
|
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 com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import top.baogutang.common.domain.PageParam;
|
import top.baogutang.common.domain.PageParam;
|
||||||
@ -46,53 +42,4 @@ public class MyBatisPlusPageUtil {
|
|||||||
return new PageUtil<>(pageRes.getCurrent(), pageRes.getSize(), pageRes.getTotal(), pageRes.getPages(), rPageRes);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package top.baogutang.common.utils;
|
package top.baogutang.common.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.TypeReference;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
@ -56,7 +57,7 @@ public class OkHttpUtil {
|
|||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
String content = Objects.requireNonNull(response.body()).string();
|
String content = Objects.requireNonNull(response.body()).string();
|
||||||
if (StringUtils.isNotBlank(content)) {
|
if (StringUtils.isNotBlank(content)) {
|
||||||
return JacksonUtil.fromJson(content, type);
|
return JSON.parseObject(content, type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("postRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
log.error("postRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
||||||
@ -67,20 +68,20 @@ public class OkHttpUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T post(String url, Map<String, String> headerMap, Map<String, Object> params, TypeReference<T> type) {
|
public static <T> T post(String url, Map<String, String> headerMap, Map<String, String> params, TypeReference<T> type) {
|
||||||
try {
|
try {
|
||||||
Headers.Builder headerBuilder = new Headers.Builder();
|
Headers.Builder headerBuilder = new Headers.Builder();
|
||||||
if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) {
|
if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) {
|
||||||
headerMap.forEach(headerBuilder::add);
|
headerMap.forEach(headerBuilder::add);
|
||||||
}
|
}
|
||||||
RequestBody body = RequestBody.create(JacksonUtil.toJson(params), MediaType.parse("application/json; charset=utf-8"));
|
RequestBody body = RequestBody.create(JSON.toJSONString(params), MediaType.parse("application/json; charset=utf-8"));
|
||||||
Request request = new Request.Builder().post(body).headers(headerBuilder.build()).url(url).build();
|
Request request = new Request.Builder().post(body).headers(headerBuilder.build()).url(url).build();
|
||||||
Response response = OkHttpUtil.getInstance().newCall(request).execute();
|
Response response = OkHttpUtil.getInstance().newCall(request).execute();
|
||||||
log.info("postJSONRequestReq:{},Res:{}, ", JacksonUtil.toJson(params), JacksonUtil.toJson(response));
|
log.info("postJSONRequestReq:{},Res:{}, ", JSON.toJSONString(params), JSON.toJSONString(response));
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
String content = Objects.requireNonNull(response.body()).string();
|
String content = Objects.requireNonNull(response.body()).string();
|
||||||
if (StringUtils.isNotBlank(content)) {
|
if (StringUtils.isNotBlank(content)) {
|
||||||
return JacksonUtil.fromJson(content, type);
|
return JSON.parseObject(content, type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("postJSONRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
log.error("postJSONRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
||||||
@ -112,7 +113,7 @@ public class OkHttpUtil {
|
|||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
String content = Objects.requireNonNull(response.body()).string();
|
String content = Objects.requireNonNull(response.body()).string();
|
||||||
if (StringUtils.isNotBlank(content)) {
|
if (StringUtils.isNotBlank(content)) {
|
||||||
return JacksonUtil.fromJson(content, type);
|
return JSON.parseObject(content, type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("getRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
log.error("getRequest fail ,url:{}, params:{}, res:{}", url, params, response);
|
||||||
|
|||||||
@ -45,7 +45,6 @@ public class RandImageUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 直接通过response 返回图片
|
* 直接通过response 返回图片
|
||||||
*
|
|
||||||
* @param response
|
* @param response
|
||||||
* @param resultCode
|
* @param resultCode
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
@ -58,7 +57,6 @@ public class RandImageUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成base64字符串
|
* 生成base64字符串
|
||||||
*
|
|
||||||
* @param resultCode
|
* @param resultCode
|
||||||
* @return
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="localFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${log.path}/app.log</file>
|
<file>${log.path}/app.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
<fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/app-%d{yyyy-MM-dd}.%i.log
|
||||||
@ -43,6 +43,43 @@
|
|||||||
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
<!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
|
||||||
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
|
<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.hibernate" level="ERROR" />
|
||||||
<logger name="org.apache" level="ERROR" />
|
<logger name="org.apache" level="ERROR" />
|
||||||
<logger name="ch.qos.logback" level="WARN" />
|
<logger name="ch.qos.logback" level="WARN" />
|
||||||
@ -63,13 +100,14 @@
|
|||||||
<springProfile name="test,prod">
|
<springProfile name="test,prod">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
<appender-ref ref="localFile"/>
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
<springProfile name="local">
|
<springProfile name="local">
|
||||||
<root level="DEBUG">
|
<root level="DEBUG">
|
||||||
<appender-ref ref="console"/>
|
<appender-ref ref="console"/>
|
||||||
|
<appender-ref ref="ONLINE-OUT"/>
|
||||||
</root>
|
</root>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user