Elixir 长生不老药:十进制铸造,不允许负数
您好,我收到一个值,该值不能为负值,只能为正值。如果为负值,我想返回一个自定义错误,如果为负值,则转到管道 我现在有一个问题:Elixir 长生不老药:十进制铸造,不允许负数,elixir,Elixir,您好,我收到一个值,该值不能为负值,只能为正值。如果为负值,我想返回一个自定义错误,如果为负值,则转到管道 我现在有一个问题: def call(%{"id" => id, "value" => value}, operation) do Multi.new() |> Multi.run(:account, fn repo, _changes -> get_account(repo, id) end) |&
def call(%{"id" => id, "value" => value}, operation) do
Multi.new()
|> Multi.run(:account, fn repo, _changes -> get_account(repo, id) end)
|> Multi.run(:update_balance, fn repo, %{account: account} ->
update_balance(repo, account, value, operation)
end)
end
defp update_balance(repo, account, value, operation) do
account
|> operation(value, operation)
|> update_account(repo, account)
end
defp operation(%Account{balance: balance}, value, operation) do
value
|> Decimal.cast()
|> handle_cast(balance, operation)
end
defp handle_cast({:ok, value}, balance, :deposit), do: Decimal.add(balance, value)
defp handle_cast({:ok, value}, balance, :withdraw), do: Decimal.sub(balance, value)
defp handle_cast(:error, _balance, _operation), do: {:error, "Invalid operation!"}
正如我在上一篇评论中提到的,Decimal是一种不同的类型,它大于整数 幸运的是,Decimal提供了gt?,lt?并比较可以用来比较小数和整数的函数。为了尽可能少地修改代码,一个比较干净的解决方案是在handle_cast之前添加一个小的额外函数,并为handle_cast添加另一个定义。大概是这样的: defp操作%Account{balance:balance},值,操作do 价值 |>小数点转换 |>也许是肯定的? |>操纵平衡、操作 终止 defp可能是正的?{:好的,值}do 大小写小数。比较?值,0 do :lt->{:错误,:负数} :gt->{:好的,值} 终止 终止 defp可能为正?错误,do:错误 defp句柄\u强制转换{:错误,:负数,\u平衡,\u操作do {:错误,数字必须为正} 终止 defp handle_cast{:确定,值},余额,:存款,do:Decimal.addbalance,值 ...
这样,如果它是一个负数,它将通过handle_cast的{:error,:negative_number}案例,如果它是正数或者由于某种原因转换失败,它将通过您已有的另一个handle_cast验证,而@sbacarob的答案是完全正确的,为了方便起见,我将发布此消息 Decimal是一个函数,所以可以使用它的内部结构来处理负数。这将适用于现代版本的长生不老药≥ 1.11和检察官办公室≥ 二十三, defguard是_decimal _positivevalue _mapvalue和 值.\uuuu结构\uuuu==十进制和 value.sign==1 有关详细信息,请参阅。并将其用作 defp操作%Account{balance:balance},值,操作do 价值 |>小数点转换 |>操纵平衡、操作 终止 defp handle_cast{:ok,value},balance 什么时候是十进制值, do:Decimal.addbalance,value 解除控制手柄\u投射\u错误,\u平衡,\u, do:{:错误,数字必须为正小数}
太好了,我要吃长生不老药! 欧盟金融机构助理和职能: 安置:
我试过这样做,但仍然接受负数,而且我还需要在检查负数之前转换为十进制。我用反完整代码编辑问题。如果我将值用作字符串,它仍然是负数,但如果我使用有效的数字。是的,如果是字符串,它将不起作用,因为字符串总是大于数字。2<3不会失败,但会返回false。我刚刚用更多的选项更新了答案是的,对不起。我正在更新答案。Decimal是一种不同的类型,和字符串一样,它也总是大于整数,这太神奇了!我没有意识到这一点,但肯定允许使用更干净的代码,而不需要太多的麻烦。嗯,我提供了一个本地支持警卫的程序。让我们看看。@Aleksei Matiushkin谢谢你伟大的解决方案,兄弟。
defmodule Rocketpay.Accounts.Operation do
alias Ecto.Multi
alias Rocketpay.Account
def call(%{"id" => id, "value" => value}, operation) do
fetch_account_operation_name = build_fetch_account_operation_name(operation)
Multi.new()
|> Multi.run(
fetch_account_operation_name,
fn repo, _previous -> fetch_account(repo, id) end
)
|> Multi.run(operation, fn repo, fetch_result ->
account = Map.get(fetch_result, fetch_account_operation_name)
update_balance(repo, account, value, operation)
end)
end
defp fetch_account(repo, id) do
case repo.get(Account, id) do
nil -> {:error, "Account not found."}
account -> {:ok, account}
end
end
defp update_balance(repo, account, value, operation) do
account
|> calculate_new_balance(value, operation)
|> update_account(account, repo)
end
defp calculate_new_balance(%Account{balance: balance}, value, operation) do
value
|> Decimal.cast()
|> validate_greater_than_zero()
|> handle_cast(balance, operation)
end
defp validate_greater_than_zero({:ok, casted_value}) do
case Decimal.compare(casted_value, 0) do
:lt -> :error
:eq -> :error
:gt -> {:ok, casted_value}
end
end
defp validate_greater_than_zero(:error), do: :error
defp handle_cast({:ok, valid_value}, balance, :deposit), do: Decimal.add(balance, valid_value)
defp handle_cast({:ok, valid_value}, balance, :withdraw), do: Decimal.sub(balance, valid_value)
defp handle_cast(:error, _balance, :deposit), do: {:error, "Invalid deposit value."}
defp handle_cast(:error, _balance, :withdraw), do: {:error, "Invalid withdraw value."}
defp update_account({:error, _reason} = error, _account, _repo), do: error
defp update_account(new_balance, account, repo) do
account
|> Account.changeset(%{balance: new_balance})
|> repo.update()
end
defp build_fetch_account_operation_name(operation),
do: "fetch_#{operation}_account" |> String.to_atom()
end