字符串的自定义自动递增字段提供重复条目

Custom auto incrementing fields for string giving duplicate entries

我目前正在开发一个应用程序,要求在系统中注册的每个学生在注册时都有一个唯一的注册号,每次注册都会自动递增。 2019年第一个注册的学生注册号格式为“01/19”,第890个学生注册号为“890/19”等

注册数据是从 kobo data collection 工具上传的,因此它会在非常短的时间间隔内到达我的端点,一次可以完成近 200 次提交。

我发现在 1000 条记录中有将近 27 个重复注册号。这就是我实现注册号生成逻辑的方式

def registration_changeset(student, attrs) do
    student |> changeset(attrs) |> Accounts.add_registration_number()
 end

然后Accounts context有如下添加注册号的代码

def add_registration_number(changeset) do
  struct = changeset.data.__struct__
  registration_number =
  case struct |> last() |> Repo.one() do
    nil -> _create_new_registration_number()
    resource -> _increment_registration(resource.registration_number)
  end

  put_change(changeset, :registration_number, registration_number)
end

我打赌最后创建的学生在上述情况下拥有最新的注册号

有没有更好的实现方式?

最简单的解决办法就是让自增ID作为注册号的第一部分,将两位数的年份单独存储为一个字段。然后在模型中,您只需使用一个 'get_registration_number' 方法来组合这两者。这解决了重复问题。

不利之处在于,您将有 890/19 作为 2019 年的最后一名学生,然后 891/20 作为 2020 年的第一名学生,而不是 01/20。但是您的示例似乎也不能很好地处理这种情况,并且暗示但不清楚这是一项要求。

如果是,您可以不让 ID 成为第一部分,而是创建一个像以前一样自动递增的列(id_num,比如说),然后在年份更改时重置下一个值该列上的 1。如果您想成为偏执狂,您可以说 'id_num' 列和 'two_digit_year' 列一起形成一个唯一键。

TL;DR:处理这个问题的最简单方法是让数据库处理它。

这里需要一些同步代码:)

创建一个专门用于单一目的的进程:生成数字。 GenServer.handle_call/3 已经满足您的所有需求,进程邮箱是完美的队列,OTP 会为您做所有事情。

defmodule MyApp.RegNumHelper do
  @moduledoc false

  use GenServer

  def start_link(opts),
    do: GenServer.start_link(__MODULE__, opts, name: name)

  def add_registration_number(changeset),
    do: GenServer.call(__MODULE__, {:reg_num, changeset})


  @impl true
  def init(opts), do: {:ok, opts}

  @impl true
  def handle_call({:reg_num, changeset}, _from, state) do
    # your logic assigning changeset
    {:reply, changeset, state}
  end
end

这种方法还有另一个优点:由于进程已经是有状态的,您实际上不需要每次都查询数据库。只需在进程启动时查询它并将当前数字保存到 state.