思路:
实现的效果,在后台配置虚拟货币,然后当它的涨幅度超过设定的值。然后发送钉钉通知

实现的思路:
技术选型:mysql+redis+rabbitmq+定时任务+代理服务器
mysql:负责存储虚拟货币配置类
redis:数据不落库,然后放到redis中,比如存当前时间戳,涨幅度,上一次价格。
rabbitmq:异步发送钉钉通知
代理服务器:因为ok交易所需要科学上网
第一步:看配置先利用mysql配置虚拟货币配置,可以直接拿若依的

第二步:首先拿到所有配置类,然后判定redis中是否有数据,我们放的是一个redis的hashMap。
第一次没有数据,然后调用ok的api然后进行请求,然后对应的价格,然后把时间戳,价 格,涨幅度放到redis中,第二次在执行的时候如果redis中有数据,然后这时候拿到应 的数据,用原来的价格-现价除以原价能知道他是涨还是跌,然后并且判定一下是否大于 涨幅达,如果大于涨幅度,则发送钉钉通知。
java
package com.ruoyi.system.corn;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.IsoConstants;
import com.ruoyi.common.mq.IsoMqConstant;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.system.domain.SysIsoIncreaseConfig;
import com.ruoyi.system.mq.vo.IsoMqDTO;
import com.ruoyi.system.service.ISysIsoIncreaseConfigService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author hu
* @desc 虚拟货币定时任务
*/
@RestController
@AllArgsConstructor
@RequestMapping("/aaa")
public class SysIsoConfigCorn {
private final ISysIsoIncreaseConfigService sysIsoIncreaseConfigService;
/**
* 代理服务器ip
*/
private static final String PROXY_HOST = "127.0.0.1";
/**
* 代理服务端口
*/
private static final Integer PROXY_PORT = 7890;
/**
* redis
*/
private final RedisTemplate redisTemplate;
/**
* rabbitmq
*/
private final RabbitTemplate rabbitTemplate;
/**
*
*/
@SneakyThrows
@GetMapping("/aaaa")
public String corn() {
// 第一步直接加锁,我们会使用redis的分布式锁。然后符合条件发送给mq,有时候mq处理慢。所以不希望重复发送
// 第一步拿到传递过来的数据,虚拟货币配置表里面的数据
List<SysIsoIncreaseConfig> list = sysIsoIncreaseConfigService.list();
// 然后循环遍历
for (int i = 0; i < list.size(); i++) {
SysIsoIncreaseConfig sysIsoIncreaseConfig = list.get(i);
// 第一步先拿到它的涨幅时间限制
// 第一步从redis中拿到它的数据,判定是否存在是一个map数据里面
Boolean hasKey = redisTemplate.opsForHash().hasKey(IsoConstants.ISO_REDIS, IsoConstants.ISO_MAP + sysIsoIncreaseConfig.getIsoCode());
// 判定key是否存在,如果不存在说明是第一次,然后这时候先进行拼接数据,然后再rabbitmq发送消息,然后不发送。在此执行一次就可以发送了
// 然后创建一个map
// 说明在范围内,然后这时候在调用ok交易所接口,拿到价格
// 然后用上一次价格减去当前价格/上一次价格,然后判定是否大于0,如果大于0说明跌了,如果小于0说明涨了,然后进行对比
// 如果涨幅度在范围内则发送消息
Long timeDate = null;
BigDecimal price = null;
String rise = null;
// ture 代表涨了,false 代表跌了
boolean status = true;
if (hasKey) {
JSONObject jsonMap = (JSONObject) redisTemplate.opsForHash().get(IsoConstants.ISO_REDIS, IsoConstants.ISO_MAP + sysIsoIncreaseConfig.getIsoCode());
// 拿到它的时间戳
timeDate = jsonMap.getLong(IsoConstants.ISO_LAST_TIME);
// 价格
price = jsonMap.getBigDecimal(IsoConstants.ISO_PRICE);
// 幅度
rise = jsonMap.getString(IsoConstants.ISO_RISE);
}
Map<String, String> map = new HashMap<>(list.size());
map.put(IsoConstants.INST_ID, sysIsoIncreaseConfig.getIsoCode() + IsoConstants.ISO_CODE);
String json = HttpUtils.doGet(IsoConstants.OK_URL, map, PROXY_HOST, PROXY_PORT);
// 然后拿到ok交易所的数据。然后转换为json,
JSONObject jsonObject = JSONObject.parseObject(json);
// 然后这时候拿到code,data。如果code为0,并且data不是空的,然后这时候可以拿到数据
if (jsonObject.getString(IsoConstants.OK_CODE).equals("0")) {
// 然后拿到里面的数据
JSONArray jsonArray = jsonObject.getJSONArray(IsoConstants.OK_DATA);
JSONObject jsonObjectInfo = jsonArray.getJSONObject(0);
// 24小时成交量
BigDecimal vol24h = jsonObjectInfo.getBigDecimal(IsoConstants.OK_VOLUME_24H);
// 24小时成交金额
BigDecimal vol24hCny = jsonObjectInfo.getBigDecimal(IsoConstants.OK_VOLUME_24H_CNY);
// 格式有很多,然后拿到价格
BigDecimal last = jsonObjectInfo.getBigDecimal(IsoConstants.OK_LAST);
// 然后用上一次价格减去当前价格,如果价格是空的,这时候直接往redis里面赋值
if (StringUtils.isNotNull(price)) {
// 用bigDecimal类型来计算 ,四舍五入保留2为小数
BigDecimal divide = price.subtract(last).divide(price, 4, BigDecimal.ROUND_DOWN);
// 然后拿到价格后,判定是否大于0说明跌,如果小于0,说明涨,
// 然后拿到绝对值,判定一下是否大于等于涨幅度限制 然后再用绝对值*100算出是否大于涨幅度
BigDecimal absDivide = divide.abs().multiply(new BigDecimal(100));
if (absDivide.compareTo(new BigDecimal(rise)) < 0) {
// 然后直接跳出循环
continue;
}
// 然后给一个标识用来确认是涨的还是跌的
if (divide.compareTo(BigDecimal.ZERO) > 0) {
status = false;
}
if (divide.compareTo(BigDecimal.ZERO) < 0) {
status = true;
}
// 获取当前时间错
long time = System.currentTimeMillis();
// 然后拿到上一次时间错,相减,然后转换为分钟
long l = time - timeDate;
// 转换为分钟
l = l / 1000 / 60;
IsoMqDTO build = IsoMqDTO.builder()
.isoName(sysIsoIncreaseConfig.getIsoName())
.isoCode(sysIsoIncreaseConfig.getIsoCode())
.riseStatus(status)
.rise(String.valueOf(absDivide))
.risePercent(rise)
.riseTime(String.valueOf(l))
.lastPrice(price)
.price(last)
.volCcy24h(vol24hCny)
.vol24h(vol24h).build();
// 如果价格是空的,然后这时候需要把里面的数据赋值给redis,然后进行判断,然后进行发送消息
rabbitTemplate.convertAndSend(IsoMqConstant.ISO_DISCOUNT_EXCHANGE,
IsoMqConstant.ISO_DISCOUNT_ROUTING_KEY, build);
} else {
Map<String, Object> redisMap = new HashMap<>(3);
// 时间戳
redisMap.put(IsoConstants.ISO_LAST_TIME, System.currentTimeMillis());
// 当前价格
redisMap.put(IsoConstants.ISO_PRICE, last);
// 涨幅度
redisMap.put(IsoConstants.ISO_RISE, sysIsoIncreaseConfig.getIsoIncreaseRate());
redisTemplate.opsForHash().put(IsoConstants.ISO_REDIS, IsoConstants.ISO_MAP + sysIsoIncreaseConfig.getIsoCode(), redisMap);
}
}
}
return null;
}
}
第三步 一些ok交易所返回的值以及静态的变量。实体
java
package com.ruoyi.system.mq.vo;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author hu
*/
@Data
@Builder
public class IsoMqDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 币种
*/
private String isoName;
/**
* 币种code
*/
private String isoCode;
/**
* 涨幅状态
*/
private Boolean riseStatus;
/**
* 涨幅百分比
*/
private String rise;
/**
* 上一次幅度
*/
private String risePercent;
/**
* 涨幅/跌幅时间
*/
private String riseTime;
/**
* 上一次价格
*/
private BigDecimal lastPrice;
/**
* 现价
*/
private BigDecimal price;
/**
* 24计价货币成交量币价
*/
private BigDecimal volCcy24h;
/**
* 24小时成交量
*/
private BigDecimal vol24h;
}
java
package com.ruoyi.common.constant;
/**
* @author hu
* @date 2025/05
* @desc 币种常量
*/
public class IsoConstants {
/**
* 产品标识
*/
public static final String INST_ID = "instId";
/**
* 欧易交易所获取产品信息URL
*/
public static final String OK_URL = "https://www.okx.com/api/v5/market/ticker";
/**
* 币种现货标识
*/
public static final String ISO_CODE = "-USDT";
/**
* iso的redis前缀
*/
public static final String ISO_LOCK_REDIS = "ISO_LOCK";
/**
* redis数据
*/
public static final String ISO_REDIS = "ISO:";
/**
* redisMap里面的数据币价
*/
public static final String ISO_PRICE = "PRICE";
/**
* 上一次执行时间戳
*/
public static final String ISO_LAST_TIME = "LAST_TIME";
/**
* 涨幅
*/
public static final String ISO_RISE = "RISE";
/**
* ok交易所code
*/
public static final String OK_CODE = "code";
/**
* ok交易所data
*/
public static final String OK_DATA = "data";
/**
* ok最新价格
*/
public static final String OK_LAST = "last";
/**
* ok最新成交量
*/
public static final String OK_LAST_SZ = "lastSz";
/**
* ok24小时成交量
*/
public static final String OK_VOLUME_24H = "vol24h";
/**
* ok24小时成交金额
*/
public static final String OK_VOLUME_24H_CNY = "volCcy24h";
/**
* redisMap
*/
public static final String ISO_MAP = "ISO_MAP:";
}
第四步:http请求
java
package com.ruoyi.common.utils.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.http.MediaType;
/**
* 通用http发送方法
*
* @author ruoyi
*/
public class HttpUtils
{
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url)
{
return sendGet(url, StringUtils.EMPTY);
}
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param)
{
return sendGet(url, param, Constants.UTF8);
}
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @param contentType 编码类型
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try
{
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("recv - {}", result);
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (Exception ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数
* @param contentType 内容类型
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param, String contentType)
{
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try
{
log.info("sendPost - {}", url);
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("recv - {}", result);
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
public static String sendSSLPost(String url, String param)
{
return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
}
public static String sendSSLPost(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
try
{
log.info("sendSSLPost - {}", urlNameString);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
URL console = new URL(urlNameString);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.connect();
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String ret = "";
while ((ret = br.readLine()) != null)
{
if (ret != null && !"".equals(ret.trim()))
{
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
}
}
log.info("recv - {}", result);
conn.disconnect();
br.close();
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
}
return result.toString();
}
public static String doGet(String url, Map<String, String> params, String proxyHost, int proxyPort) throws IOException {
// 构建URL参数
StringBuilder fullUrl = new StringBuilder(url);
if (params != null && !params.isEmpty()) {
boolean isFirstParam = true;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (isFirstParam) {
fullUrl.append("?");
isFirstParam = false;
} else {
fullUrl.append("&");
}
fullUrl.append(entry.getKey()).append("=").append(entry.getValue());
}
}
// 创建代理对象
HttpHost proxy = new HttpHost(proxyHost, proxyPort, "http");
// 使用代理配置HttpClient
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpClient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
// 创建HttpGet请求
HttpGet httpGet = new HttpGet(fullUrl.toString());
// 设置请求头(可选)
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
// 执行请求
String result = null;
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
result = IOUtils.toString(instream, StandardCharsets.UTF_8);
}
} finally {
// 关闭资源
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
private static class TrustAnyTrustManager implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
{
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[] {};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
}
}
java
package com.ruoyi.system.config;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @author hu
* @desc 钉钉通知配置类
*/
@Configuration
public class DingDingConfig {
@Value("${dingDing.url}")
private String URL;
@Value("${dingDing.secret}")
private String SECRET;
/**
* 组装签名url
*
* @return url
*/
@Bean
public String getUrl() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
Long timestamp = System.currentTimeMillis();
String stringToSign = timestamp + "\n" + SECRET;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
String signResult = "×tamp=" + timestamp + "&sign=" + sign;
// 得到拼接后的 URL
return URL + signResult;
}
/**
* 获取客户端
*
* @return
*/
@Bean
public DingTalkClient getClient() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
return new DefaultDingTalkClient(getUrl());
}
}
第6步 rabbitmq消费者消费消息及钉钉通知以及一些变量
java
package com.ruoyi.system.mq;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.rabbitmq.client.Channel;
import com.ruoyi.common.constant.IsoConstants;
import com.ruoyi.system.config.DingDingConfig;
import com.ruoyi.system.mq.vo.IsoMqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import static com.ruoyi.common.mq.IsoMqConstant.*;
/**
* @author hu
*/
@Component
@Slf4j
public class IsoConsumer {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DingDingConfig dingDingConfig;
/**
* 收到nack
* 第二个参数是是否批量处理,true代表批量确认小于等于第二个参数的消息,false代表只确认当前一个消息收到
* 第三个参数是是否重新入队,true代表重新入队,false代表丢弃
*/
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = ISO_DISCOUNT_QUEUE, durable = "true"),
exchange = @Exchange(name = ISO_DISCOUNT_EXCHANGE), key = {ISO_DISCOUNT_ROUTING_KEY}))
public void isoConsumer(Channel channel, Message message, IsoMqDTO isoMqDTO) throws IOException {
log.info("虚拟货币收到rabbitmq消息了:{}", isoMqDTO);
try {
// 直接进行ack
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// 删除redis里面的数据
redisTemplate.opsForHash().delete(IsoConstants.ISO_REDIS, IsoConstants.ISO_MAP + isoMqDTO.getIsoCode());
// 然后拿到里面的数据,开始放到redis中
Map<String, Object> redisMap = new HashMap<>(3);
// 上一次涨幅时间
redisMap.put(IsoConstants.ISO_LAST_TIME, System.currentTimeMillis());
// 当前价格
redisMap.put(IsoConstants.ISO_PRICE, isoMqDTO.getPrice());
// 涨幅度
redisMap.put(IsoConstants.ISO_RISE, isoMqDTO.getRisePercent());
redisTemplate.opsForHash().put(IsoConstants.ISO_REDIS, IsoConstants.ISO_MAP + isoMqDTO.getIsoCode(), redisMap);
// string 类型转换为json
// 然后这时候发送钉钉通知
DingTalkClient client = dingDingConfig.getClient();
OapiRobotSendRequest request = new OapiRobotSendRequest();
// 消息类型 - 文本
request.setMsgtype("text");
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
// 确定是涨幅还是跌幅的文字描述
String riseOrFall = isoMqDTO.getRiseStatus() ? "涨" : "跌";
// 构建文本内容
// 构建文本内容,添加上涨和下跌的图标以及基本的Markdown样式
String content = "🚨 OK交易所「" + isoMqDTO.getIsoName() + "」价格异动通知\n\n" +
"---\n" + // 分割线
"🔹 **币种代码**:`" + isoMqDTO.getIsoCode() + IsoConstants.ISO_CODE + "`\n" +
"🔹 **波动方向**:" +
(isoMqDTO.getRiseStatus() ?
"🟢 **<< 多头行情 >>**" : // 绿色表示上涨
"🔴 **<< 空头预警 >>**") + "\n" + // 红色表示下跌
"🔹 **当前" + riseOrFall + "幅**:`" + isoMqDTO.getRise() + "%` " +
getTrendIcon(Double.parseDouble(isoMqDTO.getRise()), Integer.valueOf(isoMqDTO.getRisePercent()), isoMqDTO.getRiseStatus()) + "\n" + // 根据涨幅动态显示箭头
"🔹 **达标倒计时**:⏳ `" + isoMqDTO.getRiseTime() + "分钟`\n\n" +
"**📊 价格快照**\n" +
"┌──────────────┬─────────────┐\n" +
"│ 上次价格 │ " + formatNumber(isoMqDTO.getLastPrice()) + " │\n" +
"├──────────────┼─────────────┤\n" +
"│ 最新报价 │ " + formatNumber(isoMqDTO.getPrice()) + " │\n" +
"└──────────────┴─────────────┘\n" +
"**📈 市场数据**\n" +
"▪️ 24h计价量:`" + formatNumber(isoMqDTO.getVolCcy24h()) + "`\n" +
"▪️ 24h成交量:`" + formatNumber(isoMqDTO.getVol24h()) + "`\n\n" +
"**📌 风险提示**\n" +
"※ 数据来源:OKEx交易所实时行情\n" +
"**⚠️ 投资有风险,操作需谨慎**";
text.setContent(content);
request.setText(text);
OapiRobotSendResponse response = client.execute(request);
} catch (Exception e) {
e.printStackTrace();
// 不管包不包过就进行ack,防止重复发送
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
// 数值格式化方法
private String formatNumber(Number value) {
return ((BigDecimal) value).toPlainString();
}
// 动态趋势图标
private String getTrendIcon(double rise, Integer risePercent, boolean riseStatus) {
if (riseStatus && rise > risePercent){
return "🚀";
}else if (!riseStatus && -rise < risePercent){
return "💥";
}
return "➡️";
}
}
java
package com.ruoyi.common.mq;
/**
* @data
* @desc 描述
*/
public class IsoMqConstant {
/**
* iso交换机名字
*/
public static final String ISO_DISCOUNT_EXCHANGE = "iso.exchange";
/**
* iso队列
*/
public static final String ISO_DISCOUNT_QUEUE = "iso.queue";
/**
* iso路由key
*/
public static final String ISO_DISCOUNT_ROUTING_KEY = "iso.routing.key";
}
第7步:钉钉配置首先
邀请3个人

点击设置

就算可以了。
第8步:代理服务器
首先是在windows上利用clash上网,前提是自己有
最后就可以记性发送了,

结果
下一章节。在linux按照clash。并部署到服务器