Files
Dena/app/models/member.rb
2026-08-13 19:50:53 +03:30

328 lines
9.4 KiB
Ruby

# encoding: UTF-8
# frozen_string_literal: true
require 'securerandom'
class Member < ApplicationRecord
include ModelCaching
MAX_GROUP = 4
has_many :orders
has_many :accounts
has_many :stats_member_pnl
has_many :payment_addresses
has_many :withdraws, -> { order(id: :desc) }
has_many :deposits, -> { order(id: :desc) }
has_many :beneficiaries, -> { order(id: :desc) }
has_many :bonus
serialize :cards, Array
serialize :ibans, Array
scope :enabled, -> { where(state: 'active') }
before_validation :downcase_email
validates :uid, length: { maximum: 32 }
validates :referral_uid, length: { maximum: 32 }
validates :email, presence: true, uniqueness: true, email: true
validates :level, numericality: { greater_than_or_equal_to: 0 }
validates :role, inclusion: { in: ::Ability.roles }
before_create { self.group = self.group.strip.downcase }
class << self
def groups
TradingFee.distinct.pluck(:group)
end
def group_name_by_number(number)
"vip-#{number}"
end
def min_max_group(group_name)
return [ENV.fetch('VIP_4_AMOUNT').to_d, 0] if group_name == 'vip-4'
return [ENV.fetch('VIP_3_AMOUNT').to_d, ENV.fetch('VIP_4_AMOUNT').to_d] if group_name == 'vip-3'
return [ENV.fetch('VIP_2_AMOUNT').to_d, ENV.fetch('VIP_3_AMOUNT').to_d] if group_name == 'vip-2'
return [ENV.fetch('VIP_1_AMOUNT').to_d, ENV.fetch('VIP_2_AMOUNT').to_d] if group_name == 'vip-1'
return [0, ENV.fetch('VIP_1_AMOUNT').to_d] if group_name == 'vip-0'
end
end
def group_update(order)
return true if self.group == 'bot'
return true unless self.group.include?('vip')
all = self.trades.last_month.inject(0){ |sum, x| sum + x.rls }
return self.update(group: 'vip-4') if all > ENV.fetch('VIP_4_AMOUNT').to_d
return self.update(group: 'vip-3') if all > ENV.fetch('VIP_3_AMOUNT').to_d
return self.update(group: 'vip-2') if all > ENV.fetch('VIP_2_AMOUNT').to_d
return self.update(group: 'vip-1') if all > ENV.fetch('VIP_1_AMOUNT').to_d
self.update(group: 'vip-0')
end
def next_group
return group if group == Member.group_name_by_number(MAX_GROUP)
Member.group_name_by_number(group[4].to_i + 1)
end
def trades
Trade.where('maker_id = ? OR taker_id = ?', id, id)
end
def role
super&.inquiry
end
def admin?
role == "admin"
end
def get_account(model_or_id_or_code)
if model_or_id_or_code.is_a?(String) || model_or_id_or_code.is_a?(Symbol)
accounts.find_or_create_by(currency_id: model_or_id_or_code)
elsif model_or_id_or_code.is_a?(Currency)
accounts.find_or_create_by(currency: model_or_id_or_code)
end
# Thread Safe Account creation
rescue ActiveRecord::RecordNotUnique
if model_or_id_or_code.is_a?(String) || model_or_id_or_code.is_a?(Symbol)
accounts.find_by(currency_id: model_or_id_or_code)
elsif model_or_id_or_code.is_a?(Currency)
accounts.find_by(currency: model_or_id_or_code)
end
end
def referral_user
self.class.find_by(uid: referral_uid) if referral_uid.present?
end
# @deprecated
def touch_accounts
Currency.find_each do |currency|
next if accounts.where(currency: currency).exists?
accounts.create!(currency: currency)
end
end
def balance_for(currency:, kind:)
account_code = Operations::Account.find_by(
type: :liability,
kind: kind,
currency_type: currency.type
).code
liabilities = Operations::Liability.where(member_id: id, currency: currency, code: account_code)
liabilities.sum('credit - debit')
end
def legacy_balance_for(currency:, kind:)
if kind.to_sym == :main
get_account(currency).balance
elsif kind.to_sym == :locked
get_account(currency).locked
else
raise Operations::Exception, "Account for #{options} doesn't exists."
end
end
def revert_trading_activity!(trades)
trades.each(&:revert_trade!)
end
def payment_address(wallet_id, remote = false)
wallet = Wallet.find(wallet_id)
return if wallet.blank?
pa = PaymentAddress.find_by(member: self, wallet: wallet, remote: remote)
if pa.blank?
pa = payment_addresses.create!(wallet: wallet)
elsif pa.address.blank?
pa.enqueue_address_generation
end
pa
end
# Attempts to create additional deposit address for account.
def payment_address!(wallet_id, remote = false)
wallet = Wallet.find(wallet_id)
return if wallet.blank?
pa = PaymentAddress.find_by(member: self, wallet: wallet)
# The address generation process is in progress.
if pa.present? && pa.address.blank?
pa
else
# allows user to have multiple addresses
pa = payment_addresses.create!(wallet: wallet, remote: remote)
end
pa
end
def as_json_for_event_api
{
uid: uid,
email: email,
role: role,
level: level,
otp: otp,
state: state,
referral_uid: referral_uid
}
end
def sync_beneficiaries
fiat_currency = Currency.find('irt')
fiat_beneficiaries = beneficiaries.active_to_member.where(currency: fiat_currency)
array_beneficiaries = fiat_beneficiaries.map { |beneficiary| beneficiary&.data.dig('account_number') }&.compact
# delete extra beneficiary that was deleted in the Dalan
fiat_beneficiaries.each { |b| b.delete unless b.data.dig('account_number')&.in?(flat_cards + flat_ibans) }
# add beneficiary that was added in the Dalan
cards&.each { |c| create_beneficiary('card', c&.keys&.last, c&.values&.last, fiat_currency) unless c&.values&.last.in? (array_beneficiaries) }
ibans&.each { |i| create_beneficiary('iban', i&.keys&.last, i&.values&.last, fiat_currency) unless i&.values&.last.in? (array_beneficiaries) }
end
def flat_cards
return [] unless cards.present?
cards.map(&:values).flatten
end
def flat_ibans
return [] unless ibans.present?
ibans.map(&:values).flatten
end
def create_beneficiary(kind, name, data, currency, state: 'active')
beneficiaries.create!(currency: currency,
data: { full_name: name, account_number: data, kind: kind },
name: name, state: state)
end
private
def downcase_email
self.email = email.try(:downcase)
end
class << self
def uid(member_id)
Member.find_by(id: member_id)&.uid
end
def find_by_username_or_uid(uid_or_username)
if Member.find_by(uid: uid_or_username).present?
Member.find_by(uid: uid_or_username)
elsif Member.find_by(username: uid_or_username).present?
Member.find_by(username: uid_or_username)
end
end
def from_payload(p)
params = filter_payload(p)
validate_payload(params)
member = ::Member.find_or_create_by(uid: p[:uid]) do |m|
m.email = params[:email]
m.username = params[:username]
m.role = params[:role]
m.state = params[:state]
m.level = params[:level]
m.referral_uid = params.dig(:referral_uid)
m.last_change_pass = params.dig(:last_change_pass)
m.cards = params.dig(:cards)
m.ibans = params.dig(:ibans)
m.otp = params.dig(:otp)
end
member.assign_attributes(params)
# comment if dont want sync beneficiary
# flag_beneficiary = member.ibans_changed? || member.cards_changed? ? true : false
member.save! if member.changed?
# member.reload.sync_beneficiaries if flag_beneficiary
member
end
# Filter and validate payload params
def filter_payload(payload)
payload.slice(:email, :username, :uid, :role, :state, :level, :referral_uid, :last_change_pass, :cards, :ibans, :otp)
end
def validate_payload(p)
fetch_email(p)
p.fetch(:uid).tap { |uid| raise(Peatio::Auth::Error, 'UID is blank.') if uid.blank? }
p.fetch(:role).tap { |role| raise(Peatio::Auth::Error, 'Role is blank.') if role.blank? }
p.fetch(:level).tap { |level| raise(Peatio::Auth::Error, 'Level is blank.') if level.blank? }
p.fetch(:state).tap do |state|
raise(Peatio::Auth::Error, 'State is blank.') if state.blank?
raise(Peatio::Auth::Error, 'State is not active.') unless state == 'active'
end
end
def fetch_email(payload)
payload[:email].to_s.tap do |email|
raise(Peatio::Auth::Error, 'E-Mail is blank.') if email.blank?
raise(Peatio::Auth::Error, 'E-Mail is invalid.') unless EmailValidator.valid?(email)
end
end
def search(field: nil, term: nil)
term = "%#{term}%"
case field
when 'email'
where("email LIKE ?", term)
when 'uid'
where('uid LIKE ?', term)
when 'wallet_address'
joins(:payment_addresses).where('payment_addresses.address LIKE ?', term)
else
all
end.order(:id).reverse_order
end
end
end
# == Schema Information
# Schema version: 20201207134745
#
# Table name: members
#
# id :integer not null, primary key
# uid :string(32) not null
# email :string(255) not null
# username :string(255)
# level :integer not null
# role :string(16) not null
# group :string(32) default("vip-0"), not null
# state :string(16) not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_members_on_email (email) UNIQUE
# index_members_on_uid (uid) UNIQUE
# index_members_on_username (username) UNIQUE
#