【微服务】Staffjoy 项目源码解析(二)—— SMS 模块

一. 架构设计

架构图
微服务模块所有的架构设计都是分成 API 与 SVC 两个包来设计。
API 接口模块内会包含对外的接口及接口中会使用的数据模型。
而 SVC 模块中则包含项目的主要代码。
本 SMS 模块负责短信发送的处理。所以,只有一个主要的发送方法。结构较为基本而清晰。

二. 代码分析

2.1 sms-api
SmsClient 内只含有一个 send 方法,参数为服务类型数据和 SmsRequest 对象。
返回一个 BaseResponse 对象(内涵一个 message(String),一个 code(ResultCode:判断返回状态)及一个 isSuccess 方法)。绑定在  /queue_send 路径上。
负责发送短信。
@PostMapping(path = "/queue_send")
    BaseResponse send(@RequestHeader(AuthConstant.AUTHORIZATION_HEADER) String authz, @RequestBody @Valid SmsRequest smsRequest);
此方法将在 bot 模块的 helperService 中被调用。

而 SmsRequest 中主要包含三个参数,发给谁,发送状态,发什么
	@NotBlank(message = "Please provide a phone number")
    private String to;
    @NotBlank(message = "Please provide a template code")
    private String templateCode;
    private String templateParam;
2.2 sms-svc
从底层向上分析。

首先是 AppProps 这个 SVC 中的底层数据封装对象。里面是一些要用到的配置数据,包括 阿里云的密码等。
	@NotNull private String aliyunAccessKey;
    @NotNull private String aliyunAccessSecret;
    @NotNull private String aliyunSmsSignName;

    private boolean whiteListOnly;
    private String whiteListPhoneNumbers;
    private int concurrency;
然后是 AppConfig 类。
其中导入并生成了操作阿里云模块的 IAcsClient 对象来与阿里云进行交互。
并生成了一个异步线程池,准备操作的处理。
    @Bean
    public IAcsClient acsClient(@Autowired SentryClient sentryClient) {
        IClientProfile profile = DefaultProfile.getProfile(SmsConstant.ALIYUN_REGION_ID, appProps.getAliyunAccessKey(), appProps.getAliyunAccessSecret());
        try {
            DefaultProfile.addEndpoint(SmsConstant.ALIYUN_SMS_ENDPOINT_NAME, SmsConstant.ALIYUN_REGION_ID, SmsConstant.ALIYUN_SMS_PRODUCT, SmsConstant.ALIYUN_SMS_DOMAIN);
        } catch (ClientException ex) {
            sentryClient.sendException(ex);
            logger.error("Fail to create acsClient ", ex);
        }
        IAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    @Bean(name=ASYNC_EXECUTOR_NAME)
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(appProps.getConcurrency());
        executor.setMaxPoolSize(appProps.getConcurrency());
        executor.setQueueCapacity(SmsConstant.DEFAULT_EXECUTOR_QUEUE_CAPACITY);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadNamePrefix("AsyncThread-");
        executor.initialize();
        return executor;
    }
接着是 SmsSendService 类,属于 Service 层。
因为没有数据库调用,所以没有 Dao 层,直接到 Service 层中。

关键步骤是生成 request 请求对象,发送并得到 Response,下面展示关键语句。
SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(smsRequest.getTo());
        request.setSignName(appProps.getAliyunSmsSignName());
        request.setTemplateCode(smsRequest.getTemplateCode());
        request.setTemplateParam(smsRequest.getTemplateParam());

        try {
            SendSmsResponse response = acsClient.getAcsResponse(request);
           .....
            
但是代码中还是有很长一段的,是返回参数的校验与错误处理及日志记录,这都应该是一个正规项目应该有的部分。
大致相同,贴一部分。
            Context sentryContext = sentryClient.getContext();
            sentryContext.addTag("to", smsRequest.getTo());
            sentryContext.addTag("template_code", smsRequest.getTemplateCode());
            sentryClient.sendException(ex);
            logger.error("failed to make aliyun sms request ", ex);

最后是 Controller 层的 SmsController 类。
就一个 send 的方法,要注意的点也是参数校验。
一个是头部的参数校验,发送过来的服务是否符合操作的权限(company,account,bot 等)
再一个是判断传过来的电话号是否都能够发送(数据非空且被包含在白名单中),最后发送目标。
@PostMapping(path = "/queue_send")
    @Authorize({
            AuthConstant.AUTHORIZATION_COMPANY_SERVICE,
            AuthConstant.AUTHORIZATION_ACCOUNT_SERVICE,
            AuthConstant.AUTHORIZATION_BOT_SERVICE
    })
    public BaseResponse send(@RequestBody @Valid SmsRequest smsRequest) {

        if (appProps.isWhiteListOnly()) {
            String whiteList = appProps.getWhiteListPhoneNumbers();
            boolean allowedToSend = !StringUtils.isEmpty(whiteList)
                    && whiteList.contains(smsRequest.getTo());
            if (!allowedToSend) {
                String msg = String.format("prevented sending to number %s due to whitelist", smsRequest.getTo());
                logger.warn(msg);
                return BaseResponse.builder().code(ResultCode.REQ_REJECT).message(msg).build();
            }
        }

        smsSendService.sendSmsAsync(smsRequest);
        String msg = String.format("sent message to %s. async", smsRequest.getTo());
        logger.debug(msg);
        return BaseResponse.builder().message(msg).build();
    }

三. 小关键点

展示一下 lombok 的基本注解
// 自动生成构建类对象的方法
@Builder
// 自动生成无参构造器
@NoArgsConstructor
// 自动生成全参构造器
@AllArgsConstructor
// 必须非空
@NotNull

Spring 自带的数据校验注解

// 校验启动
@Validated
// 在方法参数中加
@Valid
@RequestBody
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值