Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ gem 'schema_to_scaffold'
gem 'http_accept_language'
gem 'locale_router', git: 'https://github.com/yhk1038/locale_router.git'
gem 'omise'
gem 'iamport'
gem 'rack-cors'
gem 'rails-file_storage', git: 'https://github.com/yhk1038/file_storage.git'

Expand Down
29 changes: 16 additions & 13 deletions app/adapters/external_channel/adapter_factory.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
module ExternalChannel
class AdapterFactory
def self.get_adapter(channel_name)
case channel_name.downcase
when 'haravan'
HaravanAdapter.new
when 'tiki'
TikiAdapter.new
when 'sendo'
SendoAdapter.new
when 'lazada'
LazadaAdapter.new
when 'shopee'
ShopeeAdapter.new
else
class << self
def known_adapter_map
{
haravan: HaravanAdapter,
tiki: TikiAdapter,
sendo: SendoAdapter,
lazada: LazadaAdapter,
shopee: ShopeeAdapter
}
end

def adapter(channel_name)
known_adapter_map[channel_name.to_s.downcase.to_sym].new || adapter_not_found!
end

def adapter_not_found!
raise NotImplementedError, 'Requested Data Is Not Supported!'
end
end
Expand Down
21 changes: 17 additions & 4 deletions app/adapters/external_channel/base_adapter.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ExternalChannel
class BaseAdapter
DEAFULT_EXCEPTION = [
DEFAULT_EXCEPTION = [
Errno::ETIMEDOUT, Timeout::Error,
Faraday::TimeoutError, Faraday::RetriableResponse, Faraday::ConnectionFailed
].freeze
Expand Down Expand Up @@ -28,7 +28,7 @@ def check_token_validation; end

# == 리퀘스트를 던지는 caller 메소드입니다.
def request_get(endpoint, params, headers, retry_exceptions = nil)
retry_exceptions ||= DEAFULT_EXCEPTION
retry_exceptions ||= DEFAULT_EXCEPTION
Faraday.new(endpoint, params: params, request: { open_timeout: 5, timeout: 5 }) do |conn|
conn.response :logger, Rails.logger
conn.request(:retry, max: 5, interval: 1, exceptions: retry_exceptions)
Expand All @@ -38,7 +38,7 @@ def request_get(endpoint, params, headers, retry_exceptions = nil)
end

def request_post(endpoint, body, headers, retry_exceptions = nil)
retry_exceptions ||= DEAFULT_EXCEPTION
retry_exceptions ||= DEFAULT_EXCEPTION
Faraday.new(endpoint, request: { open_timeout: 5, timeout: 5 }) do |conn|
conn.response :logger, Rails.logger
conn.request(:retry, max: 5,
Expand All @@ -54,7 +54,20 @@ def request_post(endpoint, body, headers, retry_exceptions = nil)
end

# == 전달받은 쿼리 파라미터를 각 채널의 포맷에 맞게 변환합니다.
def parse_query_hash(query); end
# == query_mapper 와 date_formatter 가 반드시 필요합니다.
# == query_mapper 와 date_formatter 는 하위 클래스에서 세부 구현합니다.
def parse_query_hash(query_mapper, query_hash)
query_hash['from'] ||= date_formatter((DateTime.now - 1.days).beginning_of_day.utc)
query_hash['to'] ||= date_formatter((DateTime.now - 1.days).end_of_day.utc)
query_hash['key'] ||= 'updated'

query_hash.replace({
query_mapper[query_hash['key']][0]=> date_formatter(query_hash['from'].to_time),
query_mapper[query_hash['key']][1]=> date_formatter(query_hash['to'].to_time)
})
end

def date_formatter(utc_time); end

# == 적절하게 정제된 데이터를 리턴합니다.
def products(query = {}); end
Expand Down
24 changes: 10 additions & 14 deletions app/adapters/external_channel/haravan_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class HaravanAdapter < BaseAdapter
# = fields : response 데이터 중 특정 스키마만 골라서 볼 수 있습니다
}

QUERY_MAPPER = {
'created'=> %w[created_at_min created_at_max],
'updated'=> %w[updated_at_min updated_at_max],
}

def initialize
super
api_key = Rails.application.credentials.dig(:haravan, :api, :key)
Expand All @@ -52,26 +57,17 @@ def initialize

# == 적절하게 정제된 데이터를 리턴합니다.
def products(query_hash = {})
parse_query_hash(query_hash)

refine_products(call_products(query_hash))
refine_products(call_products(parse_query_hash(QUERY_MAPPER, query_hash)))
end

def orders(query_hash = {})
parse_query_hash(query_hash)

refine_orders(call_orders(query_hash))
refine_orders(call_orders(parse_query_hash(QUERY_MAPPER, query_hash)))
end

def login; end

def parse_query_hash(query_hash)
query_hash['updated_from'] ||= DateTime.now - 1.days
query_hash['updated_to'] ||= DateTime.now
query_hash['updated_at_min'] = query_hash['updated_from'].to_s
query_hash['updated_at_max'] = query_hash['updated_to'].to_s
query_hash.delete('updated_from')
query_hash.delete('updated_to')
def date_formatter(utc_time)
utc_time.strftime('%Y-%m-%d %H:%M')
end

# == 외부 채널의 API 를 사용하여 각 레코드를 가져옵니다.
Expand Down Expand Up @@ -151,7 +147,7 @@ def refine_orders(records)
paid_at: paid_at,
billing_amount: record['total_price'],
ship_fee: record['shipping_lines'].inject(0) { |sum, line| sum + (line['price']) },
variant_ids: record['line_items'].map { |variant| [variant['variant_id'], variant['quantity'].to_i] },
variant_ids: record['line_items'].map { |variant| [variant['variant_id'], variant['quantity'].to_i, variant['price'].to_i ] },
cancelled_status: record['cancelled_status'],
shipping_status: record['fulfillments_status']
}
Expand Down
44 changes: 21 additions & 23 deletions app/adapters/external_channel/lazada_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@ class LazadaAdapter < BaseAdapter
# = sort_direction : 오름차순과 내림차순을 결정
# = offset : 지정한 갯수의 데이터를 skip
# = limit : 응답 받을 데이터의 갯수
# = create_before : 주문 생성 시간 <= date (ISO 8601)
# = create_after : 주문 생성 시간 >= date (ISO 8601)
# = created_before : 주문 생성 시간 <= date (ISO 8601)
# = created_after : 주문 생성 시간 >= date (ISO 8601)
# = update_before : 주문 업데이트 시간 <= date (ISO 8601)
# = update_after : 주문 업데이트 시간 >= date (ISO 8601)
}

QUERY_MAPPER = {
'products'=> {
'created'=> %w[create_after create_before],
'updated'=> %w[update_after update_before],
},
'orders'=> {
'created'=> %w[created_after created_before],
'updated'=> %w[update_after update_before],
}
}

def initialize
super
@token = ExternalChannel::Token.find_or_create_by(country: Country.send(ApplicationRecord.country_code),
Expand Down Expand Up @@ -62,7 +73,7 @@ def get_code
options.add_argument('--no-sandbox')

browser = Selenium::WebDriver.for :chrome, options: options
browser.navigate.to "https://auth.lazada.com/oauth/authorize?response_type=code&force_auth=true&redirect_uri=https://api.gomistore.com/external_channels/code&client_id=#{app_key}"
browser.navigate.to "https://auth.lazada.com/oauth/authorize?response_type=code&force_auth=true&redirect_uri=https://408d19fef5ad.ngrok.io/external_channels/code&client_id=#{app_key}"

# = 로그인이 안 되어있는 경우 : form.empty? => true
form = browser.find_elements(css: 'form[name=form1]')
Expand Down Expand Up @@ -131,29 +142,20 @@ def check_token_validation
def products(query_hash = {})
check_token_validation

parse_query_hash(query_hash)

refine_products(call_products(query_hash))
refine_products(call_products(parse_query_hash(QUERY_MAPPER['products'], query_hash)))
end

def orders(query_hash = {})
check_token_validation

parse_query_hash(query_hash)

refine_orders(call_orders(query_hash))
refine_orders(call_orders(parse_query_hash(QUERY_MAPPER['orders'], query_hash)))
end

protected
def login; end

def parse_query_hash(query_hash)
query_hash['updated_from'] ||= DateTime.now - 1.days
query_hash['updated_to'] ||= DateTime.now
query_hash['update_after'] = query_hash['updated_from'].to_time.iso8601.to_s
query_hash['update_before'] = query_hash['updated_to'].to_time.iso8601.to_s
query_hash.delete('updated_from')
query_hash.delete('updated_to')
def date_formatter(utc_time)
utc_time.to_datetime.iso8601
end

def call_products(query_hash)
Expand All @@ -162,8 +164,6 @@ def call_products(query_hash)
end

def call_orders(query_hash)
query_hash.merge!({ created_after: '2018-02-10T16:00:00+08:00' }) if query_hash.empty?

response = request_get('/orders/get', query_hash)
response.body['data']['orders']
end
Expand Down Expand Up @@ -193,7 +193,7 @@ def refine_products(records)
brand_name: record['attributes']['brand'],
variants: refine_product_options(record['skus'])
}
end
end if records.present?

product_property
end
Expand Down Expand Up @@ -223,7 +223,6 @@ def refine_orders(records)

records.each do |record|
call_order_items(record['order_id']).each_with_index do |order_item, index|
#Rails.logger.info order_item['sku']
order_property << {
id: "#{record['order_id']}-#{index}",
order_number: record['order_number'],
Expand All @@ -235,13 +234,12 @@ def refine_orders(records)
billing_amount: record['price'].to_i + record['shipping_fee'],
ship_fee: record['shipping_fee'],
cancelled_status: ['cancelled'].include?(order_item['status']) ? order_item['status'] : nil,
variant_ids: [[order_item['sku'].to_s, 1]],
variant_ids: [[order_item['sku'].to_s, 1, order_item['item_price'].to_i]],
shipping_status: %w[ready_to_ship, delivered, shipped returned].include?(record['statuses']) ? order_item['status'] : nil
}
end
end
end if records.present?

#Rails.logger.info order_property
order_property
end
end
Expand Down
89 changes: 46 additions & 43 deletions app/adapters/external_channel/sendo_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ class SendoAdapter < BaseAdapter
# order_status: 주문 상태
# }

attr_reader :base_url, :default_headers, :token
attr_reader :base_url, :default_headers, :token, :request_type

QUERY_MAPPER = {
'products'=> {
'updated'=> %w[date_from date_to]
},
'orders'=> {
'created'=> %w[order_date_from order_date_to],
'updated'=> %w[order_status_date_from order_status_date_to]
}
}

def initialize
super
Expand All @@ -35,30 +45,26 @@ def check_token_validation
login if !token.auth_token || @token.auth_token_expired?
end

# query로 들어오는 값을 지원하는 방식에 맞게 수정합니다.

# === sendo는 데이터 형식에 따라 query를 리턴하는 방식이 다릅니다.
# === 따라서 curring하는 형식으로 처리했습니다.
def parse_query_hash(data_type)
case data_type
when 'product'
->(query) { parse_query_on_product(default_query(query)) }
when 'order'
->(query) { parse_query_on_order(default_query(query)) }
end
end

# == 적절하게 정제된 데이터를 리턴합니다.
def products(query_hash = {})
check_token_validation

refine_products(call_products(parse_query_hash('product').call(query_hash)))
refine_products(call_products(parse_query_hash(QUERY_MAPPER['products'], query_hash)))
end

def orders(query_hash = {})
check_token_validation

refine_orders(call_orders(parse_query_hash('order').call(query_hash)))
refine_orders(call_orders(parse_query_hash(QUERY_MAPPER['orders'], query_hash)))
end

def parse_query_hash(query_mapper, query_hash)
@request_type = query_hash['key'] || 'updated'
super
end

def date_formatter(utc_time)
utc_time.to_datetime.new_offset(0)
end

def login
Expand Down Expand Up @@ -101,8 +107,14 @@ def call_orders(query_hash = {})
'Content-Type': 'application/json',
'cache-control': 'no-cache'
}
request_lists(orders_url, orders_body, orders_header) do |orders|
orders['result']['data']
from, to = QUERY_MAPPER['orders'][request_type]
interval = 2.days
request_interval(orders_body[from], orders_body[to], interval) do |requestFrom, requestTo|
orders_body[from] = date_time_format(requestFrom)
orders_body[to] = date_time_format(requestTo)
request_lists(orders_url, orders_body, orders_header) do |orders|
orders['result']['data']
end
end
end

Expand All @@ -127,7 +139,7 @@ def refine_orders(orders)
id: sales_data['order_number'].to_s,
order_number: sales_data['order_number'],
billing_amount: sales_data['total_amount_buyer'],
variant_ids: sales_details.map { |option| [option['sku'], option['quantity']] },
variant_ids: sales_details.map { |option| [ option['sku'], option['quantity'].to_i, option['price'].to_i ] },
order_status: map_order_status(sales_data['order_status']),
cancelled_status: cancelled_status(sales_data['order_status']),
shipping_status: shipping_status(sales_data['order_status']),
Expand All @@ -142,30 +154,8 @@ def refine_orders(orders)

private

### === 요청 query데이터를 parsing하는 로직입니다.

def parse_query_on_product(query_hash)
update_from = query_hash[:updated_from].to_datetime
update_to = query_hash[:updated_to].to_datetime
{
date_from: "#{update_from.year}/#{update_from.month}/#{update_from.day}",
date_to: "#{update_to.year}/#{update_to.month}/#{update_to.day}"
}
end

def parse_query_on_order(query_hash)
update_from = query_hash[:updated_from].to_datetime
update_to = query_hash[:updated_to].to_datetime
{
order_date_from: "#{update_from.year}/#{update_from.month}/#{update_from.day}",
order_date_to: "#{update_to.year}/#{update_to.month}/#{update_to.day}"
}
end

def default_query(query_hash)
query_hash[:updated_from] ||= Time.now - 1.days
query_hash[:updated_to] ||= Time.now
query_hash
def date_time_format(utc_time)
"#{utc_time.year}/#{utc_time.month}/#{utc_time.day}"
end

### === 데이터를 불러오는 로직입니다.
Expand All @@ -190,6 +180,19 @@ def get_all_by_ids(ids, url, query_hash = {})
end
end

def request_interval(from, to, interval)
return unless block_given?

data = []
while from < to
tempFrom = to - interval
request_from = tempFrom < from ? from : tempFrom
data << yield(request_from, to)
to = request_from
end
data.flatten
end

# == list 에서 모든 데이터를 요청합니다.
def request_lists(url, body, header)
body[:token] = ''
Expand Down
Loading