# README
这个项目集成了一系列的推送服务。个推,小米,华为。

### 使用准备
* 向项目负责人申请app_name,app_id,app_secret

### 关键api

* /api/v1/token
    * 请求参数

        |参数名称|类型|是否必填|说明|
        |-----|-----|------|----|
        |app_name|String|是|应用名称|
        |app_id|String| 是| 应用app_id|
        |app_secret|String |是|应用app_secret|
        
        这些配置都有在app.yml 文件中配置
    * 返回结果

        |code|message|token|ttl|
        |----|----|---|---|
        |0|success|token值|token 过期时间|
        |-1|应用不存在或者app_id/app_secret不匹配|||

* /api/v1/push

    * 请求参数

        |参数名称|类型|是否必填|说明|
        |-----|-----|------|----|
        |app_name|String|是|应用名称|
        |user_device_ids|Array/String| 是| 设备id|
        |message|String|是|需要返送的信息的json字符串|
        |igetui_opts|String|是|个推的一些额外参数|

    * 返回结果

        |code|message|说明|
        |----|----|---|
        |0|success|异步任务正在处理|
        |-1|token 错误/过期|
        
### 使用案例
* 配置文件
```
  app_push:
    host: 'http://localhost:3001'
    app_name: 'crm'
    app_id: '****'
    app_secret: '****'
```
AppSettings(可以使用gem settingslogic)读取配置文件信息

* 关键代码
```
module PushService
  module Push
    TOKEN_URL = '/api/v1/token'.freeze
    PUSH_URL = '/api/v1/push'.freeze
    TOKEN_KEY = 'push_service:push:token'.freeze
    EXPIRE_TIME = 3500
    class << self
      # 调用推送服务,推送信息
      # user_device_ids, 用户设备信息,可以是字符串数组,也可以是字符串。
      # message 要推送的具体信息
      # igetui_opts 是hash类型,主要是一些其他的信息。
      #   由于历史遗留问题,封装的不够好,这里主要用来处理个推的一些参数。
      #   目前华为推送,小米推送不需要到这个。
      def push(user_device_ids, message, igetui_opts = {})
        url = AppSettings.app_push['host'] + PUSH_URL
        headers = {
          'Accept' => 'application/json;',
          'Authorization' => "Token token=#{token}"
        }
        body = {
          app_name: AppSettings.app_push['app_name'],
          user_device_ids: user_device_ids,
          message: message.to_json,
          igetui_opts: igetui_opts.to_json
        }
        response = HTTParty.post(url, body: body, headers: headers, timeout: 5)
        PushService::Log.info("url: #{url} headers: #{headers} body: #{body} response: #{response}")
      end

      # 获取token
      def token
        return $redis.get(TOKEN_KEY) if $redis.get(TOKEN_KEY).present?

        res = req_token
        store_token(res)
      end

      def token_ttl
        $redis.ttl(TOKEN_KEY)
      end

      def req_token
        body = {
          app_name: AppSettings.app_push['app_name'],
          app_id: AppSettings.app_push['app_id'],
          app_secret: AppSettings.app_push['app_secret']
        }
        url = AppSettings.app_push['host'] + TOKEN_URL
        response = HTTParty.post(url, body: body, headers: { Accept: 'application/json' }, timeout: 5)
        PushService::Log.info("url: #{url} body: #{body} response: #{response}")
        response
      end

      def store_token(response)
        if response['code'].zero?
          token = response['token']
          $redis.set(TOKEN_KEY, token, ex: EXPIRE_TIME)
          token
        else
          ''
        end
      end
    end
  end
end
```

* 异步执行代码
```
  class VariousPushWorker
    include Sidekiq::Worker
    sidekiq_options queue: :low, retry: true, backtrace: true
    def perform(client_ids, message, opts)
      PushService::Push.push(client_ids, message, opts)
    rescue StandError => e
      logger.error "exec_sync exception: #{e.message}:#{e.backtrace}"
    end
    private
    def logger
      @logger ||= ActiveSupport::Logger.new("#{Rails.root}/log/various_push_sidekiq.log", 'weekly')
    end
  end
```

* 参数例子
```
user_device_ids = ["f30b426be09f5111fde8e717919c35d9"]
message = {"category"=>0, "push_type"=>2, "extras"=>{"due_at"=>"2019-05-22", "title"=>"工作报告通知", "text"=>"温馨提示,现在还没有人提交工作报告,第一个会不会是你呢?去写日报。"}, "id"=>1261514, "title"=>"工作报告通知", "text"=>"温馨提示,现在还没有人提交工作报告,第一个会不会是你呢?去写日报。", "notifiable_id"=>5501712, "notifiable_type"=>"User", "subject_id"=>nil, "subject_type"=>"ScheduleReport::Daily", "notify_type"=>"notify_should_create", "created_at"=>1558518806, "user_id"=>5501712, "is_ring"=>true, "is_vibrate"=>true}
igetui_opts = {"transmission_type"=>0}
VariousPushWorker.perform_async(user_device_ids, message,igetui_opts )
```