kvs_account.erl 6.5 KB

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