Commit 000436c5 by zmj

add api

parent d581e4c7
class PushsController < ApplicationController
include ActionController::HttpAuthentication::Token
skip_before_action :verify_authenticity_token
def token
param! :app_name, String, required: true
param! :app_id, String, required: true
param! :app_secret, String, required: true
app_setting = AppSettings.send(params[:app_name].to_sym) rescue {}
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])}
else
render json: { code: -1, message: '应用不存在或者app_id/app_secret不匹配'}
end
end
def push
param! :app_name, String, required: true
param! :user_device_ids, Array, required: true
param! :message, String, required: true
token = token_and_options(request).first
if token == Token.token(params[:app_name])
::PushWorker.perform_async(params[:user_device_ids], JSON.parse(params[:message]))
render json: { code: 0, message: 'success'}
else
render json: { code: -1, message: 'token 错误/过期'}
end
end
end
class AppSettings < Settingslogic
source "#{Rails.root}/config/app.yml"
namespace Rails.env
end
\ No newline at end of file
module ErrorLog
class << self
def info(info)
logger.tagged(Time.zone.now) { logger.info info }
end
def error(e)
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 } }
end
private
def logger
@logger ||= ActiveSupport::TaggedLogging.new(::Logger.new("#{Rails.root}/log/error.log", 'weekly'))
end
end
end
module Push
module Huawei
ACCESS_TOKEN_KEY = "push:huawei_push:access_token".freeze
TOKEN_URL = 'https://login.cloud.huawei.com/oauth2/v2/token'.freeze
PUSH_URL = 'https://api.push.hicloud.com/pushsend.do'.freeze
ACCESS_TOEKN_EXPIRE_TIME = 3500
class << self
# 4a7d376b47c847634571a86b37e93272 测试使用的device_token
def push(device_tokens, message)
handle_token_res if access_token.blank?
device_tokens.each_slice(100) do |device_token_array|
res_push(message, device_token_array)
end
rescue StandardError => err
Push::Log.error(err)
end
def res_push(message, device_tokens)
body = message(message, device_tokens)
nsp_ctx = {
ver: '1',
appId: PushSettings.huawei['app_id']
}
url = PUSH_URL + "nsp_ctx=#{CGI::escape(nsp_ctx.to_json)}"
post(url, body)
end
# 推送信息
def message(message, device_tokens)
payload = {
hps: {
msg: {
type: 1,
body: message
}
}
}
Push::Log.info("huawei_payload: #{payload}")
params = {
access_token: CGI.escape(access_token),
nsp_svc: CGI.escape('openpush.message.api.send'),
nsp_ts: CGI.escape(Time.now.to_i.to_s),
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 += "#{key}=#{value}"
params_str += '&' if index + 1 < params.size
end
params_str
end
def handle_token_res
loop do
res = req_token
if res['error'].blank? && res['access_token'].present?
$redis.set(ACCESS_TOKEN_KEY, res['access_token'], ex: ACCESS_TOEKN_EXPIRE_TIME)
break
end
end
end
def req_token
body = {
grant_type: 'client_credentials',
client_id: PushSettings.huawei['app_id'],
client_secret: PushSettings.huawei['app_secret']
}
post(TOKEN_URL, body)
end
def access_token
$redis.get(ACCESS_TOKEN_KEY)
end
def post(url, body)
headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
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}")
JSON.parse(response)
end
end
end
end
\ No newline at end of file
module Push
module Igetui
class << self
attr_accessor :config
def push(user_device_ids, template_data = {})
if user_device_ids.size > 1
push_transmission_to_list_igetui(user_device_ids, template_data)
else
push_transmission_to_igetui(user_device_ids, template_data)
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)
Push::Log.info("template_data: #{template_data}")
Push::Log.info("opts: #{opts}")
Push::Log.info("user_device_ids: #{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("result: #{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
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'
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 ||= IGeTui.pusher(
PushSettings.igetui['app_id'],
PushSettings.igetui['app_key'],
PushSettings.igetui['master_secret']
)
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!(PushSettings.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
module Push
module Log
class << self
def info(info)
logger.tagged(Time.zone.now) { logger.info info }
end
def error(e)
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 } }
end
private
def logger
@logger ||= ActiveSupport::TaggedLogging.new(::Logger.new("#{Rails.root}/log/various_push.log", 'weekly'))
end
end
end
end
module Push
class << self
# transmission_content 是真正的推送信息
# {
# transmission_content: opts,
# transmission_type: transmission_type
# }
def push(user_device_ids, message)
Log.info("push: user_device_ids: #{user_device_ids}, message: #{message}")
message = message.with_indifferent_access
# igetui
Igetui.push(user_device_ids, message)
# xiaomi
# https://dev.mi.com/console/doc/detail?pId=1163 文档中心
Xiaomi.push(user_device_ids, message['transmission_content'])
# huawei
# https://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush_agent.html?page=hmssdk_huaweipush_api_reference_agent_s1 接入文档
Huawei.push(user_device_ids, message['transmission_content'])
# etc
rescue StandardError => err
Log.error(err)
end
end
end
module Push
module Xiaomi
PUSH_URL = 'https://api.xmpush.xiaomi.com/v3/message/regid'.freeze
class << self
# 4a7d376b47c847634571a86b37e93272 测试使用的registration_id
def push(registration_id, payload)
registration_id = registration_id.join(',')
post(message(payload, registration_id))
rescue StandardError => err
Push::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
# DEFAULT_ALL = -1;
# DEFAULT_SOUND = 1; // 使用默认提示音提示;
# DEFAULT_VIBRATE = 2; // 使用默认震动提示;
# DEFAULT_LIGHTS = 4; // 使用默认led灯光提示;
# pass_through
# 0 表示通知栏消息, 1 表示透传消息
def config
{
restricted_package_name: PushSettings.xiaomi['package_name'],
pass_through: 1,
title: 'xiaomi push',
description: 'xiaomi push',
notify_type: 1
}
end
def post(params)
response = HTTParty.post(PUSH_URL, query: params,
headers: {
'Accept' => 'application/json;',
"Authorization": "key=#{PushSettings.xiaomi['app_secret']}"
},
timeout: 5)
Push::Log.info("xiaomi_push: \n url: #{PUSH_URL}\n params: #{params}\n response: #{response}")
response
end
end
end
end
class PushSettings < Settingslogic
source "#{Rails.root}/config/push.yml"
namespace Rails.env
end
\ No newline at end of file
class Settings < Settingslogic
source "#{Rails.root}/config/settings.yml"
namespace Rails.env
end
\ No newline at end of file
module Token
ACCESS_TOEKN_EXPIRE_TIME = 3600
class << self
def key(app_name)
"push:api:#{app_name}:access_token"
end
def generate_token(app_name)
token = SecureRandom.hex(32)
$redis.set(key(app_name), token, ex: ACCESS_TOEKN_EXPIRE_TIME)
token
end
def token(app_name)
$redis.get(key(app_name))
end
def ttl(app_name)
$redis.ttl(key(app_name))
end
end
end
\ No newline at end of file
class PushWorker
include Sidekiq::Worker
sidekiq_options queue: :push
def perform(user_device_ids, message)
Push.push(user_device_ids, message)
rescue StandardError => e
ErrorLog.error(e)
end
end
\ No newline at end of file
defaults: &defaults
crm:
app_id: 'ba9936cf01aaf888'
app_secret: 'cd855e8ea1ff27034425e502d974eb3a'
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
\ No newline at end of file
$redis = Redis.new(
host: Settings.redis.host,
port: Settings.redis.port,
password: Settings.redis[:password],
db: Settings.redis.data_db
)
\ No newline at end of file
# https://github.com/mperham/sidekiq/wiki/Using-Redis Disabled CLIENT command
redis_config = {
host: Settings.redis.host,
port: Settings.redis.port,
password: Settings.redis[:password],
db: Settings.redis.job_db,
id: nil
}
Sidekiq.configure_server do |config|
config.redis = redis_config
end
Sidekiq.configure_client do |config|
config.redis = redis_config
end
Sidekiq.default_worker_options = {
'retry' => false,
'queue' => 'push',
'backtrace' => true
}
igetui:
app_id: "APP_ID"
app_key: "APP_KEY"
app_secret: "APP_SECRET"
master_secret: "MASTER_SECRET"
template_base:
logo: "ik_logo.png"
logo_url: "http://www.ikcrm.com/assets/img/ik_logo.png"
title: ""
text: ""
huawei:
app_id: "****"
app_secret: "****"
\ No newline at end of file
defaults: &defaults
redis: &redis
host: localhost
port: 6379
data_db: 0
job_db: 1
cache_db: 2
timeout: 5000
development:
<<: *defaults
log_cache: true
test:
<<: *defaults
production:
<<: *defaults
\ No newline at end of file
:concurrency: 5
development:
:concurrency: 10
:queues:
- push
\ No newline at end of file
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