From 6dc432d49c80ed049275a5aee3c2f38ddb837d72 Mon Sep 17 00:00:00 2001 From: sin Date: Sun, 19 May 2019 16:24:33 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E6=B7=BB=E5=8A=A0=E7=9F=AD=E4=BF=A1=20?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=EF=BC=8C=E6=89=B9=E9=87=8F=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iocoder/mall/admin/api/SmsPlatform.java | 32 ++++++ .../cn/iocoder/mall/admin/api/SmsService.java | 19 ++++ .../mall/admin/api/bo/sms/SmsPage.java | 53 ++++++++++ .../api/constant/AdminErrorCodeEnum.java | 2 +- .../mall/admin/service/SmsServiceImpl.java | 37 +++++++ .../admin/service/SmsYunPianPlatform.java | 99 +++++++++++++++++-- .../admin/service/SmsServiceImplTest.java | 6 ++ 7 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsPage.java diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java index c2f8f1178..b9e927754 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsPlatform.java @@ -3,6 +3,8 @@ package cn.iocoder.mall.admin.api; import lombok.Data; import lombok.experimental.Accessors; +import java.util.List; + /** * 短信平台 * @@ -81,4 +83,34 @@ public interface SmsPlatform { * @return */ Result deleteTemplate(String tplId); + + + @Data + @Accessors(chain = true) + class SendResult { + + private Boolean hasSuccess; + + private Integer code; + + private String message; + + private List success; + + private List fail; + } + + /** + * 短信发送 - 单个 + * + * @return + */ + SendResult singleSend(String mobile, String template); + + /** + * 短信发送 - 批量 + * + * @return + */ + SendResult batchSend(List mobileList, String template); } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java index ee5079358..9e653106a 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/SmsService.java @@ -3,6 +3,8 @@ package cn.iocoder.mall.admin.api; import cn.iocoder.mall.admin.api.bo.sms.SmsSignBO; import cn.iocoder.mall.admin.api.bo.sms.SmsTemplateBO; +import java.util.List; + /** * 短信服务 * @@ -11,6 +13,8 @@ import cn.iocoder.mall.admin.api.bo.sms.SmsTemplateBO; */ public interface SmsService { + + /** * 签名 - 创建 * @@ -64,4 +68,19 @@ public interface SmsService { * @param id */ void deleteTemplate(Integer id); + + + /** + * 短信发送 - 单个 + * + * @return + */ + void singleSend(String mobile, Integer smsTemplateId); + + /** + * 短信发送 - 批量 + * + * @return + */ + void batchSend(List mobileList, Integer smsTemplateId); } diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsPage.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsPage.java new file mode 100644 index 000000000..373b8d914 --- /dev/null +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/bo/sms/SmsPage.java @@ -0,0 +1,53 @@ +package cn.iocoder.mall.admin.api.bo.sms; + +import com.baomidou.mybatisplus.core.metadata.IPage; + +import java.util.List; + +/** + * + * + * @author Sin + * @time 2019/5/19 4:23 PM + */ +public class SmsPage implements IPage { + @Override + public List getRecords() { + return null; + } + + @Override + public IPage setRecords(List records) { + return null; + } + + @Override + public long getTotal() { + return 0; + } + + @Override + public IPage setTotal(long total) { + return null; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public IPage setSize(long size) { + return null; + } + + @Override + public long getCurrent() { + return 0; + } + + @Override + public IPage setCurrent(long current) { + return null; + } +} diff --git a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java index 57787489d..bde9139ee 100644 --- a/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java +++ b/system/system-service-api/src/main/java/cn/iocoder/mall/admin/api/constant/AdminErrorCodeEnum.java @@ -51,7 +51,7 @@ public enum AdminErrorCodeEnum { DATA_DICT_NOT_EXISTS(1002005001, "该数据字典不存在"), // ========== 短信模板 1002006000 ========== - SMS_PLATFORM_FAIL(1002006000, "短信模板添加失败"), + SMS_PLATFORM_FAIL(1002006000, "短信平台调用失败【具体错误会动态替换】"), SMS_SIGN_NOT_EXISTENT(1002006001, "短信签名不存在"), SMS_SIGN_IS_EXISTENT(1002006002, "短信签名已存在"), SMS_TEMPLATE_NOT_EXISTENT(1002006020, "短信签名不存在"), diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java index 296aa0777..1d8db2610 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; +import java.util.List; /** * 短信 @@ -203,4 +204,40 @@ public class SmsServiceImpl implements SmsService { smsTemplateMapper.delete(new UpdateWrapper() .set("deleted", DeletedStatusEnum.DELETED_YES).eq("id", id)); } + + @Override + public void singleSend(String mobile, Integer smsTemplateId) { + SmsTemplateDO smsTemplateDO = smsTemplateMapper.selectOne( + new QueryWrapper().eq("id", smsTemplateId)); + + if (smsTemplateDO == null + || smsTemplateDO.getDeleted().equals(DeletedStatusEnum.DELETED_YES.getValue())) { + throw new ServiceException(AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getCode(), + AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getMessage()); + } + + SmsSignDO smsSignDO = smsSignMapper.selectOne( + new QueryWrapper().eq("id", smsTemplateDO.getSmsSignId())); + + smsPlatform.singleSend(mobile, + String.format(SMS_TEMPLATE, smsSignDO.getSign(), smsTemplateDO.getTemplate())); + } + + @Override + public void batchSend(List mobileList, Integer smsTemplateId) { + SmsTemplateDO smsTemplateDO = smsTemplateMapper.selectOne( + new QueryWrapper().eq("id", smsTemplateId)); + + if (smsTemplateDO == null + || smsTemplateDO.getDeleted().equals(DeletedStatusEnum.DELETED_YES.getValue())) { + throw new ServiceException(AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getCode(), + AdminErrorCodeEnum.SMS_TEMPLATE_NOT_EXISTENT.getMessage()); + } + + SmsSignDO smsSignDO = smsSignMapper.selectOne( + new QueryWrapper().eq("id", smsTemplateDO.getSmsSignId())); + + smsPlatform.batchSend(mobileList, + String.format(SMS_TEMPLATE, smsSignDO.getSign(), smsTemplateDO.getTemplate())); + } } diff --git a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java index f23af2739..8c82fef4f 100644 --- a/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java +++ b/system/system-service-impl/src/main/java/cn/iocoder/mall/admin/service/SmsYunPianPlatform.java @@ -16,15 +16,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; +import org.assertj.core.util.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 云片 短信平台 @@ -44,8 +42,6 @@ public class SmsYunPianPlatform implements SmsPlatform { "https://sms.yunpian.com/v2/user/get.json"; //智能匹配模板发送接口的http地址 - private static final String URI_SEND_SMS = - "https://sms.yunpian.com/v2/sms/single_send.json"; //模板发送接口的http地址 private static final String URI_TPL_SEND_SMS = @@ -91,6 +87,15 @@ public class SmsYunPianPlatform implements SmsPlatform { * 模板 - 删除 */ private static final String URL_TEMPLATE_DELETE = "https://sms.yunpian.com/v2/tpl/del.json"; + /** + * 短信发送 - 单个 + */ + private static final String URL_SEND_SINGLE = "https://sms.yunpian.com/v2/sms/single_send.json"; + /** + * 短信发送 - 批量 + */ + private static final String URL_SEND_BATCH = "https://sms.yunpian.com/v2/sms/batch_send.json"; + //编码格式。发送编码格式统一用UTF-8 private static String ENCODING = "UTF-8"; @@ -266,6 +271,88 @@ public class SmsYunPianPlatform implements SmsPlatform { return new Result().setId(tplId).setApplyStatus(null).setApplyMessage(null); } + @Override + public SendResult singleSend(String mobile, String template) { + Map params = new LinkedHashMap<>(); + params.put("apikey", apiKey); + params.put("mobile", mobile); + params.put("text", template); + // TODO: 2019/5/19 sin 运营商发送报告 回调 + // params.put("callback_url", template); + String result = post(URL_TEMPLATE_DELETE, params); + JSONObject jsonObject = JSON.parseObject(result); + if (jsonObject.containsKey("code") + && !(jsonObject.getInteger("code") == SUCCESS_CODE)) { + throw new ServiceException(AdminErrorCodeEnum.SMS_PLATFORM_FAIL.getCode(), + jsonObject.getString("detail")); + } + + return new SendResult() + .setHasSuccess(SUCCESS_CODE == jsonObject.getInteger("code")) + .setCode(jsonObject.getInteger("code")) + .setMessage(jsonObject.getString("detail")) + .setSuccess(Lists.newArrayList(mobile)) + .setFail(Collections.EMPTY_LIST); + } + + @Override + public SendResult batchSend(List mobileList, String template) { + + // 最大发送数为 1000,我们设置为 500 个, 分段发送 + int maxSendSize = 500; + int maxSendSizeCount = mobileList.size() % maxSendSize; + int j = 0; + int j2 = maxSendSize; + + List successList = new ArrayList<>(); + List failList = new ArrayList<>(); + for (int i = 0; i < maxSendSizeCount; i++) { + StringBuffer sendMobileStr = new StringBuffer(); + for (int k = j; k < j2; k++) { + sendMobileStr.append(","); + sendMobileStr.append(mobileList.get(k)); + } + + String dividedMobile = sendMobileStr.toString().substring(1); + + // 发送手机号 + Map params = new LinkedHashMap<>(); + params.put("apikey", apiKey); + params.put("mobile", dividedMobile); + params.put("text", template); + // TODO: 2019/5/19 sin 运营商发送报告 回调 + // params.put("callback_url", template); + String result = post(URL_SEND_BATCH, params); + JSONObject jsonObject = JSON.parseObject(result); + if (jsonObject.containsKey("code") + && !(jsonObject.getInteger("code") == SUCCESS_CODE)) { + throw new ServiceException(AdminErrorCodeEnum.SMS_PLATFORM_FAIL.getCode(), + jsonObject.getString("detail")); + } + + JSONArray jsonArray = jsonObject.getJSONArray("data"); + for (Object o : jsonArray) { + JSONObject dataJSONObject = (JSONObject) o; + if (SUCCESS_CODE == dataJSONObject.getInteger("code")) { + successList.add(dataJSONObject.getString("mobile")); + } else { + failList.add(dataJSONObject.getString("mobile")); + } + } + + // 用于递增 maxSendSize + j = j2; + j2 = j + maxSendSize; + } + + return new SendResult() + .setHasSuccess(true) + .setCode(SUCCESS_CODE) + .setMessage(null) + .setSuccess(successList) + .setFail(failList); + } + /** * 短信 status 和 云片状态 映射关系 * diff --git a/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java index 64e77e8ef..40edf35ba 100644 --- a/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java +++ b/system/system-service-impl/src/test/java/cn/iocoder/mall/admin/service/SmsServiceImplTest.java @@ -66,4 +66,10 @@ public class SmsServiceImplTest { public void deleteTemplateTest() { smsService.deleteTemplate(3); } + + @Test + public void singleSendTest() { + String mobile = "13302926050"; + smsService.singleSend(mobile, 1); + } }