From 5b81d000e26e2e771ae50c864bb0040214bc9052 Mon Sep 17 00:00:00 2001 From: heqijun Date: Sat, 7 Jun 2025 17:03:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=A0=A1=E9=AA=8C+?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E4=BD=99=E9=A2=9D=E6=A0=A1=E9=AA=8C+?= =?UTF-8?q?=E9=9B=AA=E8=8A=B1=E7=AE=97=E6=B3=95=E7=94=9F=E6=88=90=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mashibing/api/ApiApplicaiton.java | 2 + .../api/controller/SMSController.java | 10 +- .../impl/ApikeySendCheckServiceImpl.java | 2 +- .../impl/FeeSendCheckServiceImpl.java | 44 ++++- .../impl/IpSendCheckServiceImpl.java | 2 +- .../impl/MobileSendCheckServiceImpl.java | 14 +- .../impl/SignSendCheckServiceImpl.java | 2 +- .../impl/TemplateSendCheckServiceImpl.java | 2 +- .../api/util/PhoneFormatCheckUtil.java | 30 ++++ .../common/constant/SMSConstant.java | 2 + .../common/enums/ExceptionEnums.java | 4 +- .../mashibing/common/utils/SnowFlakeUtil.java | 158 ++++++++++++++++++ .../test/mapper/ClientBalanceMapper.java | 5 +- .../test/mapper/ClientBalanceMapperTest.java | 14 +- 14 files changed, 276 insertions(+), 15 deletions(-) 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/utils/SnowFlakeUtil.java diff --git a/beacon-api/src/main/java/com/mashibing/api/ApiApplicaiton.java b/beacon-api/src/main/java/com/mashibing/api/ApiApplicaiton.java index ddf5577..10a6897 100644 --- a/beacon-api/src/main/java/com/mashibing/api/ApiApplicaiton.java +++ b/beacon-api/src/main/java/com/mashibing/api/ApiApplicaiton.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 heqijun @@ -16,6 +17,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients @EnableDiscoveryClient +@ComponentScan(basePackages = {"com.mashibing.api", "com.mashibing.common"}) public class ApiApplicaiton { public static void main(String[] args) { 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 086dbfc..4cdb49d 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 @@ -7,6 +7,7 @@ import com.mashibing.api.service.sendCheck.SendCheckContext; import com.mashibing.common.pojo.JsonResult; import com.mashibing.common.pojo.StandardSubmit; import com.mashibing.common.utils.JsonResultUtil; +import com.mashibing.common.utils.SnowFlakeUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +32,9 @@ import javax.validation.Valid; @RefreshScope public class SMSController { + @Autowired + SnowFlakeUtil snowFlakeUtil; + @Autowired SendCheckContext sendCheckContext; @@ -49,8 +53,12 @@ public class SMSController { standardSubmit.setRealIp(realIp); BeanUtils.copyProperties(request, standardSubmit); - //请求参数校验 + //责任链校验 sendCheckContext.check(standardSubmit); + + //雪花算法生成唯一id + standardSubmit.setSequenceId(snowFlakeUtil.nextId()); + //TODO 发送到MQ SingleSendResponse singleSendResponse = new SingleSendResponse(); diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/ApikeySendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/ApikeySendCheckServiceImpl.java index c806e9b..7c1ac11 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/ApikeySendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/ApikeySendCheckServiceImpl.java @@ -31,7 +31,7 @@ public class ApikeySendCheckServiceImpl implements SendCheckService { @Override public void check(StandardSubmit submit) { - log.info("【接口模块】发送短信前,apikey校验。。。"); + log.info("【接口模块】apikey校验================================"); Map clientBusinessMap = (Map) cacheClient.hget(CacheConstant.CLIENT_BUSINESS + submit.getApikey()); log.info("【接口模块】缓存中查询到客户信息:{}", clientBusinessMap); if (MapUtils.isEmpty(clientBusinessMap)) { diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/FeeSendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/FeeSendCheckServiceImpl.java index f132b61..0efc5c1 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/FeeSendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/FeeSendCheckServiceImpl.java @@ -3,6 +3,10 @@ package com.mashibing.api.service.sendCheck.impl; import com.mashibing.api.feignClient.CacheClient; import com.mashibing.api.pojo.SingleSendRequest; import com.mashibing.api.service.sendCheck.SendCheckService; +import com.mashibing.common.constant.CacheConstant; +import com.mashibing.common.constant.SMSConstant; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.pojo.StandardSubmit; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -20,10 +24,46 @@ import org.springframework.stereotype.Service; public class FeeSendCheckServiceImpl implements SendCheckService { @Autowired - CacheClient cacheClient; + private CacheClient 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 standardSubmit) { - log.info("Check Fee Send Check"); + log.info("【接口模块】客户余额校验================================"); + //1、从submit中获取到短信内容 + int length = standardSubmit.getText().length(); + + //2、判断短信内容的长度,如果小于等于70,算作一条,如果大于70字,按照67字/条,算出来当前短信的费用 + if (length <= MAX_LENGTH) { + // 当前短信内容是一条 + standardSubmit.setFee(SMSConstant.SINGLE_FEE); + } else { + int strip = length % LOOP_LENGTH == 0 ? length / LOOP_LENGTH : length / LOOP_LENGTH + 1; + standardSubmit.setFee(SMSConstant.SINGLE_FEE * strip); + } + + //3、从Redis中查询出客户剩余的金额 + Long balance = ((Integer) cacheClient.hget(CacheConstant.CLIENT_BALANCE + standardSubmit.getClientId(), BALANCE)).longValue(); + + //4、判断金额是否满足当前短信费用\ + if (balance >= standardSubmit.getFee()) { + log.info("【接口模块-校验客户余额】 用户金额充足!!"); + return; + } + + //5、不满足就抛出异常 + log.info("【接口模块-校验客户余额】 客户余额不足"); + throw new ApiException(ExceptionEnums.BALANCE_NOT_ENOUGH); } } diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/IpSendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/IpSendCheckServiceImpl.java index d686602..b9d6f32 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/IpSendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/IpSendCheckServiceImpl.java @@ -29,7 +29,7 @@ public class IpSendCheckServiceImpl implements SendCheckService { @Override public void check(StandardSubmit submit) { - log.info("【接口模块-校验ip】校验ing…………"); + log.info("【接口模块】ip白名单校验================================"); //1. 根据CacheClient根据客户的apikey以及ipAddress去查询客户的IP白名单 String ip = cacheClient.hgetString(CacheConstant.CLIENT_BUSINESS + submit.getApikey(), IP_ADDRESS); submit.setIpAddress(ip); diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/MobileSendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/MobileSendCheckServiceImpl.java index be49484..b12c7ce 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/MobileSendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/MobileSendCheckServiceImpl.java @@ -1,7 +1,11 @@ package com.mashibing.api.service.sendCheck.impl; +import com.alibaba.cloud.commons.lang.StringUtils; import com.mashibing.api.pojo.SingleSendRequest; import com.mashibing.api.service.sendCheck.SendCheckService; +import com.mashibing.api.util.PhoneFormatCheckUtil; +import com.mashibing.common.enums.ExceptionEnums; +import com.mashibing.common.exception.ApiException; import com.mashibing.common.pojo.StandardSubmit; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -18,6 +22,14 @@ import org.springframework.stereotype.Service; public class MobileSendCheckServiceImpl implements SendCheckService { @Override public void check(StandardSubmit standardSubmit) { - log.info("Check mobile send check"); + log.info("【接口模块】手机号校验================================"); + String mobile = standardSubmit.getMobile(); + if (!StringUtils.isEmpty(mobile) && PhoneFormatCheckUtil.isChinaPhone(mobile)) { + // 如果校验进来,代表手机号么得问题 + log.info("【接口模块-校验手机号】 手机号格式合法 mobile = {}", mobile); + return; + } + log.info("【接口模块-校验手机号】 手机号格式不正确 mobile = {}", mobile); + throw new ApiException(ExceptionEnums.ERROR_MOBILE); } } diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/SignSendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/SignSendCheckServiceImpl.java index cef9cfd..1812f9f 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/SignSendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/SignSendCheckServiceImpl.java @@ -31,7 +31,7 @@ public class SignSendCheckServiceImpl implements SendCheckService { @Override public void check(StandardSubmit standardSubmit) { - log.info("【接口模块】短信客户签名校验。。。"); + log.info("【接口模块】客户签名校验================================"); String text = standardSubmit.getText(); //短信正确格式:【签名】短信内容。。。 //短信内容为空,显然不可能包含签名 diff --git a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/TemplateSendCheckServiceImpl.java b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/TemplateSendCheckServiceImpl.java index 1a9f560..0084a7b 100644 --- a/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/TemplateSendCheckServiceImpl.java +++ b/beacon-api/src/main/java/com/mashibing/api/service/sendCheck/impl/TemplateSendCheckServiceImpl.java @@ -35,7 +35,7 @@ public class TemplateSendCheckServiceImpl implements SendCheckService { @Override public void check(StandardSubmit standardSubmit) { - log.info("【接口模块-校验模板】 校验ing…………"); + log.info("【接口模块】短信模板校验================================"); // 1、从submit中获取到短信内容,签名信息,签名id String text = standardSubmit.getText(); String sign = standardSubmit.getSign(); 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..e810ed5 --- /dev/null +++ b/beacon-api/src/main/java/com/mashibing/api/util/PhoneFormatCheckUtil.java @@ -0,0 +1,30 @@ +package com.mashibing.api.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author heqijun + * @ClassName: PhoneFormatCheckUtil + * @Description: TODO(这里用一句话描述这个类的作用) + * @date 2025/6/7 14:31 + */ + +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(); + } +} diff --git a/beacon-common/src/main/java/com/mashibing/common/constant/SMSConstant.java b/beacon-common/src/main/java/com/mashibing/common/constant/SMSConstant.java index 720d92c..dd04d86 100644 --- a/beacon-common/src/main/java/com/mashibing/common/constant/SMSConstant.java +++ b/beacon-common/src/main/java/com/mashibing/common/constant/SMSConstant.java @@ -17,4 +17,6 @@ public class SMSConstant { public static final String SIGN_SUFFIX = "】"; + public static final Long SINGLE_FEE = 50L; + } 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 49ce81f..9cdd380 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,7 +16,9 @@ public enum ExceptionEnums { ERROR_SIGN(-3, "无可用签名"), ERROR_TEMPLATE(-4, "无可用模板"), ERROR_MOBILE(-5, "手机号格式不正确"), - BALANCE_NOT_ENOUGH(-6, "手客户余额不足"), + BALANCE_NOT_ENOUGH(-6, "客户余额不足"), + SNOWFLAKE_OUT_OF_RANGE(-10, "机器id超过最大范围!"), + SNOWFLAKE_TIME_BACK(-11, "雪花算法出现时间回拨!!!") ; private int code; diff --git a/beacon-common/src/main/java/com/mashibing/common/utils/SnowFlakeUtil.java b/beacon-common/src/main/java/com/mashibing/common/utils/SnowFlakeUtil.java new file mode 100644 index 0000000..3c517d5 --- /dev/null +++ b/beacon-common/src/main/java/com/mashibing/common/utils/SnowFlakeUtil.java @@ -0,0 +1,158 @@ +package com.mashibing.common.utils; + +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; + +/** + * @author heqijun + * @ClassName: SnowFlakeUtil + * @Description: 雪花算法生成唯一id + * @date 2025/6/7 16:40 + */ + +@Component +public class SnowFlakeUtil { + /** + * 雪花算法生成全局唯一ID + * 64个bit为的long类型的值 + * 第一位:占1个bit位,就是0. + * 第二位:占41个bit位,代表时间戳 + * 第三位:占5个bit位,代表机器id + * 第四位:占5个bit位,服务id + * 第五位:占12个bit位,序列,自增的数值 + */ + /** + * 41个bit位存储时间戳,从0开始计算,最多可以存储69.7年。 + * 那么如果默认使用,从1970年到现在,最多可以用到2039年左右。 + * 按照从2022-11-11号开始计算,存储41个bit为,这样最多可以使用到2092年不到~~ + */ + private final 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 final long machineIdBits = 5L; + + /** + * 服务id占用的bit位数 + */ + private final long serviceIdBits = 5L; + + /** + * 序列占用的bit位数 + */ + private final long sequenceBits = 12L; + + /** + * 计算出机器id的最大值 + */ + private long maxMachineId = ~(-1 << machineIdBits); + + /** + * 计算出服务id的最大值 + */ + private long maxServiceId = ~(-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 << 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; + } +} 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 69749bd..2210a25 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; @@ -12,7 +13,7 @@ import org.apache.ibatis.annotations.Select; public interface ClientBalanceMapper { - @Select("select balance from client_balance where client_id = #{clientId}") - Long getBalanceByClientId(@Param("clientId") Long clientId); + @Select("select * from client_balance where client_id = #{clientId}") + ClientBalance getBalanceByClientId(@Param("clientId") Long clientId); } 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 e38805b..52dbb8d 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,5 +1,8 @@ package com.mashibing.test.mapper; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mashibing.test.entity.ClientBalance; import com.mashibing.test.feignClient.BeaconCacheClient; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; @@ -7,6 +10,8 @@ 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.jupiter.api.Assertions.*; @SpringBootTest @@ -20,10 +25,11 @@ class ClientBalanceMapperTest { ClientBalanceMapper clientBalanceMapper; @Test - void getBalanceByClientId() { - Long balance = clientBalanceMapper.getBalanceByClientId(1L); + void getBalanceByClientId() throws JsonProcessingException { + ClientBalance balance = clientBalanceMapper.getBalanceByClientId(1L); System.out.println("balance = " + balance); - - beaconCacheClient.set("client_balance:1", balance.toString()); + ObjectMapper objectMapper = new ObjectMapper(); + Map map = objectMapper.readValue(objectMapper.writeValueAsString(balance), Map.class); + beaconCacheClient.hset("client_balance:1", map); } } \ No newline at end of file