短信微服务合并到文件操作微服务,当做平台插件微服务,原短信微服务在框架中保留
1 files modified
15 files added
New file |
| | |
| | | package com.kidgrow.sms.feign; |
| | | |
| | | import com.kidgrow.common.constant.ServiceNameConstants; |
| | | import com.kidgrow.common.model.ResultBody; |
| | | import com.kidgrow.sms.feign.fallback.SmsChuangLanServiceFallbackFactory; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: <br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/27 22:04 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @FeignClient(value = ServiceNameConstants.SMS_SERVICE_SERVER, |
| | | fallbackFactory = SmsChuangLanServiceFallbackFactory.class, decode404 = true) |
| | | public interface SmsChuangLanService { |
| | | /** |
| | | * feign rpc访问远程 接口 |
| | | */ |
| | | @PostMapping(value = "/smsChangLan/send") |
| | | ResultBody sendVerificationCode(@RequestBody Map map); |
| | | |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.feign.fallback; |
| | | |
| | | import com.kidgrow.common.model.ResultBody; |
| | | import com.kidgrow.sms.feign.SmsChuangLanService; |
| | | import feign.hystrix.FallbackFactory; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: <br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/27 22:05 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | public class SmsChuangLanServiceFallbackFactory implements FallbackFactory<SmsChuangLanService> { |
| | | @Override |
| | | public SmsChuangLanService create(Throwable throwable) { |
| | | return new SmsChuangLanService() { |
| | | @Override |
| | | public ResultBody sendVerificationCode(Map ma) { |
| | | return ResultBody.failed("短息调用失败"); |
| | | } |
| | | }; |
| | | } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.model; |
| | | |
| | | import com.kidgrow.common.constant.CommonConstant; |
| | | |
| | | public interface ConstantSMS { |
| | | String PASSWORD_SMS = "PASSWORD_SMS";//修改密码 短信验证的type值 |
| | | String REGISTER_SMS = "REGISTER_SMS";//注册 短信验证的type值 |
| | | String PHONE_SMS = "PHONE_SMS";//更换手机号 短信验证的type值 |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.model; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 发送短信实体类<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:18 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @ApiModel(value = "发送短信实体类") |
| | | @Data |
| | | public class SmsModel implements Serializable { |
| | | |
| | | private String phoneNumber; //手机号 |
| | | private String key; // 短信模板key |
| | | private String code; // 短信验证码 |
| | | private Map<String,String> params; // 内容 key 值要与模板一致 |
| | | private List<String> phoneNumbers; // 群发消息时使用 |
| | | |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.client; |
| | | |
| | | import com.aliyuncs.CommonRequest; |
| | | import com.aliyuncs.CommonResponse; |
| | | import com.aliyuncs.DefaultAcsClient; |
| | | import com.aliyuncs.IAcsClient; |
| | | import com.aliyuncs.exceptions.ClientException; |
| | | import com.aliyuncs.http.MethodType; |
| | | import com.aliyuncs.profile.DefaultProfile; |
| | | import com.aliyuncs.profile.IClientProfile; |
| | | import com.google.gson.Gson; |
| | | import com.kidgrow.sms.exception.SmsException; |
| | | import com.kidgrow.sms.template.BatchSmsTemplate; |
| | | import com.kidgrow.sms.template.SmsTemplate; |
| | | import com.kidgrow.sms.util.AliyunSmsUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云 SMS 客户端<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:07 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | public class SmsClient { |
| | | private final IAcsClient acsClient; |
| | | private final Map<String, SmsTemplate> smsTemplates; |
| | | private final Gson gson = new Gson(); |
| | | |
| | | @Value("${aliyun.commonRequest.domain}") |
| | | private String domain; |
| | | |
| | | @Value("${aliyun.commonRequest.version}") |
| | | private String version; |
| | | |
| | | @Value("${aliyun.commonRequest.action}") |
| | | private String action; |
| | | |
| | | /** |
| | | * Instantiates a new SmsClient. |
| | | * |
| | | * @param accessKeyId 阿里云短信 accessKeyId |
| | | * @param accessKeySecret 阿里云短信 accessKeySecret |
| | | */ |
| | | public SmsClient(final String accessKeyId, final String accessKeySecret) { |
| | | this(accessKeyId, accessKeySecret, Collections.emptyMap()); |
| | | } |
| | | |
| | | /** |
| | | * Instantiates a new SmsClient. |
| | | * |
| | | * @param accessKeyId 阿里云短信 accessKeyId |
| | | * @param accessKeySecret 阿里云短信 accessKeySecret |
| | | * @param smsTemplates 预置短信模板 |
| | | */ |
| | | public SmsClient(final String accessKeyId, |
| | | final String accessKeySecret, |
| | | final Map<String, SmsTemplate> smsTemplates) { |
| | | AliyunSmsUtils.checkNotEmpty(accessKeyId, "'accessKeyId' must be not empty"); |
| | | AliyunSmsUtils.checkNotEmpty(accessKeySecret, "'accessKeySecret' must be not empty"); |
| | | |
| | | final IClientProfile clientProfile = DefaultProfile.getProfile( |
| | | "default", accessKeyId, accessKeySecret); |
| | | |
| | | this.acsClient = new DefaultAcsClient(clientProfile); |
| | | this.smsTemplates = smsTemplates; |
| | | } |
| | | |
| | | /** |
| | | * Instantiates a new SmsClient. |
| | | * |
| | | * @param acsClient IAcsClient |
| | | * @param smsTemplates 预置短信模板 |
| | | */ |
| | | public SmsClient(final IAcsClient acsClient, final Map<String, SmsTemplate> smsTemplates) { |
| | | this.acsClient = acsClient; |
| | | this.smsTemplates = smsTemplates; |
| | | } |
| | | |
| | | /** |
| | | * 发送短信验证码. |
| | | * |
| | | * @param phoneNumber 手机号码(中国) |
| | | * |
| | | * @return 6 位数的随机码 |
| | | */ |
| | | public int sendVerificationCode(final String smsTemplateKey, final String phoneNumber) { |
| | | AliyunSmsUtils.checkPhoneNumber(phoneNumber); |
| | | final SmsTemplate smsTemplate = this.smsTemplates.get(smsTemplateKey); |
| | | Objects.requireNonNull(smsTemplate, () -> "SmsTemplate must be not null, key:" + smsTemplateKey); |
| | | |
| | | final int code = AliyunSmsUtils.randomCode(); |
| | | smsTemplate.setTemplateParam(Collections.singletonMap("code", String.valueOf(code))); |
| | | smsTemplate.setPhoneNumbers(Collections.singletonList(phoneNumber)); |
| | | send(smsTemplate); |
| | | return code; |
| | | } |
| | | |
| | | /** |
| | | * 发送消息 支持群发 |
| | | * @param smsTemplateKey |
| | | * @param params |
| | | * @param phoneNumbers |
| | | */ |
| | | public void sendSmsMsg(final String smsTemplateKey, Map<String, String> params, final List<String> phoneNumbers){ |
| | | final SmsTemplate smsTemplate = this.smsTemplates.get(smsTemplateKey); |
| | | Objects.requireNonNull(smsTemplate, () -> "SmsTemplate must be not null, key:" + smsTemplateKey); |
| | | |
| | | smsTemplate.setTemplateParam(params); |
| | | smsTemplate.setPhoneNumbers(phoneNumbers); |
| | | send(smsTemplate); |
| | | } |
| | | |
| | | /** |
| | | * 发送短信. |
| | | * |
| | | * @param smsTemplateKey 预置短信模板 key |
| | | */ |
| | | public void send(final String smsTemplateKey) { |
| | | final SmsTemplate smsTemplate = this.smsTemplates.get(smsTemplateKey); |
| | | Objects.requireNonNull(smsTemplate, () -> "SmsTemplate must be not null, key:" + smsTemplateKey); |
| | | |
| | | send(smsTemplate); |
| | | } |
| | | |
| | | /** |
| | | * 发送短信. |
| | | * |
| | | * @param smsTemplateKey 预置短信模板 key |
| | | * @param phoneNumbers 手机号码,优先于预置短信模板中配置的手机号码 |
| | | */ |
| | | public void send(final String smsTemplateKey, final String... phoneNumbers) { |
| | | final SmsTemplate smsTemplate = this.smsTemplates.get(smsTemplateKey); |
| | | Objects.requireNonNull(smsTemplate, () -> "SmsTemplate must be not null, key:" + smsTemplateKey); |
| | | |
| | | smsTemplate.setPhoneNumbers(Arrays.asList(phoneNumbers)); |
| | | send(smsTemplate); |
| | | } |
| | | |
| | | /** |
| | | * 发送短信. |
| | | * |
| | | * @param smsTemplate 短信模板 |
| | | */ |
| | | public void send(final SmsTemplate smsTemplate) { |
| | | Objects.requireNonNull(smsTemplate); |
| | | AliyunSmsUtils.checkSmsTemplate(smsTemplate); |
| | | |
| | | final CommonRequest request = new CommonRequest(); |
| | | request.setSysMethod(MethodType.POST); |
| | | request.setSysDomain(domain); |
| | | request.setSysVersion(version); |
| | | request.setSysAction(action); |
| | | request.putQueryParameter("PhoneNumbers", String.join(",", smsTemplate.getPhoneNumbers())); |
| | | request.putQueryParameter("SignName", smsTemplate.getSignName()); |
| | | request.putQueryParameter("TemplateCode", smsTemplate.getTemplateCode()); |
| | | request.putQueryParameter("TemplateParam", AliyunSmsUtils.toJsonStr(smsTemplate.getTemplateParam())); |
| | | try { |
| | | final CommonResponse response = this.acsClient.getCommonResponse(request); |
| | | AliyunSmsUtils.checkSmsResponse(response); |
| | | } |
| | | catch (final ClientException e) { |
| | | throw new SmsException(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 批量发送短信. |
| | | * |
| | | * <p> |
| | | * 批量发送短信接口,支持在一次请求中分别向多个不同的手机号码发送不同签名的短信。 |
| | | * 手机号码,签名,模板参数字段个数相同,一一对应,短信服务根据字段的顺序判断发往指定手机号码的签名。 |
| | | * |
| | | * <p> |
| | | * 如果您需要往多个手机号码中发送同样签名的短信,请使用 {@link #send(SmsTemplate)}。 |
| | | * |
| | | * @param batchSmsTemplate 批量发送短信模板 |
| | | */ |
| | | public void send(final BatchSmsTemplate batchSmsTemplate) { |
| | | Objects.requireNonNull(batchSmsTemplate); |
| | | AliyunSmsUtils.checkBatchSmsTemplate(batchSmsTemplate); |
| | | |
| | | final CommonRequest request = new CommonRequest(); |
| | | request.setSysMethod(MethodType.POST); |
| | | request.setSysDomain("dysmsapi.aliyuncs.com"); |
| | | request.setSysVersion("2017-05-25"); |
| | | request.setSysAction("SendBatchSms"); |
| | | request.putQueryParameter("PhoneNumberJson", this.gson.toJson(batchSmsTemplate.getPhoneNumbers())); |
| | | request.putQueryParameter("SignNameJson", this.gson.toJson(batchSmsTemplate.getSignNames())); |
| | | request.putQueryParameter("TemplateCode", batchSmsTemplate.getTemplateCode()); |
| | | request.putQueryParameter("TemplateParamJson", this.gson.toJson(batchSmsTemplate.getTemplateParams())); |
| | | try { |
| | | final CommonResponse response = this.acsClient.getCommonResponse(request); |
| | | AliyunSmsUtils.checkSmsResponse(response); |
| | | } |
| | | catch (final ClientException e) { |
| | | throw new SmsException(e); |
| | | } |
| | | } |
| | | |
| | | // public static void main(String[] args) { |
| | | // Map<String, SmsTemplate> smsTemplates = new HashMap<>(); |
| | | // SmsTemplate smsTemplate = SmsTemplate.builder() |
| | | // .signName("模板名称") |
| | | // .templateCode("模板code") |
| | | // .addTemplateParam("code", "123456") |
| | | // .build(); |
| | | // smsTemplates.put("aaa",smsTemplate); |
| | | // SmsClient smsClient = new SmsClient("aaaa","aaaaa",smsTemplates); |
| | | // smsClient.sendVerificationCode("aaa","18503198351"); |
| | | // } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.exception; |
| | | |
| | | import com.kidgrow.common.exception.KidgrowException; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: <br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 08:57 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | public class SmsException extends KidgrowException { |
| | | /** |
| | | * Instantiates a new SmsException. |
| | | * |
| | | * @param message the message |
| | | */ |
| | | public SmsException(final String message) { |
| | | super(message); |
| | | } |
| | | |
| | | /** |
| | | * Instantiates a new SmsException. |
| | | * |
| | | * @param cause the cause |
| | | */ |
| | | public SmsException(final Throwable cause) { |
| | | super(cause); |
| | | } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.service; |
| | | |
| | | import com.kidgrow.common.model.ResultBody; |
| | | import com.kidgrow.sms.model.SmsModel; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 对外API 短信发送和验证码校验接口 feignClient 继承这个接口 即可调用<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:20 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @Api(tags = "短信发送服务") |
| | | public interface AliyunSmsService { |
| | | |
| | | /** |
| | | * 发送手机验证码 |
| | | * @param smsModel |
| | | */ |
| | | @ApiOperation(value = "发送短信验证码接口") |
| | | @PostMapping("/send/verificationCode") |
| | | ResultBody sendVerificationCode(@RequestBody SmsModel smsModel); |
| | | |
| | | /** |
| | | * 校验手机验证码是否正确 |
| | | * @param smsModel |
| | | */ |
| | | @ApiOperation(value = "验证短信验证码接口") |
| | | @PostMapping("/validate/verificationCode") |
| | | ResultBody validateVerificationCode(@RequestBody SmsModel smsModel); |
| | | |
| | | /** |
| | | * 发送手机短信消息 |
| | | * @param smsModel |
| | | */ |
| | | @ApiOperation(value = "发送手机短信消息接口") |
| | | @PostMapping("/send/smsMsg") |
| | | ResultBody sendSmsMsg(@RequestBody SmsModel smsModel); |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.template; |
| | | |
| | | import lombok.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云调用批量模板<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:04 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @Builder(builderClassName = "Builder", toBuilder = true) |
| | | @Getter |
| | | @Setter |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class BatchSmsTemplate { |
| | | private List<String> signNames; |
| | | private String templateCode; |
| | | private List<Map<String, String>> templateParams; |
| | | private List<String> phoneNumbers; |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.template; |
| | | |
| | | import lombok.*; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云调用模板<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:04 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @Builder(builderClassName = "Builder", toBuilder = true) |
| | | @Getter |
| | | @Setter |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class SmsTemplate { |
| | | private String signName; |
| | | private String templateCode; |
| | | private Map<String, String> templateParam; |
| | | private List<String> phoneNumbers; |
| | | |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.util; |
| | | |
| | | import com.aliyuncs.CommonResponse; |
| | | import com.google.gson.Gson; |
| | | import com.kidgrow.sms.exception.SmsException; |
| | | import com.kidgrow.sms.template.BatchSmsTemplate; |
| | | import com.kidgrow.sms.template.SmsTemplate; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云短信服务工具类<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 08:53 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | public class AliyunSmsUtils { |
| | | private static final String SUCCESS_CODE = "OK"; |
| | | /** |
| | | * 宽松校验即可. |
| | | */ |
| | | private static final String PHONE_NUMBER_REGEX = "\\d{5,}"; |
| | | |
| | | /** |
| | | * 生成随机验证码. |
| | | * |
| | | * @return 随机数 |
| | | */ |
| | | public static int randomCode() { |
| | | return 100_000 + ThreadLocalRandom.current().nextInt(1_000_000 - 100_000); |
| | | } |
| | | |
| | | /** |
| | | * Map 转 json 字符串的简单实现. |
| | | * |
| | | * @param map the map |
| | | * |
| | | * @return the json string |
| | | */ |
| | | public static String toJsonStr(final Map<String, String> map) { |
| | | if (null == map || map.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | final StringBuilder sb = new StringBuilder(); |
| | | sb.append('{'); |
| | | for (final Map.Entry<String, String> entry : map.entrySet()) { |
| | | sb.append('"') |
| | | .append(entry.getKey().replace("\"", "\\\"")) |
| | | .append('"') |
| | | .append(':') |
| | | .append('"') |
| | | .append(entry.getValue().replace("\"", "\\\"")) |
| | | .append('"') |
| | | .append(','); |
| | | } |
| | | sb.deleteCharAt(sb.length() - 1); |
| | | sb.append('}'); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 校验 SmsTemplate. |
| | | * |
| | | * @param template the SmsTemplate |
| | | */ |
| | | public static void checkSmsTemplate(final SmsTemplate template) { |
| | | |
| | | checkNotEmpty(template.getSignName(), "SmsTemplate signName must be not empty"); |
| | | checkNotEmpty(template.getTemplateCode(), "SmsTemplate templateCode must be not empty"); |
| | | checkNotEmpty(template.getPhoneNumbers(), "SmsTemplate phoneNumbers must be not empty"); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 校验 BatchSmsTemplate. |
| | | * |
| | | * @param template the BatchSmsTemplate |
| | | */ |
| | | public static void checkBatchSmsTemplate(final BatchSmsTemplate template) { |
| | | |
| | | checkNotEmpty(template.getSignNames(), "BatchSmsTemplate signNames must be not empty"); |
| | | checkNotEmpty(template.getPhoneNumbers(), "BatchSmsTemplate phoneNumbers must be not empty"); |
| | | checkNotEmpty(template.getTemplateCode(), "BatchSmsTemplate templateCode must be not empty"); |
| | | checkNotEmpty(template.getTemplateParams(), "BatchSmsTemplate templateParams must be not empty"); |
| | | |
| | | if (template.getSignNames().size() != template.getPhoneNumbers().size() |
| | | && template.getPhoneNumbers().size() != template.getTemplateParams().size()) { |
| | | throw new IllegalArgumentException("BatchSmsTemplate phoneNumbers, signNames, templateParams size must be the same"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验 SendSmsResponse 状态. |
| | | * |
| | | * @param response the SendSmsResponse |
| | | */ |
| | | public static void checkSmsResponse(final CommonResponse response) { |
| | | if (null == response) { |
| | | throw new SmsException("Response is null"); |
| | | } |
| | | final Gson gson = new Gson(); |
| | | final Map<String, String> json = gson.fromJson(response.getData(), Map.class); |
| | | if (!SUCCESS_CODE.equalsIgnoreCase(json.get("Code"))) { |
| | | //throw new SmsException("Http status: " + response.getHttpStatus() + ", response: " + response.getData()); |
| | | throw new SmsException(response.getData()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验手机号码(中国). |
| | | * |
| | | * @param phoneNumber the phone number |
| | | */ |
| | | public static void checkPhoneNumber(final String phoneNumber) { |
| | | if (null == phoneNumber || !phoneNumber.matches(PHONE_NUMBER_REGEX)) { |
| | | throw new IllegalArgumentException("Invalid phone number"); |
| | | } |
| | | } |
| | | |
| | | /** <br> 2018年3月已知 |
| | | 中国电信号段 |
| | | 133,149,153,173,177,180,181,189,199 |
| | | 中国联通号段 |
| | | 130,131,132,145,155,156,166,175,176,185,186 |
| | | 中国移动号段 |
| | | 134(0-8),135,136,137,138,139,147,150,151,152,157,158,159,178,182,183,184,187,188,198 |
| | | 其他号段 |
| | | 14号段以前为上网卡专属号段,如中国联通的是145,中国移动的是147等等。 |
| | | 虚拟运营商 |
| | | 电信:1700,1701,1702 |
| | | 移动:1703,1705,1706 |
| | | 联通:1704,1707,1708,1709,171 |
| | | 卫星通信:148(移动) 1349 |
| | | */ |
| | | public static boolean isMobile(String str) { |
| | | boolean b = false; |
| | | String s2="^[1](([3][0-9])|([4][5,7,9])|([5][0-9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]{8}$";// 验证手机号 |
| | | if(StringUtils.isNotBlank(str)){ |
| | | return str.matches(s2); |
| | | } |
| | | return b; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 校验字符串不为空. |
| | | * |
| | | * @param str the str |
| | | * @param message the message |
| | | */ |
| | | public static void checkNotEmpty(final String str, final String message) { |
| | | if (null == str || str.isEmpty()) { |
| | | throw new IllegalArgumentException(message); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验集合不为空. |
| | | * |
| | | * @param coll the Collection |
| | | * @param message the message |
| | | */ |
| | | public static void checkNotEmpty(final Collection coll, final String message) { |
| | | if (null == coll || coll.isEmpty()) { |
| | | throw new IllegalArgumentException(message); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.util; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import java.io.BufferedReader; |
| | | import java.io.InputStreamReader; |
| | | import java.io.OutputStream; |
| | | import java.net.HttpURLConnection; |
| | | import java.net.URL; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | public class SmsChuangLanUtils { |
| | | public static void main(String[] args) { |
| | | //短信下发 |
| | | String sendUrl = "http://smssh1.253.com/msg/send/json"; |
| | | Map map = new HashMap(); |
| | | map.put("account","N2561124");//API账号 |
| | | map.put("password","Mguj6qlRWX7b5e");//API密码 |
| | | map.put("msg","123123");//短信内容 |
| | | map.put("phone","18600376209");//手机号 |
| | | map.put("report","false");//是否需要状态报告 |
| | | JSONObject js = (JSONObject) JSONObject.toJSON(map); |
| | | System.out.println(sendSmsByPost(sendUrl,js.toString())); |
| | | } |
| | | public static String send(Map map,String url){ |
| | | JSONObject js = (JSONObject) JSONObject.toJSON(map); |
| | | return sendSmsByPost(url,js.toString()); |
| | | |
| | | } |
| | | public static String sendSmsByPost(String path, String postContent) { |
| | | URL url = null; |
| | | try { |
| | | url = new URL(path); |
| | | HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); |
| | | httpURLConnection.setRequestMethod("POST"); |
| | | httpURLConnection.setConnectTimeout(10000); |
| | | httpURLConnection.setReadTimeout(10000); |
| | | httpURLConnection.setDoOutput(true); |
| | | httpURLConnection.setDoInput(true); |
| | | httpURLConnection.setRequestProperty("Charset", "UTF-8"); |
| | | httpURLConnection.setRequestProperty("Content-Type", "application/json"); |
| | | httpURLConnection.connect(); |
| | | OutputStream os=httpURLConnection.getOutputStream(); |
| | | os.write(postContent.getBytes("UTF-8")); |
| | | os.flush(); |
| | | StringBuilder sb = new StringBuilder(); |
| | | int httpRspCode = httpURLConnection.getResponseCode(); |
| | | if (httpRspCode == HttpURLConnection.HTTP_OK) { |
| | | BufferedReader br = new BufferedReader( |
| | | new InputStreamReader(httpURLConnection.getInputStream(), "utf-8")); |
| | | String line = null; |
| | | while ((line = br.readLine()) != null) { |
| | | sb.append(line); |
| | | } |
| | | br.close(); |
| | | return sb.toString(); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.aliyun.config; |
| | | |
| | | import com.kidgrow.sms.client.SmsClient; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| | | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云 SMS 自动配置<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:11 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @Configuration |
| | | @ConditionalOnClass(name = "com.aliyuncs.IAcsClient") |
| | | @EnableConfigurationProperties(SmsProperties.class) |
| | | public class SmsAutoConfiguration { |
| | | private final SmsProperties smsProperties; |
| | | |
| | | public SmsAutoConfiguration(final SmsProperties smsProperties) { |
| | | this.smsProperties = smsProperties; |
| | | } |
| | | |
| | | /** |
| | | * Configuration SmsClient bean. |
| | | * |
| | | * @return the sms client |
| | | */ |
| | | @Bean |
| | | @ConditionalOnMissingBean |
| | | public SmsClient smsClient() { |
| | | if (this.smsProperties.getTemplates() == null) { |
| | | return new SmsClient(this.smsProperties.getAccessKeyId(), this.smsProperties.getAccessKeySecret()); |
| | | } else { |
| | | return new SmsClient( |
| | | this.smsProperties.getAccessKeyId(), |
| | | this.smsProperties.getAccessKeySecret(), |
| | | this.smsProperties.getTemplates()); |
| | | } |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | package com.kidgrow.sms.aliyun.config; |
| | | |
| | | import com.kidgrow.sms.template.SmsTemplate; |
| | | import lombok.Data; |
| | | import org.springframework.beans.factory.InitializingBean; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: 阿里云 SMS 配置属性<br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:10 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @Data |
| | | @ConfigurationProperties(prefix = "aliyun.sms") |
| | | public class SmsProperties implements InitializingBean { |
| | | |
| | | private String accessKeyId; |
| | | private String accessKeySecret; |
| | | private String signName; |
| | | private Map<String, SmsTemplate> templates; |
| | | |
| | | @Override |
| | | public void afterPropertiesSet() throws Exception { |
| | | if (null != this.templates) { |
| | | for (final SmsTemplate smsTemplate : this.templates.values()) { |
| | | if (null == smsTemplate.getSignName()) { |
| | | smsTemplate.setSignName(this.signName); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.kidgrow.sms.aliyun.controller; |
| | | |
| | | import com.kidgrow.common.base.ResponseCode; |
| | | import com.kidgrow.common.model.ResultBody; |
| | | import com.kidgrow.redis.template.RedisRepository; |
| | | import com.kidgrow.sms.client.SmsClient; |
| | | import com.kidgrow.sms.exception.SmsException; |
| | | import com.kidgrow.sms.service.AliyunSmsService; |
| | | import com.kidgrow.sms.model.SmsModel; |
| | | import com.kidgrow.sms.util.AliyunSmsUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020<br> |
| | | * |
| | | * @Description: <br> |
| | | * @Project: <br> |
| | | * @CreateDate: Created in 2020/2/17 09:15 <br> |
| | | * @Author: <a href="4345453@kidgrow.com">liuke</a> |
| | | */ |
| | | @RestController |
| | | @RequestMapping("sms") |
| | | public class SmsController implements AliyunSmsService { |
| | | |
| | | @Autowired |
| | | private SmsClient smsClient; |
| | | |
| | | @Autowired |
| | | private RedisRepository redisRepository; |
| | | |
| | | @Value("${sms.verificationCode.timeOut:180}") |
| | | private Long timeOut; |
| | | |
| | | @Value("${sms.verificationCode.prefix:default}") |
| | | private String smsPrefix; |
| | | |
| | | /** |
| | | * 发送短信验证码 |
| | | * |
| | | * @param smsModel |
| | | */ |
| | | @Override |
| | | public ResultBody sendVerificationCode(@RequestBody SmsModel smsModel) { |
| | | String phoneNumber = smsModel.getPhoneNumber(); |
| | | String key = smsModel.getKey(); |
| | | if (StringUtils.isBlank(phoneNumber) || StringUtils.isBlank(key)) { |
| | | return ResultBody.fail(ResponseCode.ERROR_PARAMS, "手机号或短信模板不能为空!"); |
| | | } |
| | | if (!AliyunSmsUtils.isMobile(phoneNumber)) { |
| | | return ResultBody.fail(ResponseCode.ERROR_PARAMS, "手机号格式不正确!"); |
| | | } |
| | | try { |
| | | // 发送验证码 |
| | | Integer code = smsClient.sendVerificationCode(key, phoneNumber); |
| | | // 缓存验证码和有效期 |
| | | redisRepository.setExpire(smsPrefix + phoneNumber, code.toString(), timeOut); |
| | | // redisRepository.expire(smsPrefix + phoneNumber, timeOut); |
| | | return ResultBody.ok(); |
| | | } catch (SmsException smsEx) { |
| | | smsEx.printStackTrace(); |
| | | return ResultBody.failed().msg(smsEx.getMessage()); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultBody.failed().msg(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证码校验 |
| | | * |
| | | * @param smsModel |
| | | */ |
| | | @Override |
| | | public ResultBody validateVerificationCode(@RequestBody SmsModel smsModel) { |
| | | String phoneNumber = smsModel.getPhoneNumber(); |
| | | String code = smsModel.getCode(); |
| | | if (StringUtils.isBlank(phoneNumber) || StringUtils.isBlank(code)) { |
| | | return ResultBody.failed(ResponseCode.ERROR_PARAMS, "手机号或验证码不能为空!"); |
| | | } |
| | | try { |
| | | if (redisRepository.exists(smsPrefix + phoneNumber)) { |
| | | String rightCode = (String) redisRepository.get(smsPrefix + phoneNumber); |
| | | if (code.equals(rightCode)) { |
| | | // 验证成功 移除缓存验证码 |
| | | redisRepository.del(smsPrefix + phoneNumber); |
| | | return ResultBody.ok(); |
| | | } else { |
| | | return ResultBody.failed(ResponseCode.ERROR_PARAMS, "验证码不正确!"); |
| | | } |
| | | } else { |
| | | return ResultBody.failed(ResponseCode.ERROR_PARAMS, "验证码已失效!"); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultBody.failed("系统错误"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 发送手机短信消息 |
| | | * |
| | | * @param smsModel |
| | | * @return |
| | | */ |
| | | @Override |
| | | public ResultBody sendSmsMsg(SmsModel smsModel) { |
| | | List<String> phoneNumbers = smsModel.getPhoneNumbers(); |
| | | String key = smsModel.getKey(); |
| | | // if ((phoneNumbers == null || phoneNumbers.isEmpty()) || StringUtils.isBlank(key)) { |
| | | // return ResponseBuilder.buildResultError(ResponseCode.ERROR_PARAMS, "手机号或短信模板不能为空!"); |
| | | // } |
| | | |
| | | try { |
| | | // 发送手机短信消息 |
| | | smsClient.sendSmsMsg(key, smsModel.getParams(), phoneNumbers); |
| | | return ResultBody.ok(); |
| | | } catch (SmsException smsEx) { |
| | | smsEx.printStackTrace(); |
| | | return ResultBody.failed(smsEx.getMessage()); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultBody.failed(e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | package com.kidgrow.sms.lanchuang.controller; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.kidgrow.common.model.ResultBody; |
| | | import com.kidgrow.common.utils.DateUtils; |
| | | import com.kidgrow.redis.util.RedisUtils; |
| | | import com.kidgrow.sms.util.SmsChuangLanUtils; |
| | | import io.swagger.annotations.Api; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.*; |
| | | |
| | | @RestController |
| | | @RequestMapping("smsChangLan") |
| | | @Api(tags = "创蓝短信模块") |
| | | public class SmsChuangLanController { |
| | | @Value("${chuanglan.sms.sendUrl}") |
| | | private String CHUANGLAN_SMS_SENDURL; |
| | | @Value("${chuanglan.sms.account}") |
| | | private String CHUANGLAN_SMS_ACCOUNT; |
| | | @Value("${chuanglan.sms.password}") |
| | | private String CHUANGLAN_SMS_PASSWORD; |
| | | @Value("${chuanglan.sms.num}") |
| | | private Integer CHUANGLAN_SMS_NUM; |
| | | @Value("${chuanglan.sms.timeLimit}") |
| | | private Integer CHUANGLAN_SMS_TIMELIMIT; |
| | | @Autowired |
| | | private RedisUtils redisUtils; |
| | | |
| | | /** |
| | | * type 为类型, "type":"LOGIN_SMS" 为登录,"type":"PASSWORD_SMS" 为密码找回,"type":"REGISTER_SMS" 为 |
| | | * @param map |
| | | * @return |
| | | */ |
| | | @PostMapping("send") |
| | | public ResultBody sendVerificationCode(@RequestBody Map map) { |
| | | if(CHUANGLAN_SMS_NUM==null){ |
| | | CHUANGLAN_SMS_NUM=5; |
| | | } |
| | | Object phone = map.get("phone"); |
| | | if (map.get("phone")==null||"".equals(map.get("phone"))){ |
| | | return ResultBody.failed("请输入手机号"); |
| | | } |
| | | Object type = map.get("type"); |
| | | if (map.get("type")==null||"".equals(map.get("type"))){ |
| | | return ResultBody.failed("请输入保存的类型"); |
| | | } |
| | | String verificationCode=""; |
| | | Random random=new Random(); |
| | | for (int i = 0; i < 6; i++) { |
| | | int i1 = random.nextInt(9); |
| | | verificationCode+=i1; |
| | | } |
| | | try { |
| | | //组装发送消息的内容 |
| | | map.put("account", CHUANGLAN_SMS_ACCOUNT);//API账号 |
| | | map.put("password", CHUANGLAN_SMS_PASSWORD);//API密码 |
| | | map.put("report","false"); |
| | | map.put("msg","您好,您的验证码是"+verificationCode); |
| | | //在Redis中获取 |
| | | Object hget = redisUtils.hget(map.get("type").toString(), map.get("phone").toString()); |
| | | JSONObject jsonObject =new JSONObject(); |
| | | Map<String,Object> cunMap=new HashMap<>(); |
| | | cunMap.put("verificationCode",verificationCode); |
| | | cunMap.put("count",CHUANGLAN_SMS_NUM);//总共 |
| | | if(hget==null){ |
| | | String send = SmsChuangLanUtils.send(map, CHUANGLAN_SMS_SENDURL); |
| | | jsonObject=JSON.parseObject(send); |
| | | String s1 = DateUtils.formatCurrentDateTime(); |
| | | cunMap.put("date",s1); |
| | | cunMap.put("remnant",CHUANGLAN_SMS_NUM-1);//剩余 |
| | | Date date2 = DateUtils.addMilliseconds(DateUtils.parseDate(s1), CHUANGLAN_SMS_TIMELIMIT); |
| | | cunMap.put("endTime",DateUtils.formatDateTime(date2)); |
| | | }else { |
| | | JSONObject redisJson = JSON.parseObject(JSON.toJSONString(hget)); |
| | | Object remnantObject = redisJson.get("remnant"); |
| | | if(remnantObject instanceof Integer){ |
| | | Integer remnant=(Integer)remnantObject; |
| | | if(remnant>0){ |
| | | String send = SmsChuangLanUtils.send(map, CHUANGLAN_SMS_SENDURL); |
| | | jsonObject=JSON.parseObject(send); |
| | | String s1 = DateUtils.formatCurrentDateTime(); |
| | | cunMap.put("date",DateUtils.formatCurrentDateTime()); |
| | | cunMap.put("remnant",remnant-1);//剩余 |
| | | Date date2 = DateUtils.addMilliseconds(DateUtils.parseDate(s1), CHUANGLAN_SMS_TIMELIMIT); |
| | | cunMap.put("endTime",DateUtils.formatDateTime(date2)); |
| | | }else{ |
| | | return ResultBody.failed().data("每个手机号限用"+CHUANGLAN_SMS_NUM+"次"); |
| | | } |
| | | }else { |
| | | return ResultBody.failed(); |
| | | } |
| | | } |
| | | if ("0".equals(jsonObject.get("code"))) { |
| | | redisUtils.hset(map.get("type").toString(), map.get("phone").toString(),cunMap,DateUtils.getSecondsNextEarlyMorning()); |
| | | // return ResultBody.ok().data(verificationCode); |
| | | return ResultBody.ok(); |
| | | } else { |
| | | return ResultBody.failed(); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return ResultBody.failed(); |
| | | } |
| | | } |
| | | @PostMapping("verification") |
| | | public ResultBody verification(@RequestBody Map<String,Object> map){ |
| | | Object phone = map.get("phone"); |
| | | if (map.get("phone")==null||"".equals(map.get("phone"))){ |
| | | return ResultBody.failed("请输入手机号"); |
| | | } |
| | | Object verificationCode = map.get("verificationCode"); |
| | | if (map.get("verificationCode")==null||"".equals(map.get("verificationCode"))){ |
| | | return ResultBody.failed("请输入保存的类型"); |
| | | } |
| | | Object type = map.get("type"); |
| | | if (map.get("type")==null||"".equals(map.get("type"))){ |
| | | return ResultBody.failed("请输入保存的类型"); |
| | | } |
| | | Object hget = redisUtils.hget(map.get("type").toString(), map.get("phone").toString()); |
| | | if(hget!=null){ |
| | | JSONObject redisJson = JSON.parseObject(JSON.toJSONString(hget)); |
| | | Object verificationCodeObject = redisJson.get("verificationCode"); |
| | | if(verificationCode.equals(verificationCodeObject)){ |
| | | Object date = redisJson.get("date"); |
| | | long time = DateUtils.addMilliseconds(DateUtils.parseDate(date.toString()), CHUANGLAN_SMS_TIMELIMIT).getTime(); |
| | | Date dateNow=new Date(); |
| | | long timeNow = dateNow.getTime(); |
| | | if(timeNow<=time){ |
| | | return ResultBody.ok(); |
| | | }else { |
| | | return ResultBody.failed("验证码超时"); |
| | | } |
| | | }else { |
| | | return ResultBody.failed("验证码错误"); |
| | | } |
| | | }else { |
| | | return ResultBody.failed("该手机号没有验证码"); |
| | | } |
| | | } |
| | | //获取 存放 Redis的时间 秒(到今晚的秒数) |
| | | public Long getSecondsNextEarlyMorning() { |
| | | Date afterDay = DateUtils.getAfterDay(new Date()); |
| | | String s = DateUtils.formatDate(afterDay, null); |
| | | Date date = DateUtils.parseDate(s); |
| | | System.out.println(date.getTime()-System.currentTimeMillis()); |
| | | long second= (date.getTime()-System.currentTimeMillis())/1000; |
| | | return second; |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | |
| | | |
| | | kidgrow: |
| | | # fdfs: |
| | | # web-url: 192.168.28.130 |
| | |
| | | # trackerList: ${kidgrow.fdfs.trackerList} |
| | | |
| | | |
| | | #创蓝 |
| | | chuanglan: |
| | | sms: |
| | | sendUrl: http://smssh1.253.com/msg/send/json |
| | | account: N2561124 |
| | | password: Mguj6qlRWX7b5e |
| | | num: 5 |
| | | timeLimit: 720000 |
| | | #阿里云相关 |
| | | aliyun: |
| | | sms: |
| | | accessKeyId: LTAIX1whVSBUUKNW |
| | | accessKeySecret: j7UywJDoUkPRvGafKrik1pyYtEC2ys |
| | | signName: 微课堂 |
| | | templates: |
| | | ### 模板key |
| | | key1: |
| | | ## 模板code 需要在阿里云控制台设置 |
| | | templateCode: SMS_151231928 # 与阿里云设置的保持一致,模板内容需在后面加上,方便使用:**** |
| | | key2: |
| | | templateCode: SMS_105665033 # 与阿里云设置的保持一致,模板内容需在后面加上,方便使用:**** |
| | | commonRequest: |
| | | domain: dysmsapi.aliyuncs.com |
| | | ## 版本号 |
| | | version: 2017-05-25 |
| | | ## |
| | | action: SendSms |
| | | |
| | | ## 短信验证码过期时间,时间单位/秒 |
| | | sms: |
| | | verificationCode: |
| | | ## 短信验证码过期时间,时间单位/秒 |
| | | timeOut: 120 |
| | | ## 缓存手机号key值前缀 |
| | | prefix: "sms:aliyun:code:" |