From 3c47fd0adccbb8ba94e6d2951c67f2a9ad48b5e7 Mon Sep 17 00:00:00 2001 From: DanielDeng Date: Thu, 21 Mar 2024 01:53:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=83=BD=E7=81=AB=E4=BA=91=E9=95=BF=E6=A4=BF?= =?UTF-8?q?=E8=A1=973.21=201:52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/mashibing/ApiStarterApp.java | 5 + .../api/client/BeaconCacheClient.java | 10 ++ .../mashibing/api/config/RabbitMQConfig.java | 32 ++++ .../api/config/RabbitTemplateConfig.java | 60 +++++++ .../api/controller/SmsController.java | 16 ++ .../api/filter/impl/FeeCheckFilter.java | 48 +++++- .../api/filter/impl/IPCheckFilter.java | 28 ++- .../api/filter/impl/MobileCheckFilter.java | 14 +- .../api/filter/impl/SignCheckFilter.java | 58 ++++++- .../api/filter/impl/TemplateCheckFilter.java | 60 ++++++- .../api/util/PhoneFormatCheckUtil.java | 35 ++++ beacon-api/src/main/resources/bootstrap.yml | 4 +- .../cache/controller/CacheController.java | 18 ++ beacon-cache/src/main/resources/bootstrap.yml | 4 +- beacon-common/pom.xml | 5 + .../common/constant/ApiConstant.java | 14 ++ .../common/constant/RabbitMQConstants.java | 16 ++ .../common/enums/ExceptionEnums.java | 2 + .../common/model/StandardSubmit.java | 7 +- .../mashibing/common/util/SnowFlakeUtil.java | 160 ++++++++++++++++++ beacon-strategy/pom.xml | 51 ++++++ .../strategy/StrategyStarterApp.java | 19 +++ .../strategy/mq/PreSendListener.java | 31 ++++ .../src/main/resources/bootstrap.yml | 17 ++ .../test/mapper/ClientBalanceMapper.java | 5 +- .../src/main/resources/application.yml | 4 +- .../test/mapper/ClientBalanceMapperTest.java | 15 +- pom.xml | 1 + 28 files changed, 720 insertions(+), 19 deletions(-) create mode 100644 beacon-api/src/main/java/com/mashibing/api/config/RabbitMQConfig.java create mode 100644 beacon-api/src/main/java/com/mashibing/api/config/RabbitTemplateConfig.java create mode 100644 beacon-api/src/main/java/com/mashibing/api/util/PhoneFormatCheckUtil.java create mode 100644 beacon-common/src/main/java/com/mashibing/common/constant/ApiConstant.java create mode 100644 beacon-common/src/main/java/com/mashibing/common/constant/RabbitMQConstants.java create mode 100644 beacon-common/src/main/java/com/mashibing/common/util/SnowFlakeUtil.java create mode 100644 beacon-strategy/pom.xml create mode 100644 beacon-strategy/src/main/java/com/mashibing/strategy/StrategyStarterApp.java create mode 100644 beacon-strategy/src/main/java/com/mashibing/strategy/mq/PreSendListener.java create mode 100644 beacon-strategy/src/main/resources/bootstrap.yml diff --git a/beacon-api/src/main/java/com/mashibing/ApiStarterApp.java b/beacon-api/src/main/java/com/mashibing/ApiStarterApp.java index 0af7d4e..ac4ad5c 100644 --- a/beacon-api/src/main/java/com/mashibing/ApiStarterApp.java +++ b/beacon-api/src/main/java/com/mashibing/ApiStarterApp.java @@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; /** * @author dch @@ -12,6 +13,10 @@ import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients +@ComponentScan(basePackages = { + "com.mashibing.api", + "com.mashibing.common" +}) public class ApiStarterApp { public static void main(String[] args) { diff --git a/beacon-api/src/main/java/com/mashibing/api/client/BeaconCacheClient.java b/beacon-api/src/main/java/com/mashibing/api/client/BeaconCacheClient.java index a73a8f5..8a386b3 100644 --- a/beacon-api/src/main/java/com/mashibing/api/client/BeaconCacheClient.java +++ b/beacon-api/src/main/java/com/mashibing/api/client/BeaconCacheClient.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import java.util.Map; +import java.util.Set; /** * @author dch @@ -16,4 +17,13 @@ public interface BeaconCacheClient { @GetMapping("/cache/hgetall/{key}") Map hGetAll(@PathVariable(value = "key")String key); + @GetMapping("/cache/hget/{key}/{field}") + public Object hget(@PathVariable(value = "key")String key,@PathVariable(value = "field")String field); + + @GetMapping("/cache/hget/{key}/{field}") + public String hgetString(@PathVariable(value = "key")String key,@PathVariable(value = "field")String field); + + @GetMapping("/cache/smember/{key}") + Set smember(@PathVariable(value = "key")String key); + } diff --git a/beacon-api/src/main/java/com/mashibing/api/config/RabbitMQConfig.java b/beacon-api/src/main/java/com/mashibing/api/config/RabbitMQConfig.java new file mode 100644 index 0000000..53c2964 --- /dev/null +++ b/beacon-api/src/main/java/com/mashibing/api/config/RabbitMQConfig.java @@ -0,0 +1,32 @@ +package com.mashibing.api.config; + +/** + * @author dch + * @create 2024-03-21 0:19 + */ + +import com.mashibing.common.constant.RabbitMQConstants; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.QueueBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * 构建队列&交换机信息 + * @author zjw + * @description + */ +@Configuration +public class RabbitMQConfig { + + /** + * 接口模块发送消息到策略模块的队列 + * @return + */ + @Bean + public Queue preSendQueue(){ + return QueueBuilder.durable(RabbitMQConstants.SMS_PRE_SEND).build(); + } + +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/config/RabbitTemplateConfig.java b/beacon-api/src/main/java/com/mashibing/api/config/RabbitTemplateConfig.java new file mode 100644 index 0000000..6944d67 --- /dev/null +++ b/beacon-api/src/main/java/com/mashibing/api/config/RabbitTemplateConfig.java @@ -0,0 +1,60 @@ +package com.mashibing.api.config; + +/** + * @author dch + * @create 2024-03-21 0:31 + */ + +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.CorrelationData; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 设置RabbitTemplate的confirm&return机制 + * @author zjw + * @description + */ +@Configuration +@Slf4j +public class RabbitTemplateConfig { + + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){ + //1、构建RabbitTemplate对象 + RabbitTemplate rabbitTemplate = new RabbitTemplate(); + + //2、设置connectionFactory + rabbitTemplate.setConnectionFactory(connectionFactory); + + //3、配置confirm机制 + rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){ + + @Override + public void confirm(CorrelationData correlationData, boolean ack, String cause) { + // ack为false,代表消息没有发送到exchange。 + if(!ack){ + log.error("【接口模块-发送消息】 消息没有发送到交换机,correlationData = {},cause = {}",correlationData,cause); + } + } + }); + + //4、配置return机制 + rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback(){ + + // 触发这个回调,说明交换机没有把消息路由到指定的队列中 + @Override + public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { + log.error("【接口模块-发送消息】 消息没有路由到指定的Queue。 message = {},exchange = {},routingKey = {}", + new String(message.getBody()),exchange,routingKey); + } + }); + + //5、返回 + return rabbitTemplate; + } + +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/controller/SmsController.java b/beacon-api/src/main/java/com/mashibing/api/controller/SmsController.java index 97330d9..ff219ce 100644 --- a/beacon-api/src/main/java/com/mashibing/api/controller/SmsController.java +++ b/beacon-api/src/main/java/com/mashibing/api/controller/SmsController.java @@ -5,9 +5,13 @@ import com.mashibing.api.filter.CheckFilterContext; import com.mashibing.api.form.SingleSendForm; import com.mashibing.api.util.R; import com.mashibing.api.vo.ResultVO; +import com.mashibing.common.constant.RabbitMQConstants; import com.mashibing.common.enums.ExceptionEnums; import com.mashibing.common.model.StandardSubmit; +import com.mashibing.common.util.SnowFlakeUtil; import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.connection.CorrelationData; +import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; @@ -30,6 +34,12 @@ import javax.servlet.http.HttpServletRequest; @Slf4j public class SmsController { + @Autowired + private SnowFlakeUtil snowFlakeUtil; + + @Autowired + private RabbitTemplate rabbitTemplate; + /** * 客户端IP地址的请求头信息,多个用','隔开。 */ @@ -75,7 +85,13 @@ public class SmsController { checkFilterContext.check(submit); + //============================基于雪花算法生成唯一标识sequenceId================================= + + submit.setSequenceId(snowFlakeUtil.nextId()); + //=========================发送到MQ,交给策略模块处理========================================= + rabbitTemplate.convertAndSend(RabbitMQConstants.SMS_PRE_SEND,submit,new CorrelationData(submit.getSequenceId().toString())); + return R.ok(); } diff --git a/beacon-api/src/main/java/com/mashibing/api/filter/impl/FeeCheckFilter.java b/beacon-api/src/main/java/com/mashibing/api/filter/impl/FeeCheckFilter.java index f2a2fc2..a1ffb5e 100644 --- a/beacon-api/src/main/java/com/mashibing/api/filter/impl/FeeCheckFilter.java +++ b/beacon-api/src/main/java/com/mashibing/api/filter/impl/FeeCheckFilter.java @@ -1,8 +1,14 @@ package com.mashibing.api.filter.impl; +import com.mashibing.api.client.BeaconCacheClient; import com.mashibing.api.filter.CheckFilter; +import com.mashibing.common.constant.ApiConstant; +import com.mashibing.common.constant.CacheConstant; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.model.StandardSubmit; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** @@ -14,9 +20,49 @@ import org.springframework.stereotype.Service; @Slf4j public class FeeCheckFilter implements CheckFilter { + @Autowired + private BeaconCacheClient cacheClient; + + /** + * 只要短信内容的文字长度小于等于70个字,按照一条计算 + */ + private final int MAX_LENGTH = 70; + + /** + * 如果短信内容的文字长度超过70,67字/条计算 + */ + private final int LOOP_LENGTH = 67; + + private final String BALANCE = "balance"; + @Override public void check(StandardSubmit submit) { log.info("【接口模块-校验客户余额】 校验ing…………"); + //1、从submit中获取到短信内容 + int length = submit.getText().length(); + + //2、判断短信内容的长度,如果小于等于70,算作一条,如果大于70字,按照67字/条,算出来当前短信的费用 + if(length <= MAX_LENGTH){ + // 当前短信内容是一条 + submit.setFee(ApiConstant.SINGLE_FEE); + }else{ + int strip = length % LOOP_LENGTH == 0 ? length / LOOP_LENGTH : length / LOOP_LENGTH + 1; + submit.setFee(ApiConstant.SINGLE_FEE * strip); + } + + //3、从Redis中查询出客户剩余的金额 + Long balance = ((Integer) cacheClient.hget(CacheConstant.CLIENT_BALANCE + submit.getClientId(), BALANCE)).longValue(); + + //4、判断金额是否满足当前短信费用\ + if(balance >= submit.getFee()){ + log.info("【接口模块-校验客户余额】 用户金额充足!!"); + return; + } + + //5、不满足就抛出异常 + log.info("【接口模块-校验客户余额】 客户余额不足"); + throw new ApiException(ExceptionEnums.BALANCE_NOT_ENOUGH); } -} + +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/filter/impl/IPCheckFilter.java b/beacon-api/src/main/java/com/mashibing/api/filter/impl/IPCheckFilter.java index 7f2c36f..8b25691 100644 --- a/beacon-api/src/main/java/com/mashibing/api/filter/impl/IPCheckFilter.java +++ b/beacon-api/src/main/java/com/mashibing/api/filter/impl/IPCheckFilter.java @@ -1,8 +1,14 @@ package com.mashibing.api.filter.impl; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.mashibing.api.client.BeaconCacheClient; import com.mashibing.api.filter.CheckFilter; +import com.mashibing.common.constant.CacheConstant; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.model.StandardSubmit; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** @@ -13,8 +19,28 @@ import org.springframework.stereotype.Service; @Service(value = "ip") @Slf4j public class IPCheckFilter implements CheckFilter { + + @Autowired + private BeaconCacheClient cacheClient; + + private final String IP_ADDRESS = "ipAddress"; + + @Override public void check(StandardSubmit submit) { log.info("【接口模块-校验ip】 校验ing…………"); + //1. 根据CacheClient根据客户的apikey以及ipAddress去查询客户的IP白名单 + String ip = cacheClient.hgetString(CacheConstant.CLIENT_BUSINESS + submit.getApikey(), IP_ADDRESS); + submit.setIp(ip); + + //2. 如果IP白名单为null,直接放行 + if(StringUtils.isEmpty(ip) || ip.contains(submit.getRealIP())){ + log.info("【接口模块-校验ip】 客户端请求IP合法!"); + return; + } + + //3. IP白名单不为空,并且客户端请求不在IP报名单内 + log.info("【接口模块-校验ip】 请求的ip不在白名单内"); + throw new ApiException(ExceptionEnums.IP_NOT_WHITE); } -} +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/filter/impl/MobileCheckFilter.java b/beacon-api/src/main/java/com/mashibing/api/filter/impl/MobileCheckFilter.java index 49f49a6..fc4b8a9 100644 --- a/beacon-api/src/main/java/com/mashibing/api/filter/impl/MobileCheckFilter.java +++ b/beacon-api/src/main/java/com/mashibing/api/filter/impl/MobileCheckFilter.java @@ -1,6 +1,10 @@ package com.mashibing.api.filter.impl; +import com.alibaba.cloud.commons.lang.StringUtils; import com.mashibing.api.filter.CheckFilter; +import com.mashibing.api.util.PhoneFormatCheckUtil; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.model.StandardSubmit; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -18,5 +22,13 @@ public class MobileCheckFilter implements CheckFilter { @Override public void check(StandardSubmit submit) { log.info("【接口模块-校验手机号】 校验ing…………"); + String mobile = submit.getMobile(); + if(!StringUtils.isEmpty(mobile) && PhoneFormatCheckUtil.isChinaPhone(mobile)){ + // 如果校验进来,代表手机号么得问题 + log.info("【接口模块-校验手机号】 手机号格式合法 mobile = {}",mobile); + return; + } + log.info("【接口模块-校验手机号】 手机号格式不正确 mobile = {}",mobile); + throw new ApiException(ExceptionEnums.ERROR_MOBILE); } -} +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/filter/impl/SignCheckFilter.java b/beacon-api/src/main/java/com/mashibing/api/filter/impl/SignCheckFilter.java index 363b8a3..dd12957 100644 --- a/beacon-api/src/main/java/com/mashibing/api/filter/impl/SignCheckFilter.java +++ b/beacon-api/src/main/java/com/mashibing/api/filter/impl/SignCheckFilter.java @@ -1,10 +1,20 @@ package com.mashibing.api.filter.impl; +import com.alibaba.cloud.commons.lang.StringUtils; +import com.mashibing.api.client.BeaconCacheClient; import com.mashibing.api.filter.CheckFilter; +import com.mashibing.common.constant.ApiConstant; +import com.mashibing.common.constant.CacheConstant; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.model.StandardSubmit; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Map; +import java.util.Set; + /** * @author dch * @create 2024-03-18 23:22 @@ -14,9 +24,55 @@ import org.springframework.stereotype.Service; @Slf4j public class SignCheckFilter implements CheckFilter { + @Autowired + private BeaconCacheClient cacheClient; + + /** + * 截取签名的开始索引 + */ + private final int SIGN_START_INDEX = 1; + + /** + * 客户存储签名信息的字段 + */ + private final String CLIENT_SIGN_INFO = "signInfo"; + + private final String SIGN_ID = "id"; @Override public void check(StandardSubmit submit) { log.info("【接口模块-校验签名】 校验ing…………"); + //1. 判断短信内容是否携带了【】 + String text = submit.getText(); + if (!text.startsWith(ApiConstant.SIGN_PREFIX) || !text.contains(ApiConstant.SIGN_SUFFIX)) { + log.info("【接口模块-校验签名】 无可用签名 text = {}", text); + throw new ApiException(ExceptionEnums.ERROR_SIGN); + } + //2. 将短信内容中的签名截取出来 + String sign = text.substring(SIGN_START_INDEX, text.indexOf(ApiConstant.SIGN_SUFFIX)); + if (StringUtils.isEmpty(sign)) { + log.info("【接口模块-校验签名】 无可用签名 text = {}", text); + throw new ApiException(ExceptionEnums.ERROR_SIGN); + } + + //3. 从缓存中查询出客户绑定的签名 + Set set = cacheClient.smember(CacheConstant.CLIENT_SIGN + submit.getClientId()); + if (set == null || set.size() == 0) { + log.info("【接口模块-校验签名】 无可用签名 text = {}", text); + throw new ApiException(ExceptionEnums.ERROR_SIGN); + } + //4. 判断~ + for (Map map : set) { + if (sign.equals(map.get(CLIENT_SIGN_INFO))) { + submit.setSign(sign); + submit.setSignId(Long.parseLong(map.get(SIGN_ID) + "")); + log.info("【接口模块-校验签名】 找到匹配的签名 sign = {}", sign); + return; + } + } + + //5. 到这,说明没有匹配的签名 + log.info("【接口模块-校验签名】 无可用签名 text = {}", text); + throw new ApiException(ExceptionEnums.ERROR_SIGN); } -} +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/filter/impl/TemplateCheckFilter.java b/beacon-api/src/main/java/com/mashibing/api/filter/impl/TemplateCheckFilter.java index c0801dc..77cf3bd 100644 --- a/beacon-api/src/main/java/com/mashibing/api/filter/impl/TemplateCheckFilter.java +++ b/beacon-api/src/main/java/com/mashibing/api/filter/impl/TemplateCheckFilter.java @@ -1,10 +1,19 @@ package com.mashibing.api.filter.impl; +import com.mashibing.api.client.BeaconCacheClient; import com.mashibing.api.filter.CheckFilter; +import com.mashibing.common.constant.ApiConstant; +import com.mashibing.common.constant.CacheConstant; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.model.StandardSubmit; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Map; +import java.util.Set; + /** * @author dch * @create 2024-03-18 23:22 @@ -14,9 +23,58 @@ import org.springframework.stereotype.Service; @Slf4j public class TemplateCheckFilter implements CheckFilter { + @Autowired + private BeaconCacheClient cacheClient; + + /** + * 模板内容中的具体模板信息 + */ + private final String TEMPLATE_TEXT = "templateText"; + + private final String TEMPLATE_PLACEHOLDER = "#"; @Override public void check(StandardSubmit submit) { log.info("【接口模块-校验模板】 校验ing…………"); + // 1、从submit中获取到短信内容,签名信息,签名id + String text = submit.getText(); + String sign = submit.getSign(); + Long signId = submit.getSignId(); + // 2、将短信内容中的签名直接去掉,获取短信具体内容 + + text = text.replace(ApiConstant.SIGN_PREFIX + sign + ApiConstant.SIGN_SUFFIX, ""); + // 3、从缓存中获取到签名id绑定的所有模板 + Set templates = cacheClient.smember(CacheConstant.CLIENT_TEMPLATE + signId); + // 4、在tempaltes不为null时,遍历签名绑定的所有模板信息 + if(templates != null && templates.size() > 0) { + for (Map template : templates) { + // 4.1 将模板内容和短信具体内容做匹配-true-匹配成功 + String templateText = (String) template.get(TEMPLATE_TEXT); + if(text.equals(templateText)){ + // 短信具体内容和模板是匹配的。 + log.info("【接口模块-校验模板】 校验模板通过 templateText = {}",templateText); + return; + } + // 4.2 判断模板中是否只包含一个变量,如果是,直接让具体短信内容匹配前缀和后缀 + // 例子:您的验证码是123434。如非本人操作,请忽略本短信 + // 例子:您的验证码是#code#。如非本人操作,请忽略本短信 + if(templateText != null && templateText.contains(TEMPLATE_PLACEHOLDER) + && templateText.length() - templateText.replaceAll(TEMPLATE_PLACEHOLDER,"").length() == 2){ + // 可以确认模板不为空,并且包含#符号,而且#符号有2个,代表是一个占位符(变量)。 + // 获取模板撇去占位符之后的前缀和后缀 + String templateTextPrefix = templateText.substring(0, templateText.indexOf(TEMPLATE_PLACEHOLDER)); + String templateTextSuffix = templateText.substring(templateText.lastIndexOf(TEMPLATE_PLACEHOLDER) + 1); + // 判断短信的具体内容是否匹配前缀和后缀 + if(text.startsWith(templateTextPrefix) && text.endsWith(templateTextSuffix)){ + // 当前的短信内容匹配短信模板 + log.info("【接口模块-校验模板】 校验模板通过 templateText = {}",templateText); + return; + } + } + } + } + // 5、 模板校验失败 + log.info("【接口模块-校验模板】 无可用模板 text = {}",text); + throw new ApiException(ExceptionEnums.ERROR_TEMPLATE); } -} +} \ No newline at end of file diff --git a/beacon-api/src/main/java/com/mashibing/api/util/PhoneFormatCheckUtil.java b/beacon-api/src/main/java/com/mashibing/api/util/PhoneFormatCheckUtil.java new file mode 100644 index 0000000..f05f27e --- /dev/null +++ b/beacon-api/src/main/java/com/mashibing/api/util/PhoneFormatCheckUtil.java @@ -0,0 +1,35 @@ +package com.mashibing.api.util; + +/** + * @author dch + * @create 2024-03-20 22:25 + */ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 校验手机号格式的合法性正则 + * + * @author zjw + * @description + */ +public class PhoneFormatCheckUtil { + + /** + * 国内手机号的正则表达式 + */ + private final static Pattern CHINA_PATTERN = Pattern.compile("^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$"); + + /** + * 根据正则校验手机号是否合法 + * + * @param number + * @return + */ + public static boolean isChinaPhone(String number) { + Matcher matcher = CHINA_PATTERN.matcher(number); + return matcher.matches(); + } + +} \ No newline at end of file diff --git a/beacon-api/src/main/resources/bootstrap.yml b/beacon-api/src/main/resources/bootstrap.yml index beeb0fa..66a912a 100644 --- a/beacon-api/src/main/resources/bootstrap.yml +++ b/beacon-api/src/main/resources/bootstrap.yml @@ -9,9 +9,9 @@ spring: cloud: nacos: discovery: - server-addr: 192.168.1.133:8848 + server-addr: 192.168.43.132:8848 # nacos配置中心地址: config: - server-addr: 192.168.1.133:8848 + server-addr: 192.168.43.132:8848 file-extension: yml # beacon-api-dev.yml diff --git a/beacon-cache/src/main/java/com/mashibing/cache/controller/CacheController.java b/beacon-cache/src/main/java/com/mashibing/cache/controller/CacheController.java index d9fce4b..282a919 100644 --- a/beacon-cache/src/main/java/com/mashibing/cache/controller/CacheController.java +++ b/beacon-cache/src/main/java/com/mashibing/cache/controller/CacheController.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; +import java.util.Set; /** * @author dch @@ -43,4 +44,21 @@ public class CacheController { return value; } + @GetMapping("/cache/hget/{key}/{field}") + public Object hget(@PathVariable(value = "key")String key,@PathVariable(value = "field")String field){ + log.info("【缓存模块】 hget方法,获取key ={},field = {}的数据", key,field); + Object value = redisClient.hGet(key, field); + log.info("【缓存模块】 hget,获取key ={},field = {} 的数据 value = {}", key,field,value); + return value; + } + + @GetMapping("/cache/smember/{key}") + public Set smember(@PathVariable(value = "key")String key){ + log.info("【缓存模块】 smember方法,获取key ={}的数据", key); + Set values = redisClient.sMembers(key); + log.info("【缓存模块】 smember方法,获取key ={} 的数据 value = {}", key,values); + return values; + } + + } diff --git a/beacon-cache/src/main/resources/bootstrap.yml b/beacon-cache/src/main/resources/bootstrap.yml index 67a8317..4b85102 100644 --- a/beacon-cache/src/main/resources/bootstrap.yml +++ b/beacon-cache/src/main/resources/bootstrap.yml @@ -9,9 +9,9 @@ spring: cloud: nacos: discovery: - server-addr: 192.168.1.133:8848 + server-addr: 192.168.43.132:8848 # nacos配置中心地址: config: - server-addr: 192.168.1.133:8848 + server-addr: 192.168.43.132:8848 file-extension: yml # beacon-cache-dev.yml diff --git a/beacon-common/pom.xml b/beacon-common/pom.xml index 866ad0a..88d1e36 100644 --- a/beacon-common/pom.xml +++ b/beacon-common/pom.xml @@ -21,6 +21,11 @@ org.projectlombok lombok + + org.springframework + spring-context + 5.3.12 + \ No newline at end of file diff --git a/beacon-common/src/main/java/com/mashibing/common/constant/ApiConstant.java b/beacon-common/src/main/java/com/mashibing/common/constant/ApiConstant.java new file mode 100644 index 0000000..0ce06b3 --- /dev/null +++ b/beacon-common/src/main/java/com/mashibing/common/constant/ApiConstant.java @@ -0,0 +1,14 @@ +package com.mashibing.common.constant; + +/** + * @author dch + * @create 2024-03-20 20:54 + */ +public interface ApiConstant { + + String SIGN_PREFIX = "【"; + + String SIGN_SUFFIX = "】"; + + Long SINGLE_FEE = 50L; +} diff --git a/beacon-common/src/main/java/com/mashibing/common/constant/RabbitMQConstants.java b/beacon-common/src/main/java/com/mashibing/common/constant/RabbitMQConstants.java new file mode 100644 index 0000000..80ce65f --- /dev/null +++ b/beacon-common/src/main/java/com/mashibing/common/constant/RabbitMQConstants.java @@ -0,0 +1,16 @@ +package com.mashibing.common.constant; + +/** + * @author dch + * @create 2024-03-21 0:17 + * common中声明 + * RabbitMQ中的一些队列信息 + * @description + */ +public interface RabbitMQConstants { + + /** + * 接口模块发送消息到策略模块的队列名称 + */ + String SMS_PRE_SEND = "sms_pre_send_topic"; +} diff --git a/beacon-common/src/main/java/com/mashibing/common/enums/ExceptionEnums.java b/beacon-common/src/main/java/com/mashibing/common/enums/ExceptionEnums.java index 539b5cb..bdf8588 100644 --- a/beacon-common/src/main/java/com/mashibing/common/enums/ExceptionEnums.java +++ b/beacon-common/src/main/java/com/mashibing/common/enums/ExceptionEnums.java @@ -16,6 +16,8 @@ public enum ExceptionEnums { ERROR_MOBILE(-5,"手机号格式不正确"), BALANCE_NOT_ENOUGH(-6,"手客户余额不足"), PARAMETER_ERROR(-10, "参数不合法!"), + SNOWFLAKE_OUT_OF_RANGE(-11,"雪花算法的机器id或服务id超出最大范围!"), + SNOWFLAKE_TIME_BACK(-12,"雪花算法的服务器出现时间回拨问题") ; private Integer code; diff --git a/beacon-common/src/main/java/com/mashibing/common/model/StandardSubmit.java b/beacon-common/src/main/java/com/mashibing/common/model/StandardSubmit.java index 7fa2555..0ebed95 100644 --- a/beacon-common/src/main/java/com/mashibing/common/model/StandardSubmit.java +++ b/beacon-common/src/main/java/com/mashibing/common/model/StandardSubmit.java @@ -4,6 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; import java.time.LocalDateTime; /** @@ -15,7 +16,7 @@ import java.time.LocalDateTime; @Data @NoArgsConstructor @AllArgsConstructor -public class StandardSubmit { +public class StandardSubmit implements Serializable { /** * 针对当前短信的唯一标识,雪花算法 @@ -106,4 +107,8 @@ public class StandardSubmit { 短信的类型 0-验证码短信 1-通知类 3-营销类 */ private Integer state; + + + /**签名的id*/ + private Long signId; } \ No newline at end of file diff --git a/beacon-common/src/main/java/com/mashibing/common/util/SnowFlakeUtil.java b/beacon-common/src/main/java/com/mashibing/common/util/SnowFlakeUtil.java new file mode 100644 index 0000000..bbab8b8 --- /dev/null +++ b/beacon-common/src/main/java/com/mashibing/common/util/SnowFlakeUtil.java @@ -0,0 +1,160 @@ +package com.mashibing.common.util; + +/** + * @author dch + * @create 2024-03-20 23:24 + */ + +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +/** + * 雪花算法生成全局唯一ID + * 64个bit为的long类型的值 + * 第一位:占1个bit位,就是0. + * 第二位:占41个bit位,代表时间戳 + * 第三位:占5个bit位,代表机器id + * 第四位:占5个bit位,服务id + * 第五位:占12个bit位,序列,自增的数值 + * @author zjw + * @description + */ +@Component +public class SnowFlakeUtil { + + /** + * 41个bit位存储时间戳,从0开始计算,最多可以存储69.7年。 + * 那么如果默认使用,从1970年到现在,最多可以用到2039年左右。 + * 按照从2022-11-11号开始计算,存储41个bit为,这样最多可以使用到2092年不到~~ + */ + private long timeStart = 1668096000000L; + + /** + * 机器id + */ + @Value("${snowflake.machineId:0}") + private long machineId; + + /** + * 服务id + */ + @Value("${snowflake.serviceId:0}") + private long serviceId; + + /** + * 序列 + */ + private long sequence; + + + /** + * 机器id占用的bit位数 + */ + private long machineIdBits = 5L; + + /** + * 服务id占用的bit位数 + */ + private long serviceIdBits = 5L; + + /** + * 序列占用的bit位数 + */ + private long sequenceBits = 12L; + + /** + * 计算出机器id的最大值 + */ + private long maxMachineId = -1 ^ (-1 << machineIdBits); + + /** + * 计算出服务id的最大值 + */ + private long maxServiceId = -1 ^ (-1 << serviceIdBits); + + @PostConstruct + public void init(){ + if(machineId > maxMachineId || serviceId > maxServiceId){ + System.out.println("机器ID或服务ID超过最大范围值!!"); + throw new ApiException(ExceptionEnums.SNOWFLAKE_OUT_OF_RANGE); + } + } + + /** + * 服务id需要位移的位数 + */ + private long serviceIdShift = sequenceBits; + + /** + * 机器id需要位移的位数 + */ + private long machineIdShift = sequenceBits + serviceIdBits; + + /** + * 时间戳需要位移的位数 + */ + private long timestampShift = sequenceBits + serviceIdBits + machineIdBits; + + /** + * 序列的最大值 + */ + private long maxSequenceId = -1 ^ (-1 << sequenceBits); + + /** + * 记录最近一次获取id的时间 + */ + private long lastTimestamp = -1; + + /** + * 获取系统时间毫秒值 + * @return + */ + private long timeGen(){ + return System.currentTimeMillis(); + } + + + public synchronized long nextId(){ + //1、 拿到当前系统时间的毫秒值 + long timestamp = timeGen(); + // 避免时间回拨造成出现重复的id + if(timestamp < lastTimestamp){ + // 说明出现了时间回拨 + System.out.println("当前服务出现时间回拨!!!"); + throw new ApiException(ExceptionEnums.SNOWFLAKE_TIME_BACK); + } + + //2、 判断当前生成id的时间和上一次生成的时间 + if(timestamp == lastTimestamp){ + // 同一毫秒值生成id + sequence = (sequence + 1) & maxSequenceId; + // 0000 10100000 :sequence + // 1111 11111111 :maxSequenceId + if(sequence == 0){ + // 进到这个if,说明已经超出了sequence序列的最大取值范围 + // 需要等到下一个毫秒再做回来生成具体的值 + timestamp = timeGen(); + while(timestamp <= lastTimestamp){ + // 时间还没动。 + timestamp = timeGen(); + } + } + }else{ + // 另一个时间点生成id + sequence = 0; + } + //3、重新给lastTimestamp复制 + lastTimestamp = timestamp; + + //4、计算id,将几位值拼接起来。 41bit位的时间,5位的机器,5位的服务 ,12位的序列 + return ((timestamp - timeStart) << timestampShift) | + (machineId << machineIdShift) | + (serviceId << serviceIdShift) | + sequence & + Long.MAX_VALUE; + } +} \ No newline at end of file diff --git a/beacon-strategy/pom.xml b/beacon-strategy/pom.xml new file mode 100644 index 0000000..56e8285 --- /dev/null +++ b/beacon-strategy/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.mashibing + beacon-cloud + 1.0-SNAPSHOT + + + beacon-strategy + + + 8 + 8 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + com.mashibing + beacon-common + 1.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + + \ No newline at end of file diff --git a/beacon-strategy/src/main/java/com/mashibing/strategy/StrategyStarterApp.java b/beacon-strategy/src/main/java/com/mashibing/strategy/StrategyStarterApp.java new file mode 100644 index 0000000..f36c9d2 --- /dev/null +++ b/beacon-strategy/src/main/java/com/mashibing/strategy/StrategyStarterApp.java @@ -0,0 +1,19 @@ +package com.mashibing.strategy; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * @author dch + * @create 2024-03-21 1:19 + */ +@SpringBootApplication +@EnableDiscoveryClient +public class StrategyStarterApp { + + public static void main(String[] args) { + SpringApplication.run(StrategyStarterApp.class,args); + } + +} \ No newline at end of file diff --git a/beacon-strategy/src/main/java/com/mashibing/strategy/mq/PreSendListener.java b/beacon-strategy/src/main/java/com/mashibing/strategy/mq/PreSendListener.java new file mode 100644 index 0000000..4a92dd9 --- /dev/null +++ b/beacon-strategy/src/main/java/com/mashibing/strategy/mq/PreSendListener.java @@ -0,0 +1,31 @@ +package com.mashibing.strategy.mq; + +import com.mashibing.common.constant.RabbitMQConstants; +import com.mashibing.common.model.StandardSubmit; +import com.rabbitmq.client.Channel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author dch + * @create 2024-03-21 1:45 + */ +@Component +@Slf4j +public class PreSendListener { + + @RabbitListener(queues = RabbitMQConstants.SMS_PRE_SEND) + public void listen(StandardSubmit submit, Message message, Channel channel) throws IOException { + log.info("【策略模块-接收消息】 接收到接口模块发送的消息 submit = {}",submit); + // 处理业务………… + + + + log.info("【策略模块-消费完毕】手动ack"); + channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); + } +} \ No newline at end of file diff --git a/beacon-strategy/src/main/resources/bootstrap.yml b/beacon-strategy/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..4a04b3e --- /dev/null +++ b/beacon-strategy/src/main/resources/bootstrap.yml @@ -0,0 +1,17 @@ +# 服务名称 +spring: + application: + name: beacon-strategy + # 多环境 + profiles: + active: dev + # nacos注册中心地址 + cloud: + nacos: + discovery: + server-addr: 192.168.43.132:8848 + # nacos配置中心地址: + config: + server-addr: 192.168.43.132:8848 + file-extension: yml + # beacon-strategy-dev.yml diff --git a/beacon-test/src/main/java/com/mashibing/test/mapper/ClientBalanceMapper.java b/beacon-test/src/main/java/com/mashibing/test/mapper/ClientBalanceMapper.java index 1ed344c..2e54371 100644 --- a/beacon-test/src/main/java/com/mashibing/test/mapper/ClientBalanceMapper.java +++ b/beacon-test/src/main/java/com/mashibing/test/mapper/ClientBalanceMapper.java @@ -1,5 +1,6 @@ package com.mashibing.test.mapper; +import com.mashibing.test.entity.ClientBalance; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; @@ -8,7 +9,7 @@ import org.apache.ibatis.annotations.Select; * @create 2024-03-20 1:28 */ public interface ClientBalanceMapper { - @Select("select balance from client_balance where client_id = #{clientId}") - Long findByClientId(@Param("clientId")Long clientId); + @Select("select * from client_balance where client_id = #{clientId}") + ClientBalance findByClientId(@Param("clientId")Long clientId); } diff --git a/beacon-test/src/main/resources/application.yml b/beacon-test/src/main/resources/application.yml index 97118ee..c04b196 100644 --- a/beacon-test/src/main/resources/application.yml +++ b/beacon-test/src/main/resources/application.yml @@ -6,13 +6,13 @@ spring: cloud: nacos: discovery: - server-addr: 192.168.1.133:8848 + server-addr: 192.168.43.132:8848 # datasource datasource: driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/beacon_cloud?characterEncoding=utf-8&useSSL=false username: root - password: 123456 + password: 1234 # 端口号 server: port: 20000 diff --git a/beacon-test/src/test/java/com/mashibing/test/mapper/ClientBalanceMapperTest.java b/beacon-test/src/test/java/com/mashibing/test/mapper/ClientBalanceMapperTest.java index 96606e7..298ce54 100644 --- a/beacon-test/src/test/java/com/mashibing/test/mapper/ClientBalanceMapperTest.java +++ b/beacon-test/src/test/java/com/mashibing/test/mapper/ClientBalanceMapperTest.java @@ -1,12 +1,17 @@ package com.mashibing.test.mapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.mashibing.test.client.CacheClient; +import com.mashibing.test.entity.ClientBalance; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; +import java.util.Map; + import static org.junit.Assert.*; /** @@ -24,10 +29,10 @@ public class ClientBalanceMapperTest { private CacheClient cacheClient; @Test - public void findByClientId() { - Long balance = mapper.findByClientId(1L); - System.out.println(balance); - - cacheClient.set("client_balance:1", balance); + public void findByClientId() throws JsonProcessingException { + ClientBalance clientBalance = mapper.findByClientId(1L); + ObjectMapper objectMapper = new ObjectMapper(); + Map map = objectMapper.readValue(objectMapper.writeValueAsString(clientBalance), Map.class); + cacheClient.hmset("client_balance:1",map); } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0cbec28..50d13e5 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ beacon-common beacon-cache beacon-test + beacon-strategy