Commit 98556222 by liangyuzhe

项目重构

parent d2b2b524
...@@ -37,3 +37,5 @@ ...@@ -37,3 +37,5 @@
# Ignore master key for decrypting credentials and more. # Ignore master key for decrypting credentials and more.
/config/master.key /config/master.key
config/database.yml
ruby-2.6.0 ruby-2.6.5
\ No newline at end of file \ No newline at end of file
...@@ -2,12 +2,13 @@ source 'https://gems.ruby-china.com' ...@@ -2,12 +2,13 @@ source 'https://gems.ruby-china.com'
git_source(:github) { |repo| "https://github.com/#{repo}.git" } git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.6.0' ruby '2.6.5'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.2', '>= 5.2.2.1' gem 'rails', '~> 6.0.0'
# Use sqlite3 as the database for Active Record # Use sqlite3 as the database for Active Record
gem 'sqlite3' # gem 'sqlite3'
gem 'mysql2', '~> 0.5.3'
# Use Puma as the app server # Use Puma as the app server
gem 'puma', '~> 3.11' gem 'puma', '~> 3.11'
# Use SCSS for stylesheets # Use SCSS for stylesheets
......
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Metal
ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left|
include left
end
include RailsParam::Param
rescue_from StandardError, with: :rescue_all rescue_from StandardError, with: :rescue_all
include TokenValidate
def rescue_all(e) def rescue_all(e)
ErrorLog.error(e) ErrorLog.error(e)
......
module Token module TokenValidate
extend ActiveSupport::Concern
ACCESS_TOEKN_EXPIRE_TIME = 7200 ACCESS_TOEKN_EXPIRE_TIME = 7200
class << self
def key(app_name) def validate_token(app_name, token)
"push:api:#{app_name}:access_token" !! (token == get_token(app_name))
end end
def token_ttl(app_name)
$redis.ttl(tkey(app_name))
end
def generate_token(app_name) def generate_token(app_name)
token = SecureRandom.hex(32) token = SecureRandom.hex(32)
$redis.set(key(app_name), token, ex: ACCESS_TOEKN_EXPIRE_TIME) $redis.set(tkey(app_name), token, ex: ACCESS_TOEKN_EXPIRE_TIME)
token token
end end
def token(app_name) private
$redis.get(key(app_name))
end
def ttl(app_name) def tkey(app_name)
$redis.ttl(key(app_name)) "push:api:#{app_name}:access_token"
end
end end
def get_token(app_name)
$redis.get(tkey(app_name))
end
end end
\ No newline at end of file
class PushsController < ApplicationController class PushsController < ApplicationController
include ActionController::HttpAuthentication::Token include ActionController::HttpAuthentication::Token
skip_before_action :verify_authenticity_token # skip_before_action :verify_authenticity_token
def token def token
param! :app_name, String, required: true param! :app_name, String, required: true
...@@ -9,44 +9,117 @@ class PushsController < ApplicationController ...@@ -9,44 +9,117 @@ class PushsController < ApplicationController
app_setting = Settings.send(params[:app_name].to_sym) rescue {} app_setting = Settings.send(params[:app_name].to_sym) rescue {}
if app_setting['app_id'] == params[:app_id] && app_setting['app_secret'] == params[:app_secret] if app_setting['app_id'] == params[:app_id] && app_setting['app_secret'] == params[:app_secret]
render json: { code: 0, message: 'success', token: Token.generate_token(params[:app_name]), ttl: Token.ttl(params[:app_name])} render json: { code: 0, message: 'success', token: generate_token(params[:app_name]), ttl: token_ttl(params[:app_name])}
else else
render json: { code: -1, message: '应用不存在或者app_id/app_secret不匹配'} render json: { code: -1, message: '应用不存在或者app_id/app_secret不匹配'}
end end
end end
def single
param! :app_name, String, required: true, in: %w(crm)
# param! :device_ids_opts, String, required: true
param! :message, String, required: true
param! :igetui_opts, String
param! :app_type, String
param! :sync_push, :boolean
param! :user_id, String, required: true
param! :db_role, String, in: %w(dev test staging production)
# app_name 应用名称
# user_device_ids 设备ids igetui_opts = JSON.parse(params[:igetui_opts]) rescue {}
# message 推送信息的json 字符串 message = JSON.parse(params[:message]) rescue {}
# igetui_opts 个推推送的额外选项
def push db_role = fetch_db_role
param! :app_name, String, required: true user_device_exist = false
param! :device_ids_opts, String, required: true ActiveRecord::Base.connected_to(role: db_role) do
user_device_exist = UserDevice.where(user_id: params[:user_id]).exists?
end
unless user_device_exist
return render json: { code: -1, message: 'user_device找不到,user_id错误'}
end
token_array = token_and_options(request)
if token_array.blank?
return render json: { code: -1, message: 'token错误'}
end
if validate_token(params[:app_name], token_array.first)
push_sync(params[:sync_push], {db_role: db_role, user_ids: Array(params[:user_id]), message: message, app_type: params[:app_type], igetui_opts: igetui_opts.merge(pusher_type: 'push_message_to_single')})
render json: { code: 0, message: 'success', describe: '异步任务正在处理'}
else
render json: { code: -1, message: 'token 错误/过期'}
end
end
def batch
param! :app_name, String, required: true, in: %w(crm)
# param! :device_ids_opts, String, required: true
param! :message, String, required: true param! :message, String, required: true
param! :igetui_opts, String param! :igetui_opts, String
param! :app_type, String param! :app_type, String
param! :sync_push, String param! :sync_push, String
param! :user_ids, Array, required: true
param! :db_role, String, in: %w(dev test staging production)
igetui_opts = JSON.parse(params[:igetui_opts]) rescue {} igetui_opts = JSON.parse(params[:igetui_opts]) rescue {}
message = JSON.parse(params[:message]) rescue {} message = JSON.parse(params[:message]) rescue {}
device_ids_opts = JSON.parse(params[:device_ids_opts]) rescue {} user_ids = params[:user_ids] rescue []
token = token_and_options(request).first
if token == Token.token(params[:app_name]) db_role = fetch_db_role
if params.has_key?(:sync_push) user_device_exist = false
timeout_seconds = $redis.get('push_timeout').to_i ActiveRecord::Base.connected_to(role: db_role) do
if timeout_seconds > 0 user_device_exist = UserDevice.where(user_id: user_ids).exists?
Timeout.timeout(timeout_seconds) { end
::PushWorker.new.perform(device_ids_opts, message, igetui_opts, params[:app_type]) unless user_device_exist
} return render json: { code: -1, message: 'user_device找不到,user_ids错误'}
else end
::PushWorker.new.perform(device_ids_opts, message, igetui_opts, params[:app_type])
end token_array = token_and_options(request)
else if token_array.blank?
::PushWorker.perform_async(device_ids_opts, message, igetui_opts, params[:app_type]) return render json: { code: -1, message: 'token错误'}
end end
if validate_token(params[:app_name], token_array.first)
push_sync(params[:sync_push], {db_role: db_role, user_ids: user_ids, message: message, app_type: params[:app_type], igetui_opts: igetui_opts.merge(pusher_type: 'push_message_to_list')})
render json: { code: 0, message: 'success', describe: '异步任务正在处理'} render json: { code: 0, message: 'success', describe: '异步任务正在处理'}
else else
render json: { code: -1, message: 'token 错误/过期'} render json: { code: -1, message: 'token 错误/过期'}
end end
end
private
def push_sync(sync_push, opts={})
if sync_push
timeout_seconds = $redis.get('push_timeout').to_i
if timeout_seconds > 0
Timeout.timeout(timeout_seconds) {
::PushWorker.new.perform(opts)
}
else
::PushWorker.new.perform(opts)
end
else
::PushWorker.perform_async(opts)
end
end
def fetch_db_role
return params[:db_role].to_sym if params[:db_role].present?
case request.host
when 'lxcrm-dev.weiwenjia.com', 'ik-dev.ikcrm.com'
:dev
when 'lxcrm-test.weiwenjia.com', 'ik-test.weiwenjia.com'
:test
else
:production
end
end end
end end
class UserDevice < ApplicationRecord
self.table_name = 'user_devices'
connects_to database: { dev: :dev_development, test: :test_development, staging: :staging_development, production: :production }
# 当前app端传递的platform的值:
# android: igetui, xiaomi, huawei
# iso: igetui 目前还不清楚为啥不用apns,暂时不修改
enum platform: %i[igetui apns xiaomi huawei]
enum device_platform: %i[ios android]
# 用于区分当前的应用,为了推送准备, 因为推送需要根据不同的应用使用不同的 id,secret等。
enum app_type: %i[ik_duli lx_duli lx_yun]
end
\ No newline at end of file
module Push module Huawei
module Huawei class Pusher
ACCESS_TOKEN_KEY = "push:huawei_push:access_token".freeze ACCESS_TOKEN_KEY = "push:huawei_push:access_token".freeze
TOKEN_URL = 'https://login.cloud.huawei.com/oauth2/v2/token'.freeze TOKEN_URL = 'https://login.cloud.huawei.com/oauth2/v2/token'.freeze
PUSH_URL = 'https://api.push.hicloud.com/pushsend.do'.freeze PUSH_URL = 'https://api.push.hicloud.com/pushsend.do'.freeze
ACCESS_TOEKN_EXPIRE_TIME = 3500 ACCESS_TOEKN_EXPIRE_TIME = 3500
BATCH_PUSH_MAX = 80 BATCH_PUSH_MAX = 80
def initialize(device_ids, message)
@device_tokens = device_ids
@message = message
end
# 4a7d376b47c847634571a86b37e93272 测试使用的device_token
def perform
device_tokens = @device_tokens
message = @message
class << self handle_token_res if access_token.blank?
# 4a7d376b47c847634571a86b37e93272 测试使用的device_token device_tokens.each_slice(BATCH_PUSH_MAX) do |device_token_array|
def push(device_tokens, message) res = res_push(message, device_token_array)
handle_token_res if access_token.blank? # 容错处理,华为的token异常问题,重新获取发送。
device_tokens.each_slice(BATCH_PUSH_MAX) do |device_token_array| if %w(80300002 80200001).include? res['code']
res = res_push(message, device_token_array) handle_token_res
# 容错处理,华为的token异常问题,重新获取发送。 res_push(message, device_token_array)
if %w(80300002 80200001).include? res['code']
handle_token_res
res_push(message, device_token_array)
end
end end
rescue StandardError => err
Push::Log.error(err)
end end
rescue StandardError => err
::Log.error(err)
end
def res_push(message, device_tokens) def res_push(message, device_tokens)
body = message(message, device_tokens) body = message(message, device_tokens)
nsp_ctx = { nsp_ctx = {
ver: '1', ver: '1',
appId: Settings.platform_settings.huawei['app_id'] appId: Settings.platform_settings.huawei['app_id']
} }
url = PUSH_URL + "?nsp_ctx=#{CGI::escape(nsp_ctx.to_json)}" url = PUSH_URL + "?nsp_ctx=#{CGI::escape(nsp_ctx.to_json)}"
post(url, body) post(url, body)
end end
# 推送信息 # 推送信息
def message(message, device_tokens) def message(message, device_tokens)
payload = { payload = {
hps: { hps: {
msg: { msg: {
type: 1, type: 1,
body: message body: message
}
} }
} }
Push::Log.info("huawei_payload: #{payload}, device_tokens: #{device_tokens}") }
params = { ::Log.info("huawei_payload: #{payload}, device_tokens: #{device_tokens}")
access_token: CGI.escape(access_token), params = {
nsp_svc: CGI.escape('openpush.message.api.send'), access_token: CGI.escape(access_token),
nsp_ts: CGI.escape(Time.now.to_i.to_s), nsp_svc: CGI.escape('openpush.message.api.send'),
device_token_list: CGI.escape(device_tokens.to_s), nsp_ts: CGI.escape(Time.now.to_i.to_s),
payload: CGI.escape(payload.to_json) device_token_list: CGI.escape(device_tokens.to_s),
} payload: CGI.escape(payload.to_json)
params_str = '' }
params.each_with_index do |(key,value), index| params_str = ''
params_str += "#{key}=#{value}" params.each_with_index do |(key,value), index|
params_str += '&' if index + 1 < params.size params_str += "#{key}=#{value}"
end params_str += '&' if index + 1 < params.size
params_str
end end
params_str
end
def handle_token_res def handle_token_res
loop do loop do
res = req_token res = req_token
if res['error'].blank? && res['access_token'].present? if res['error'].blank? && res['access_token'].present?
$redis.set(ACCESS_TOKEN_KEY, res['access_token'], ex: ACCESS_TOEKN_EXPIRE_TIME) $redis.set(ACCESS_TOKEN_KEY, res['access_token'], ex: ACCESS_TOEKN_EXPIRE_TIME)
break break
end
end end
end end
end
def req_token def req_token
body = { body = {
grant_type: 'client_credentials', grant_type: 'client_credentials',
client_id: Settings.platform_settings.huawei['app_id'], client_id: Settings.platform_settings.huawei['app_id'],
client_secret: Settings.platform_settings.huawei['app_secret'] client_secret: Settings.platform_settings.huawei['app_secret']
} }
post(TOKEN_URL, body) post(TOKEN_URL, body)
end end
def access_token def access_token
$redis.get(ACCESS_TOKEN_KEY) $redis.get(ACCESS_TOKEN_KEY)
end end
def post(url, body) def post(url, body)
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' } headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
response = HTTParty.post(url, body: body, headers: headers, timeout: 5) response = HTTParty.post(url, body: body, headers: headers, timeout: 5)
Push::Log.info("huawei_push: \n access_token: #{access_token} expire_time: #{$redis.ttl(Push::Huawei::ACCESS_TOKEN_KEY)}\n url: #{url}\n body: #{body}\n response: #{response}") ::Log.info("huawei_push: \n access_token: #{access_token} expire_time: #{$redis.ttl(Push::Huawei::ACCESS_TOKEN_KEY)}\n url: #{url}\n body: #{body}\n response: #{response}")
JSON.parse(response.body) JSON.parse(response.body)
end
end end
end end
end end
module Igetui
class Pusher
def initialize(user_device_ids, opts)
opts = HashWithIndifferentAccess.new(opts)
@template_data = opts[:message]
@igetui_opts = opts[:igetui_opts]
@user_device_ids = user_device_ids
@pusher = pusher(opts[:device_platform], opts[:app_type])
@pusher_type = @igetui_opts[:pusher_type]
end
def perform
@template_data[:push_type] ||= 2
@igetui_opts[:transmission_type] ||= 0
@igetui_opts[:transmission_content] = @template_data.as_json
template_type = 'TransmissionTemplate'
template_data = @igetui_opts
result = push_message_to(@pusher, @pusher_type, template_type, template_data, @user_device_ids)
::Log.info("igetui_push: \n template_data: #{template_data}\n user_device_ids: #{@user_device_ids}\n response: #{result}")
end
private
def pusher(device_platform, app_type)
case app_type
when 'lx_duli'
device_platform == 'android' ? $lx_duli_android_pusher : $lx_duli_ios_pusher
when 'lx_yun'
device_platform == 'android' ? $lx_yun_android_pusher : $lx_yun_ios_pusher
when 'aike_yun'
device_platform == 'android' ? $aike_yun_android_pusher : $aike_yun_ios_pusher
else 'ik_duli'
device_platform == 'android' ? $ik_duli_android_pusher : $ik_duli_ios_pusher
end
end
def push_message_to(pusher, pusher_type, template_type, template_data, client_id)
client_id_list = Array(client_id)
template = set_template_data(template_type, template_data)
case pusher_type
when 'push_message_to_single'
message = IGeTui::SingleMessage.new
message.data = template
client_id_list.map do |client_id|
client = IGeTui::Client.new(client_id)
res = pusher.push_message_to_single(message, client)
::Log.info(" push_message_to_single client: #{client.to_json} and message is #{message.to_json} and res is #{res}")
end
when 'push_message_to_list'
message = IGeTui::ListMessage.new
message.data = template
content_id = pusher.get_content_id(message)
clients = client_id_list.map { |client_id| IGeTui::Client.new(client_id) }
res = pusher.push_message_to_list(content_id, clients)
::Log.info("push_message_to_list clients: #{clients.to_json} and message is #{message.to_json} and res is #{res}")
when 'push_message_to_app'
message = IGeTui::AppMessage.new
message.data = template
message.app_id_list = client_id_list
res = pusher.push_message_to_app(message)
::Log.info("push_message_to_app message is #{message.to_json} and res is #{res}")
end
end
def set_template_data(template_type, template_data)
@template ||= "IGeTui::#{template_type}".constantize.new
case template_type
when 'NotificationTemplate', 'NotyPopLoadTemplate', 'LinkTemplate'
template_data.reverse_merge!(Settings.platform_settings.igetui['template_base'])
template_data.each do |k, v|
@template.send("#{k}=", v)
end
@template.set_push_info(*template_data[:push_info]) if template_data.key? :push_info
when 'TransmissionTemplate'
@template.transmission_content = template_data[:transmission_content].is_a?(Hash) ? template_data[:transmission_content].to_json : template_data[:transmission_content]
@template.transmission_type = template_data[:transmission_type] if template_data.key?(:transmission_type)
@template.set_push_info(*template_data[:push_info]) if template_data.key? :push_info
end
@template
end
end
end
module Push
class << self
# 调用推送服务,推送信息
# message 要推送的具体信息
# opts 是hash类型,主要是一些其他的信息。
# 由于历史遗留问题,封装的不够好,这里主要用来处理个推的一些参数。
# 目前华为推送,小米推送不会用到这里的信息。
# device_ids_opts, 用户设备信息是hash数组。
# device_ids_opts = {
# igetui: [],
# apns: [],
# xiaomi: [],
# huawei: []
# }
def push(device_ids_opts, message, igetui_opts = {}, app_type = nil)
Settings.platform = app_type || Settings::DEFAULT
Push::Log.info("*************** push_service ***************:\n device_ids_opts: #{device_ids_opts}, message: #{message}, igetui_opts: #{igetui_opts} platform: #{Settings.platform}")
message = message.with_indifferent_access
igetui_opts = igetui_opts.with_indifferent_access
# igetui
Push::Igetui.push(device_ids_opts['igetui'], message, igetui_opts) if device_ids_opts['igetui'].present?
Push::Igetui.push(device_ids_opts['apns'], message, igetui_opts) if device_ids_opts['apns'].present?
# xiaomi
# https://dev.mi.com/console/doc/detail?pId=1163 文档中心
Push::Xiaomi.push(device_ids_opts['xiaomi'], message) if device_ids_opts['xiaomi'].present?
# huawei
# https://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush_agent.html?page=hmssdk_huaweipush_api_reference_agent_s1 接入文档
Push::Huawei.push(device_ids_opts['huawei'], message) if device_ids_opts['huawei'].present?
# etc
rescue StandardError => err
Push::Log.error(err)
end
end
end
module Push
module Igetui
class << self
def push(user_device_ids, template_data = {}, opts = {})
# 包装成igetui 真正需要的参数格式
opts[:transmission_content] = template_data
if user_device_ids.size > 1
push_transmission_to_list_igetui(user_device_ids, opts)
else
push_transmission_to_igetui(user_device_ids, opts)
end
rescue StandardError => err
::Push::Log.error(err)
end
def config
{
push_type: 2
}
end
def push_transmission_to_igetui(user_device_id, template_data = {})
template_data = handle_template_data(template_data)
push_igetui_with_asnyc(
user_device_id,
template_data,
template_type: 'TransmissionTemplate'
)
end
def push_transmission_to_list_igetui(user_device_ids, template_data = {})
template_data = handle_template_data(template_data)
push_list_igetui_with_asnyc(
user_device_ids,
template_data,
template_type: 'TransmissionTemplate', pusher_type: 'push_message_to_list'
)
end
private
def handle_template_data(template_data)
template_data = HashWithIndifferentAccess.new(template_data)
transmission_content = HashWithIndifferentAccess.new(template_data.delete(:transmission_content))
transmission_content[:push_type] ||= config[:push_type]
template_data[:transmission_type] ||= 0
template_data[:transmission_content] = transmission_content.as_json
template_data
end
def push_igetui_with_asnyc(user_device_id, template_data = {}, opts = nil)
return if template_data.blank?
opts ||= HashWithIndifferentAccess.new(template_type: 'TransmissionTemplate')
template_data = HashWithIndifferentAccess.new(template_data)
perform(template_data, opts, user_device_id)
end
def push_list_igetui_with_asnyc(user_device_ids, template_data = {}, opts = { pusher_type: 'push_message_to_list' })
return if user_device_ids.blank? || template_data.blank?
opts ||= HashWithIndifferentAccess.new(template_type: 'TransmissionTemplate')
perform(template_data, opts, user_device_ids)
end
def perform(template_data, opts, user_device_ids)
opts = HashWithIndifferentAccess.new(opts)
igetui = Igetui::Push.new(template_data, opts)
result = igetui.push_message_to(user_device_ids)
::Push::Log.info("igetui_push: \n template_data: #{template_data}\n opts: #{opts}\n user_device_ids: #{user_device_ids}\n response: #{result}")
end
end
end
end
\ No newline at end of file
module Push
module Igetui
class Push
attr_accessor :pusher, :pusher_type, :template_type, :template, :template_data
PUSHERTYPES = %w[push_message_to_single push_message_to_list push_message_to_app].freeze
TEMPLATETYPES = %w[LinkTemplate NotificationTemplate TransmissionTemplate NotyPopLoadTemplate].freeze
DEVICEPLATFORMS = %w[android ios].freeze
def initialize(template_data, opts = {})
opts = HashWithIndifferentAccess.new(opts)
@template_data = HashWithIndifferentAccess.new(template_data || {})
@pusher_type = PUSHERTYPES.include?((opts[:pusher_type]).to_s) ? opts[:pusher_type] : 'push_message_to_single'
@template_type = TEMPLATETYPES.include?((opts[:template_type]).to_s) ? opts[:template_type] : 'NotificationTemplate'
::Push::Log.info("@template_data is #{@template_data.to_json}")
set_message
set_template_data
@message.data = @template
end
def push_message_to(client_id)
client_id_list = Array(client_id)
case pusher_type
when 'push_message_to_single'
client_id_list.map do |client_id|
client = IGeTui::Client.new(client_id)
pusher.push_message_to_single(@message, client)
end
when 'push_message_to_list'
content_id = pusher.get_content_id(@message)
clients = client_id_list.map { |client_id| IGeTui::Client.new(client_id) }
pusher.push_message_to_list(content_id, clients)
when 'push_message_to_app'
@message.app_id_list = client_id_list
pusher.push_message_to_app(@message)
end
end
private
def pusher
@pusher ||=
if DEVICEPLATFORMS.include?((@template_data['device_platform'].to_s))
case @template_data['device_platform'].to_s
when 'android'
IGeTui.pusher(
Settings.platform_settings.igetui.android['app_id'],
Settings.platform_settings.igetui.android['app_key'],
Settings.platform_settings.igetui.android['master_secret']
)
when 'ios'
IGeTui.pusher(
Settings.platform_settings.igetui.ios['app_id'],
Settings.platform_settings.igetui.ios['app_key'],
Settings.platform_settings.igetui.ios['master_secret']
)
end
else
::Push::Log.info("设备类型不正确#{@device_platform}")
raise "Failed to create pusher"
end
end
def set_message
@message = case pusher_type
when 'push_message_to_single'
IGeTui::SingleMessage.new
when 'push_message_to_list'
IGeTui::ListMessage.new
when 'push_message_to_app'
IGeTui::AppMessage.new
end
end
def set_template_data
@template = "IGeTui::#{template_type}".constantize.new
case template_type
when 'NotificationTemplate', 'NotyPopLoadTemplate', 'LinkTemplate'
@template_data.reverse_merge!(Settings.platform_settings.igetui['template_base'])
@template_data.each do |k, v|
@template.send("#{k}=", v)
end
@template.set_push_info(*@template_data[:push_info]) if @template_data.key? :push_info
when 'TransmissionTemplate'
@template.transmission_content = @template_data[:transmission_content].is_a?(Hash) ? @template_data[:transmission_content].to_json : @template_data[:transmission_content]
@template.transmission_type = @template_data[:transmission_type] if @template_data.key?(:transmission_type)
@template.set_push_info(*@template_data[:push_info]) if @template_data.key? :push_info
end
end
end
end
end
#push功能的代理选择用哪种推送工具1.个推 2.小米 3.华为
class PushToolClient
def initialize(opts={})
opts = HashWithIndifferentAccess.new(opts)
@user_ids = opts[:user_ids]
@opts = opts
end
def do_push
Settings.platform = @opts[:app_type] || Settings::DEFAULT
push_device_collections ||= {}
ActiveRecord::Base.connected_to(role: @opt[:db_role].to_sym) do
push_device_collections = UserDevice.where(user_id: @user_ids).group_by(&:platform)
end
igetui_push_device_collections = push_device_collections['igetui'].group_by(&:device_platform) if push_device_collections['igetui'].is_a? Array
if igetui_push_device_collections.is_a? Hash
igetui_android_client_ids = igetui_push_device_collections['android'].map(&:client_id) if igetui_push_device_collections['android'].is_a? Array
igetui_ios_client_ids = igetui_push_device_collections['ios'].map(&:client_id) if igetui_push_device_collections['ios'].is_a? Array
end
Igetui::Pusher.new(igetui_android_client_ids, @opts.merge(device_platform: 'android')).perform if igetui_android_client_ids.present?
Igetui::Pusher.new(igetui_ios_client_ids, @opts.merge(device_platform: 'ios')).perform if igetui_ios_client_ids.present?
#现在好像不用了
# Push::IgetuiIos.push(@push_collections[:igetui_ios], @opts) if @push_collections[:apns].present?
# Push::IgetuiAndroid.push(@push_collections[:igetui_android], @opts) if @push_collections[:apns].present?
xiaomi_client_ids = push_device_collections['xiaomi'].map(&:client_id) if push_device_collections['xiaomi'].is_a? Array
huawei_client_ids = push_device_collections['huawei'].map(&:client_id) if push_device_collections['huawei'].is_a? Array
Xiaomi::Pusher.new(xiaomi_client_ids, @opts[:message]).perform if xiaomi_client_ids.present?
Huawei::Pusher.new(huawei_client_ids, @opts[:message]).perform if huawei_client_ids.present?
end
end
\ No newline at end of file
module Push module Xiaomi
module Xiaomi class Pusher
PUSH_URL = 'https://api.xmpush.xiaomi.com/v3/message/regid'.freeze PUSH_URL = 'https://api.xmpush.xiaomi.com/v3/message/regid'.freeze
BATCH_PUSH_MAX = 80 BATCH_PUSH_MAX = 80
class << self
def initialize(device_ids, message)
@registration_ids = device_ids
@payload = message
end
def push(registration_ids, payload) def perform
registration_ids.each_slice(BATCH_PUSH_MAX) do |registration_ids_array| registration_ids = @registration_ids
registration_id = registration_ids_array.join(',') payload = @payload
post(message(payload, registration_id))
end
rescue StandardError => err
Push::Log.error(err)
end
def message(payload, registration_id) registration_ids.each_slice(BATCH_PUSH_MAX) do |registration_ids_array|
message = config registration_id = registration_ids_array.join(',')
message[:registration_id] = registration_id post(message(payload, registration_id))
message[:payload] = payload.to_json
message
end end
rescue StandardError => err
::Log.error(err)
end
def message(payload, registration_id)
message = config
message[:registration_id] = registration_id
message[:payload] = payload.to_json
message
end
# notify_type # notify_type
# DEFAULT_ALL = -1; # DEFAULT_ALL = -1;
# DEFAULT_SOUND = 1; // 使用默认提示音提示; # DEFAULT_SOUND = 1; // 使用默认提示音提示;
# DEFAULT_VIBRATE = 2; // 使用默认震动提示; # DEFAULT_VIBRATE = 2; // 使用默认震动提示;
# DEFAULT_LIGHTS = 4; // 使用默认led灯光提示; # DEFAULT_LIGHTS = 4; // 使用默认led灯光提示;
# pass_through # pass_through
# 0 表示通知栏消息, 1 表示透传消息 # 0 表示通知栏消息, 1 表示透传消息
def config def config
{ {
restricted_package_name: Settings.platform_settings.xiaomi['package_name'], restricted_package_name: Settings.platform_settings.xiaomi['package_name'],
pass_through: 1, pass_through: 1,
title: 'xiaomi push', title: 'xiaomi push',
description: 'xiaomi push', description: 'xiaomi push',
notify_type: 1 notify_type: 1
} }
end end
def post(params) def post(params)
response = HTTParty.post(PUSH_URL, query: params, response = HTTParty.post(PUSH_URL, query: params,
headers: { headers: {
'Accept' => 'application/json;', 'Accept' => 'application/json;',
"Authorization": "key=#{Settings.platform_settings.xiaomi['app_secret']}" "Authorization": "key=#{Settings.platform_settings.xiaomi['app_secret']}"
}, },
timeout: 5) timeout: 5)
Push::Log.info("xiaomi_push: \n url: #{PUSH_URL}\n params: #{params}\n response: #{response}") ::Log.info("xiaomi_push: \n url: #{PUSH_URL}\n params: #{params}\n response: #{response}")
response response
end
end end
end end
end end
...@@ -2,8 +2,9 @@ class PushWorker ...@@ -2,8 +2,9 @@ class PushWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: :push sidekiq_options queue: :push
def perform(device_ids_opts, message, igetui_opts, platform = nil) def perform(opts)
Push.push(device_ids_opts, message, igetui_opts, platform) ::PushToolClient.new(opts).do_push
# Push.push(user_ids, message, igetui_opts, platform)
rescue StandardError => e rescue StandardError => e
ErrorLog.error(e) ErrorLog.error(e)
end end
......
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
#
default: &default default: &default
adapter: sqlite3 adapter: mysql2
encoding: utf8
host: rm-m5ef10gzq1n5e7b9abo.mysql.rds.aliyuncs.com
username: crm_rw
password: pHB+!*Uv9Rl9
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000 timeout: 5000
development: development:
<<: *default <<: *default
database: db/development.sqlite3 database: crm_dev
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake". # re-generated from your development database when you run "rake".
......
module Push module Log
module Log class << self
class << self def info(info)
def info(info) logger.tagged(Time.zone.now) { logger.info info }
logger.tagged(Time.zone.now) { logger.info info } end
end
def error(e) def error(e)
logger.tagged(Time.zone.now) { logger.error e.message.to_s } logger.tagged(Time.zone.now) { logger.error e.message.to_s }
e.backtrace.each { |message| logger.tagged(Time.zone.now) { logger.error message.to_s } } e.backtrace.each { |message| logger.tagged(Time.zone.now) { logger.error message.to_s } }
end end
private private
def logger def logger
@logger ||= ActiveSupport::TaggedLogging.new(::Logger.new("#{Rails.root}/log/various_push.log", 'weekly')) @logger ||= ActiveSupport::TaggedLogging.new(::Logger.new("#{Rails.root}/log/various_push.log", 'weekly'))
end
end end
end end
end end
\ No newline at end of file
$ik_duli_android_pusher ||=
IGeTui.pusher(
Settings.ik_duli.igetui.android['app_id'],
Settings.ik_duli.igetui.android['app_key'],
Settings.ik_duli.igetui.android['master_secret']
)
$ik_duli_ios_pusher ||=
IGeTui.pusher(
Settings.ik_duli.igetui.ios['app_id'],
Settings.ik_duli.igetui.ios['app_key'],
Settings.ik_duli.igetui.ios['master_secret']
)
$lx_duli_android_pusher ||=
IGeTui.pusher(
Settings.lx_duli.igetui.android['app_id'],
Settings.lx_duli.igetui.android['app_key'],
Settings.lx_duli.igetui.android['master_secret']
)
$lx_duli_ios_pusher ||=
IGeTui.pusher(
Settings.lx_duli.igetui.ios['app_id'],
Settings.lx_duli.igetui.ios['app_key'],
Settings.lx_duli.igetui.ios['master_secret']
)
$lx_yun_android_pusher ||=
IGeTui.pusher(
Settings.lx_yun.igetui.android['app_id'],
Settings.lx_yun.igetui.android['app_key'],
Settings.lx_yun.igetui.android['master_secret']
)
$lx_yun_ios_pusher ||=
IGeTui.pusher(
Settings.lx_yun.igetui.ios['app_id'],
Settings.lx_yun.igetui.ios['app_key'],
Settings.lx_yun.igetui.ios['master_secret']
)
$aike_yun_android_pusher ||=
IGeTui.pusher(
Settings.aike_yun.igetui.android['app_id'],
Settings.aike_yun.igetui.android['app_key'],
Settings.aike_yun.igetui.android['master_secret']
)
$aike_yun_ios_pusher ||=
IGeTui.pusher(
Settings.aike_yun.igetui.ios['app_id'],
Settings.aike_yun.igetui.ios['app_key'],
Settings.aike_yun.igetui.ios['master_secret']
)
require 'json' unless ENV['RAILS_ENV'] == 'development'
file = File.read('config/server.json')
file = File.read('config/server.json') deploy_config = JSON.parse(file)
deploy_config = JSON.parse(file) port deploy_config['http_port']
port deploy_config['http_port'] environment 'production'
environment 'production' workers 2
workers 2 threads 0, 16
threads 0, 16 bind "unix://#{deploy_config['deploy_to']}/shared/tmp/sockets/push-puma.sock"
bind "unix://#{deploy_config['deploy_to']}/shared/tmp/sockets/push-puma.sock" pidfile "#{deploy_config['deploy_to']}/shared/tmp/pids/puma.pid"
pidfile "#{deploy_config['deploy_to']}/shared/tmp/pids/puma.pid" state_path "#{deploy_config['deploy_to']}/shared/tmp/pids/puma.state"
state_path "#{deploy_config['deploy_to']}/shared/tmp/pids/puma.state" stdout_redirect "#{deploy_config['deploy_to']}/current/log/puma_error.log", "#{deploy_config['deploy_to']}/current/log/puma_access.log", true
stdout_redirect "#{deploy_config['deploy_to']}/current/log/puma_error.log", "#{deploy_config['deploy_to']}/current/log/puma_access.log", true plugin :tmp_restart
plugin :tmp_restart else
port 8094
environment 'development'
workers 1
threads 0, 16
bind "unix://tmp/sockets/push-puma.sock"
pidfile "tmp/pids/puma.pid"
state_path "tmp/pids/puma.state"
plugin :tmp_restart
worker_timeout 7200
end
Rails.application.routes.draw do Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
post 'api/v1/token', to: 'pushs#token' post 'api/v1/token', to: 'pushs#token'
post 'api/v1/push', to: 'pushs#push' post 'api/v1/single', to: 'pushs#single'
post 'api/v1/batch', to: 'pushs#batch'
require 'sidekiq/web' require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq' mount Sidekiq::Web => '/sidekiq'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment