diff --git a/README.md b/README.md index 707cbc6..e2121ba 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,11 @@ austin项目**核心流程**:`austin-api`接收到发送消息请求,直接 目前引用的中间件教程的安装姿势均基于`Centos 7.6`(**完全部署所有的服务,大概8G内存**),austin项目**强依赖**`MySQL`/`Redis`/`Kafka`/`apollo`,**弱依赖**`prometheus`/`graylog`/`flink`/`xxl-job`。如果缺少相关的组件可戳:[安装相关组件教程](INSTALL.md)。 -实在想要拉下clone项目后不部署环境直接启动,我这提供了[会员服务](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247505577&idx=1&sn=5114f8f583755899c2946fbea0b22e4b&chksm=ebd497a8dca31ebe8f98344483a00c860863dfc3586e51eed95b25988151427fee8101311f4f&token=735778370&lang=zh_CN#rd) + + +> 实在想要`clone`项目后不用自己部署环境直接在本地启动`debug`,我这提供了[会员服务](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247505577&idx=1&sn=5114f8f583755899c2946fbea0b22e4b&chksm=ebd497a8dca31ebe8f98344483a00c860863dfc3586e51eed95b25988151427fee8101311f4f&token=735778370&lang=zh_CN#rd),**直连**部署好的服务器 + + **1**、austin使用的MySQL版本**5.7x**。如果目前使用的MySQL版本8.0,注意改变`pom.xml`所依赖的版本 @@ -100,6 +104,22 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co **14**、正常使用**系统监控**需要部署`promethus`和`grafana`,根据[部署文档](INSTALL.md)配置`grafana`图表 +## 会员服务 + +收费课程是以**项目**为主,代码在Gitee和GitHub上都是开源的,项目没有商业版,后面也不会有。那么,付费跟我自己去拉Git仓库拉代码下来看有什么区别? + +1、有很多人的自学能力和基础确实不太行,不知道怎么开始学习,从哪开始看起,学习项目的过程中会走很多弯路,很容易就迷茫了。付费最跟自学最主要的区别就是**我的服务会更周到**。 + +我会告诉你怎么开始学这个开源项目,哪些是重点需要掌握的,如何利用最短的时间把握整个系统架构和编码的设计,把时间节省下来去做其他事情。 + +2、一个生产环境的系统肯定会依赖各种中间件,《消息推送平台-Austin》也是一样的。我专门买了两台服务器已经搭建好必要的依赖,付费的可以**使用我的远程服务器**,在**本地就可以直接启动运行体验和学习** + +3、项目在编写的过程中也经历多次的重构迭代,迭代的内容我是不会将以往文章内容重新修正发布,但语雀的文档内容一定是**及时同步**,文档跟代码是保持一致的 + +4、除了项目,还可以问我些学习经验、学习路线、简历编写、面试经验等等问题,技术和学习上的知识**知无不言** + +详情可以看戳:[我开通了付费渠道](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247505577&idx=1&sn=5114f8f583755899c2946fbea0b22e4b&chksm=ebd497a8dca31ebe8f98344483a00c860863dfc3586e51eed95b25988151427fee8101311f4f&token=319992632&lang=zh_CN#rd) + ## 里程碑 - [x] Maven+SpringBoot项目搭建 @@ -142,22 +162,12 @@ curl -XPOST "127.0.0.1:8080/send" -H 'Content-Type: application/json' -d '{"co - -**Java3y**公众号在持续更新austin系列文章,**保姆级**讲解搭建项目的过程(包括技术选型以及一些业务的探讨)以及相关环境的搭建。**扫下面的码直接关注,带你了解整个项目** - - -如果你需要用这个项目写在简历上,**强烈建议关注公众号看实现细节的思路**。如果⽂档中有任何的不懂的问题,都可以直接来找我询问,我乐意帮助你们!公众号下有我的联系方式 - -[会员服务](https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247505577&idx=1&sn=5114f8f583755899c2946fbea0b22e4b&chksm=ebd497a8dca31ebe8f98344483a00c860863dfc3586e51eed95b25988151427fee8101311f4f&token=735778370&lang=zh_CN#rd) - - - ## 如何准备面试? **对线面试官**公众号持续更新**面试系列**文章(对线面试官系列),深受各大开发的好评,已有不少的同学通过对线面试官系列得到BATTMD等一线大厂的的offer。一个**讲人话的面试系列**,八股文不再是背诵。 -![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f48cee2bbd44476f93dfcdd8aaf8a4eb~tplv-k3u1fbpfcp-watermark.image?) +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4a6cae132244355b9da6bd74d38d1ee~tplv-k3u1fbpfcp-zoom-1.image) 想要获取这份电子书,**点击关注**下方公众号,回复「**对线**」得到我的联系方式即可进群获取电子书 diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java b/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java index 40985c7..26f61f5 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/RespStatusEnum.java @@ -26,6 +26,7 @@ public enum RespStatusEnum { */ CLIENT_BAD_PARAMETERS("A0001", "客户端参数错误"), TEMPLATE_NOT_FOUND("A0002", "找不到模板或模板已被删除"), + TOO_MANY_RECEIVER("A0003", "传入的接收者大于100个"), /** * 系统 diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/SmsStatus.java b/austin-common/src/main/java/com/java3y/austin/common/enums/SmsStatus.java index 2ee30ba..285280c 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/SmsStatus.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/SmsStatus.java @@ -21,4 +21,19 @@ public enum SmsStatus { private String description; + /** + * 根据状态获取描述信息 + * @param code + * @return + */ + public static String getDescriptionByStatus(Integer code) { + for (SmsStatus value : SmsStatus.values()) { + if (value.getCode().equals(code)) { + return value.getDescription(); + } + } + return ""; + } + + } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/receipt/TencentSmsReceipt.java b/austin-handler/src/main/java/com/java3y/austin/handler/receipt/TencentSmsReceipt.java new file mode 100644 index 0000000..e59f67a --- /dev/null +++ b/austin-handler/src/main/java/com/java3y/austin/handler/receipt/TencentSmsReceipt.java @@ -0,0 +1,115 @@ +package com.java3y.austin.handler.receipt; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.google.common.base.Throwables; +import com.java3y.austin.common.constant.SendAccountConstant; +import com.java3y.austin.common.dto.account.TencentSmsAccount; +import com.java3y.austin.common.enums.SmsStatus; +import com.java3y.austin.support.config.SupportThreadPoolConfig; +import com.java3y.austin.support.dao.SmsRecordDao; +import com.java3y.austin.support.domain.SmsRecord; +import com.java3y.austin.support.utils.AccountUtils; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20210111.SmsClient; +import com.tencentcloudapi.sms.v20210111.models.*; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + + +/** + * 拉取短信回执信息 + * + * @author 3y + */ +@Component +@Slf4j +public class TencentSmsReceipt { + + + @Autowired + private AccountUtils accountUtils; + + @Autowired + private SmsRecordDao smsRecordDao; + + @PostConstruct + private void init() { + + // 获取腾讯云账号信息 + TencentSmsAccount account = accountUtils.getAccount(10, SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.class); + + SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> { + + while (true) { + try { + SmsClient client = getSmsClient(account); + + // 每次拉取10条 + PullSmsSendStatusRequest req = new PullSmsSendStatusRequest(); + req.setLimit(10L); + req.setSmsSdkAppId(account.getSmsSdkAppId()); + + PullSmsSendStatusResponse resp = client.PullSmsSendStatus(req); + List smsRecordList = new ArrayList<>(); + if (resp != null && resp.getPullSmsSendStatusSet() != null && resp.getPullSmsSendStatusSet().length > 0) { + log.debug("receipt sms:{}", JSON.toJSONString(resp.getPullSmsSendStatusSet())); + for (PullSmsSendStatus pullSmsSendStatus : resp.getPullSmsSendStatusSet()) { + SmsRecord smsRecord = SmsRecord.builder() + .sendDate(Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN))) + .messageTemplateId(0L) + .phone(Long.valueOf(pullSmsSendStatus.getSubscriberNumber())) + .supplierId(account.getSupplierId()) + .supplierName(account.getSupplierName()) + .msgContent("") + .seriesId(pullSmsSendStatus.getSerialNo()) + .chargingNum(0) + .status("SUCCESS".equals(pullSmsSendStatus.getReportStatus()) ? SmsStatus.RECEIVE_SUCCESS.getCode() : SmsStatus.RECEIVE_FAIL.getCode()) + .reportContent(pullSmsSendStatus.getDescription()) + .updated(Math.toIntExact(pullSmsSendStatus.getUserReceiveTime())) + .created(Math.toIntExact(DateUtil.currentSeconds())) + .build(); + smsRecordList.add(smsRecord); + } + } + if (!CollUtil.isEmpty(smsRecordList)) { + smsRecordDao.saveAll(smsRecordList); + } + Thread.sleep(200); + } catch (Exception e) { + log.error("TencentSmsReceipt#init fail!{}", Throwables.getStackTraceAsString(e)); + } + } + + }); + + } + + /** + * 构造smsClient + * @param account + * @return + */ + private SmsClient getSmsClient(TencentSmsAccount account) { + Credential cred = new Credential(account.getSecretId(), account.getSecretKey()); + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint(account.getUrl()); + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + return new SmsClient(cred, account.getRegion(), clientProfile); + } + + +} diff --git a/austin-support/src/main/java/com/java3y/austin/support/dao/SmsRecordDao.java b/austin-support/src/main/java/com/java3y/austin/support/dao/SmsRecordDao.java index c312996..4211d88 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/dao/SmsRecordDao.java +++ b/austin-support/src/main/java/com/java3y/austin/support/dao/SmsRecordDao.java @@ -4,12 +4,21 @@ package com.java3y.austin.support.dao; import com.java3y.austin.support.domain.SmsRecord; import org.springframework.data.repository.CrudRepository; +import java.util.List; + /** * 短信记录的Dao - * @author 3y * + * @author 3y */ public interface SmsRecordDao extends CrudRepository { - + /** + * 通过日期和手机号找到发送记录 + * + * @param phone + * @param sendDate + * @return + */ + List findByPhoneAndSendDate(Long phone, Integer sendDate); } diff --git a/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java b/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java index 12503b7..6477ceb 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java +++ b/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java @@ -1,12 +1,12 @@ package com.java3y.austin.web.controller; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; import com.java3y.austin.common.enums.RespStatusEnum; import com.java3y.austin.common.vo.BasicResultVO; import com.java3y.austin.web.service.DataService; import com.java3y.austin.web.vo.DataParam; import com.java3y.austin.web.vo.amis.EchartsVo; +import com.java3y.austin.web.vo.amis.SmsTimeLineVo; import com.java3y.austin.web.vo.amis.UserTimeLineVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -46,10 +46,16 @@ public class DataController { return new BasicResultVO<>(RespStatusEnum.SUCCESS, echartsVo); } - public static void main(String[] args) { - EchartsVo.TitleVO titleVO = EchartsVo.TitleVO.builder().text("销售情况").build(); - EchartsVo echartsVo = EchartsVo.builder().title(titleVO).build(); + @PostMapping("/sms") + @ApiOperation("/获取短信下发数据") + public BasicResultVO getSmsData(@RequestBody DataParam dataParam) { + if (dataParam == null || dataParam.getDateTime() == null || dataParam.getReceiver() == null) { + return new BasicResultVO<>(RespStatusEnum.SUCCESS, new SmsTimeLineVo()); + } + + SmsTimeLineVo smsTimeLineVo = dataService.getTraceSmsInfo(dataParam); - System.out.println(JSON.toJSONString(echartsVo)); + return new BasicResultVO<>(RespStatusEnum.SUCCESS, smsTimeLineVo); } + } diff --git a/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java b/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java index 86e1e8e..42b09b2 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java +++ b/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java @@ -1,6 +1,8 @@ package com.java3y.austin.web.service; +import com.java3y.austin.web.vo.DataParam; import com.java3y.austin.web.vo.amis.EchartsVo; +import com.java3y.austin.web.vo.amis.SmsTimeLineVo; import com.java3y.austin.web.vo.amis.UserTimeLineVo; /** @@ -28,4 +30,12 @@ public interface DataService { EchartsVo getTraceMessageTemplateInfo(String businessId); + /** + * 获取短信下发记录 + * + * @param dataParam + * @return + */ + SmsTimeLineVo getTraceSmsInfo(DataParam dataParam); + } diff --git a/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java b/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java index 010ddf5..15f3727 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java +++ b/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java @@ -11,13 +11,18 @@ import com.java3y.austin.common.constant.AustinConstant; import com.java3y.austin.common.domain.SimpleAnchorInfo; import com.java3y.austin.common.enums.AnchorState; import com.java3y.austin.common.enums.ChannelType; +import com.java3y.austin.common.enums.SmsStatus; import com.java3y.austin.support.dao.MessageTemplateDao; +import com.java3y.austin.support.dao.SmsRecordDao; import com.java3y.austin.support.domain.MessageTemplate; +import com.java3y.austin.support.domain.SmsRecord; import com.java3y.austin.support.utils.RedisUtils; import com.java3y.austin.support.utils.TaskInfoUtils; import com.java3y.austin.web.constants.AmisVoConstant; import com.java3y.austin.web.service.DataService; +import com.java3y.austin.web.vo.DataParam; import com.java3y.austin.web.vo.amis.EchartsVo; +import com.java3y.austin.web.vo.amis.SmsTimeLineVo; import com.java3y.austin.web.vo.amis.UserTimeLineVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -39,6 +44,10 @@ public class DataServiceImpl implements DataService { @Autowired private MessageTemplateDao messageTemplateDao; + @Autowired + private SmsRecordDao smsRecordDao; + + @Override public UserTimeLineVo getTraceUserInfo(String receiver) { List userInfoList = redisUtils.lRange(receiver, 0, -1); @@ -134,6 +143,44 @@ public class DataServiceImpl implements DataService { } + @Override + public SmsTimeLineVo getTraceSmsInfo(DataParam dataParam) { + + ArrayList itemsVOS = new ArrayList<>(); + SmsTimeLineVo smsTimeLineVo = SmsTimeLineVo.builder().items(itemsVOS).build(); + + Integer sendDate = Integer.valueOf(DateUtil.format(new Date(dataParam.getDateTime() * 1000L) + , DatePattern.PURE_DATE_PATTERN)); + List smsRecordList = smsRecordDao.findByPhoneAndSendDate(Long.valueOf(dataParam.getReceiver()), sendDate); + + if (CollUtil.isEmpty(smsRecordList)) { + return smsTimeLineVo; + } + + Map> maps = smsRecordList.stream().collect(Collectors.groupingBy((o) -> o.getPhone() + o.getSeriesId())); + + for (Map.Entry> entry : maps.entrySet()) { + SmsTimeLineVo.ItemsVO itemsVO = SmsTimeLineVo.ItemsVO.builder().build(); + for (SmsRecord smsRecord : entry.getValue()) { + // 发送记录 messageTemplateId >0 ,回执记录 messageTemplateId =0 + if (smsRecord.getMessageTemplateId() > 0) { + itemsVO.setBusinessId(String.valueOf(smsRecord.getMessageTemplateId())); + itemsVO.setContent(smsRecord.getMsgContent()); + itemsVO.setSendType(SmsStatus.getDescriptionByStatus(smsRecord.getStatus())); + itemsVO.setSendTime(DateUtil.format(new Date(Long.valueOf(smsRecord.getCreated()*1000L)), DatePattern.NORM_DATETIME_PATTERN)); + + } else { + itemsVO.setReceiveType(SmsStatus.getDescriptionByStatus(smsRecord.getStatus())); + itemsVO.setReceiveContent(smsRecord.getReportContent()); + itemsVO.setReceiveTime(DateUtil.format(new Date(Long.valueOf(smsRecord.getUpdated()*1000L)), DatePattern.NORM_DATETIME_PATTERN)); + } + } + itemsVOS.add(itemsVO); + } + + return smsTimeLineVo; + } + /** * 如果传入的是模板ID,则生成【当天】的businessId进行查询 * 如果传入的是businessId,则按默认的businessId进行查询 @@ -150,4 +197,14 @@ public class DataServiceImpl implements DataService { } return businessId; } + + public static void main(String[] args) { + + Long time = 1653140847 * 1000L; + + String format = DateUtil.format(new Date(time), DatePattern.NORM_DATETIME_PATTERN); + + System.out.println(format + ); + } } diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java b/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java index 1f3da93..416bb29 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java @@ -16,8 +16,9 @@ import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor public class DataParam { + /** - * 传入userId查看用户的链路信息 + * 查看用户的链路信息 */ private String receiver; @@ -30,4 +31,12 @@ public class DataParam { private String businessId; + /** + * 日期时间(检索短信的条件使用) + */ + private Long dateTime; + + + + } diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/SmsTimeLineVo.java b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/SmsTimeLineVo.java new file mode 100644 index 0000000..b008ed7 --- /dev/null +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/SmsTimeLineVo.java @@ -0,0 +1,67 @@ +package com.java3y.austin.web.vo.amis; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author 3y + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SmsTimeLineVo { + + /** + * items + */ + private List items; + + /** + * ItemsVO + */ + @Data + @Builder + public static class ItemsVO { + /** + * 业务ID + */ + private String businessId; + /** + * detail 发送内容 + */ + private String content; + + /** + * 发送状态 + */ + private String sendType; + + /** + * 回执状态 + */ + private String receiveType; + + /** + * 回执报告 + */ + private String receiveContent; + + /** + * 发送时间 + */ + private String sendTime; + + /** + * 回执时间 + */ + private String receiveTime; + + + } +} diff --git a/pom.xml b/pom.xml index 68c2ffa..7b500bd 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ com.tencentcloudapi tencentcloud-sdk-java - 3.1.390 + 3.1.510