kvs_account.erl 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. -module(kvs_account).
  2. -include_lib("kvs/include/accounts.hrl").
  3. -include_lib("kvs/include/membership.hrl").
  4. -include_lib("kvs/include/payments.hrl").
  5. -include_lib("kvs/include/log.hrl").
  6. -include_lib("kvs/include/feed_state.hrl").
  7. -compile(export_all).
  8. transaction(Account, Currency, 0, TransactionInfo) -> ok;
  9. transaction(Account, Currency, Amount, TransactionInfo) when Amount /= 0->
  10. {Remitter, Acceptor} = if Amount > 0 -> {system, Account}; true -> {Account, system} end,
  11. transaction(Remitter, Acceptor, Currency, abs(Amount), TransactionInfo).
  12. transaction(Remitter, Acceptor, Currency, Amount, TransactionInfo) ->
  13. TX = #transaction{id = generate_id(), acceptor = Acceptor, remitter = Remitter,
  14. amount = Amount, commit_time = now(), currency = Currency, info = TransactionInfo},
  15. commit_transaction(TX).
  16. debet(Account, Currency) ->
  17. case kvs:get(account,{Account, Currency}) of
  18. {ok,#account{debet = Debet}} -> {ok, Debet};
  19. Error -> Error end.
  20. credit(AccountId, Currency) ->
  21. case kvs:get(account,{AccountId, Currency}) of
  22. {ok,#account{credit = Credit}} -> {ok, Credit};
  23. Error -> Error end.
  24. balance(AccountId, Currency) ->
  25. case kvs:get(account,{AccountId, Currency}) of
  26. {ok, #account{debet = Debet, credit = Credit}} -> {ok, Debet - Credit};
  27. Error -> Error end.
  28. create_account(AccountId) ->
  29. Currencies = get_currencies(),
  30. try [{ok, Currency} = {create_account(AccountId, Currency), Currency} || Currency <- Currencies]
  31. catch _:_ -> {error, unable_create_account} end.
  32. create_account(AccountId, Currency) ->
  33. Account = #account{id = {AccountId, Currency},
  34. credit = 0, debet = 0, last_change = 0},
  35. case kvs:put(Account) of
  36. ok -> ok;
  37. Error -> ?ERROR("create_account: put to db error: ~p", [Error]),
  38. {error, unable_to_store_account} end.
  39. check_quota(User) -> check_quota(User, 0).
  40. check_quota(User, Amount) ->
  41. SoftLimit = kvs:get_config("accounts/quota_limit/soft", -20),
  42. {ok, Balance} = balance(User, quota),
  43. BalanceAfterChange = Balance - Amount,
  44. if
  45. BalanceAfterChange > SoftLimit ->
  46. ok;
  47. true ->
  48. HardLimit = kvs:get(config, "accounts/quota_limit/hard", -100),
  49. if
  50. BalanceAfterChange =< HardLimit ->
  51. {error, hard_limit};
  52. true ->
  53. {error, soft_limit}
  54. end
  55. end.
  56. commit_transaction(#transaction{remitter = R, acceptor = A, currency = Currency, amount = Amount} = TX) ->
  57. case change_accounts(R, A, Currency, Amount) of
  58. ok -> skip;
  59. %mqs:notify([transaction, user, R, add_transaction], TX),% notify_transaction(R,TX),
  60. %mqs:notify([transaction, user, A, add_transaction], TX);%notify_transaction(A,TX);
  61. Error -> skip
  62. % case TX#transaction.info of
  63. % #tx_game_event{} ->
  64. % mqs:notify_transaction(R,TX),
  65. % mqs:notify_transaction(A,TX);
  66. % _ ->
  67. % ?ERROR("commit transaction error: change accounts ~p", [Error]),
  68. % Error
  69. % end
  70. end.
  71. change_accounts(Remitter, Acceptor, Currency, Amount) ->
  72. case {kvs:get(account,{Remitter, Currency}), kvs:get(account,{Acceptor, Currency})} of
  73. {{ok, RA = #account{}}, {ok, AA = #account{}}} ->
  74. ?INFO("transacrion: RemitterAccount ~p, AcceptorAccount: ~p", [RA, AA]),
  75. %% check balance for remitter according to currency and amount
  76. case check_remitter_balance(RA, Amount) of
  77. ok -> RA1 = RA#account{credit = RA#account.credit + Amount, last_change = -Amount },
  78. AA1 = AA#account{debet = AA#account.debet + Amount, last_change = Amount},
  79. kvs:put([AA1, RA1]);
  80. {error, Reason} -> {error, {remitter_balance, Reason}} end;
  81. {{error, Reason}, #account{}} -> {error, {remitter_account_unavailable, Reason}};
  82. {#account{}, {error, Reason}} -> {error, {acceptor_account_unavailable, Reason}};
  83. {RE, AE} -> {error, both_accounts_unavailable} end.
  84. check_remitter_balance(#account{id = {system, _}}, _) -> ok;
  85. check_remitter_balance(_Account, _Amount) -> ok.
  86. get_currencies() -> [internal, currency, money, quota, points].
  87. generate_id() ->
  88. {MegSec, Sec, MicroSec} = now(),
  89. H = erlang:phash2(make_ref()),
  90. lists:concat([MegSec*1000000000000, Sec*1000000, MicroSec, "-", H]).
  91. transactions(UserId) -> tx_list(UserId, undefined, 10000).
  92. tx_list(UserId, undefined, PageAmount) ->
  93. case kvs:get(user_transaction, UserId) of
  94. {ok, O} when O#user_transaction.top =/= undefined -> tx_list(UserId, O#user_transaction.top, PageAmount);
  95. {error, notfound} -> [] end;
  96. tx_list(UserId, StartFrom, Limit) ->
  97. case kvs:get(transaction,StartFrom) of
  98. {ok, #transaction{next = N}=P} -> [ P | kvs:traversal(transaction, #transaction.next, N, Limit)];
  99. X -> [] end.
  100. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  101. handle_notice(["kvs_account", "user", User, "transaction"] = Route,
  102. Message, #state{owner = Owner, type =Type} = State) ->
  103. ?INFO("queue_action(~p): add_transaction: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
  104. MP = Message,
  105. add_transaction_to_user(User,MP),
  106. {noreply, State};
  107. handle_notice(Route, Message, State) -> error_logger:info_msg("Unknown ACCOUNTS notice").
  108. %%%%%%%%%%%%%%%%%%%%%%%%%
  109. add_transaction_to_user(UserId,Purchase) ->
  110. {ok,Team} = case kvs:get(user_transaction, UserId) of
  111. {ok,T} -> {ok,T};
  112. _ -> ?INFO("user_transaction not found"),
  113. Head = #user_transaction{ user = UserId, top = undefined},
  114. {kvs:put(Head),Head}
  115. end,
  116. EntryId = Purchase#transaction.id, %kvs:next_id("membership_purchase",1),
  117. Prev = undefined,
  118. case Team#user_transaction.top of
  119. undefined -> Next = undefined;
  120. X -> case kvs:get(transaction, X) of
  121. {ok, TopEntry} ->
  122. Next = TopEntry#transaction.id,
  123. EditedEntry = #transaction {
  124. commit_time = TopEntry#transaction.commit_time,
  125. amount = TopEntry#transaction.amount,
  126. remitter = TopEntry#transaction.remitter,
  127. acceptor = TopEntry#transaction.acceptor,
  128. currency = TopEntry#transaction.currency,
  129. info = TopEntry#transaction.info,
  130. id = TopEntry#transaction.id,
  131. next = TopEntry#transaction.next,
  132. prev = EntryId },
  133. kvs:put(EditedEntry); % update prev entry
  134. {error,notfound} -> Next = undefined
  135. end
  136. end,
  137. Entry = #transaction{id = EntryId,
  138. commit_time = Purchase#transaction.commit_time,
  139. amount = Purchase#transaction.amount,
  140. remitter = Purchase#transaction.remitter,
  141. acceptor = Purchase#transaction.acceptor,
  142. currency = Purchase#transaction.currency,
  143. info = Purchase#transaction.info,
  144. next = Next,
  145. prev = Prev},
  146. case kvs:put(Entry) of ok -> kvs:put(#user_transaction{ user = UserId, top = EntryId}), {ok, EntryId};
  147. Error -> ?INFO("Cant write transaction"), {failure,Error} end.