思路:
实现的效果,在后台配置虚拟货币,然后当它的涨幅度超过设定的值。然后发送钉钉通知
实现的思路:
技术选型:mysql+redis+rabbitmq+定时任务+代理服务器
mysql:负责存储虚拟货币配置类
redis:数据不落库,然后放到redis中,比如存当前时间戳,涨幅度,上一次价格。
rabbitmq:异步发送钉钉通知
代理服务器:因为ok交易所需要科学上网
第一步:看配置先利用mysql配置虚拟货币配置,可以直接拿若依的
第二步:首先拿到所有配置类,然后判定redis中是否有数据,我们放的是一个redis的hashMap。
第一次没有数据,然后调用ok的api然后进行请求,然后对应的价格,然后把时间戳,价 格,涨幅度放到redis中,第二次在执行的时候如果redis中有数据,然后这时候拿到应 的数据,用原来的价格-现价除以原价能知道他是涨还是跌,然后并且判定一下是否大于 涨幅达,如果大于涨幅度,则发送钉钉通知。
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交易所返回的值以及静态的变量。实体
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;
}
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请求
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");// 使用代理配置HttpClientDefaultProxyRoutePlanner 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{@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType){}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType){}@Overridepublic X509Certificate[] getAcceptedIssuers(){return new X509Certificate[] {};}}private static class TrustAnyHostnameVerifier implements HostnameVerifier{@Overridepublic boolean verify(String hostname, SSLSession session){return true;}}
}
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*/@Beanpublic 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;// 得到拼接后的 URLreturn URL + signResult;}/*** 获取客户端** @return*/@Beanpublic DingTalkClient getClient() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {return new DefaultDingTalkClient(getUrl());}
}
第6步 rabbitmq消费者消费消息及钉钉通知以及一些变量
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 {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate 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 {// 直接进行ackchannel.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 "➡️";}
}
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。并部署到服务器