You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel+MySQL中实现按年重置的自定义自增发票ID

带年份且每年重置的发票ID最优实现方案(Laravel+MySQL)

核心思路

不能直接用单一自增列实现,必须拆分年份和年度序号两个字段来维护:

  • 保留原有的自增主键id作为内部唯一标识(不对外展示),保证数据库操作的基础性能
  • 新增invoice_year(存储年份)和invoice_number(存储当年自增序号)两个字段,通过联合索引确保同一年的序号唯一,同时兼顾查询性能

具体实现步骤

1. 数据库表结构调整

用Laravel迁移修改发票表:

Schema::table('invoices', function (Blueprint $table) {
    // 存储发票年份,例如2023、2024
    $table->unsignedSmallInteger('invoice_year')->after('id');
    // 存储当年的自增序号,每年从1开始
    $table->unsignedInteger('invoice_number')->after('invoice_year');
    
    // 新增联合唯一索引,确保同一年的序号不重复,同时优化查询
    $table->unique(['invoice_year', 'invoice_number']);
    // 单独给invoice_year加索引,方便按年份筛选发票
    $table->index('invoice_year');
});

2. 自动生成年度序号逻辑

在Invoice模型的creating事件中,自动计算当前年份的下一个序号:

class Invoice extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($invoice) {
            $currentYear = date('Y');
            $invoice->invoice_year = $currentYear;
            
            // 查询当前年份最大的发票序号,不存在则设为0,加1后作为新序号
            $lastNumber = self::where('invoice_year', $currentYear)->max('invoice_number') ?? 0;
            $invoice->invoice_number = $lastNumber + 1;
        });
    }
}

如果并发量较高,建议加数据库事务避免重复序号:

static::creating(function ($invoice) {
    $currentYear = date('Y');
    DB::transaction(function () use ($invoice, $currentYear) {
        $lastNumber = self::where('invoice_year', $currentYear)->lockForUpdate()->max('invoice_number') ?? 0;
        $invoice->invoice_year = $currentYear;
        $invoice->invoice_number = $lastNumber + 1;
    });
});

3. 对外展示格式化发票ID

在模型中定义访问器,拼接成用户需要的YYYY-NNN格式:

public function getFormattedInvoiceIdAttribute()
{
    return "{$this->invoice_year}-{$this->invoice_number}";
}

之后在代码中可以直接通过$invoice->formatted_invoice_id获取格式化后的发票ID。

索引性能说明

  • 原有的自增主键id依然是主键索引,数据库的增删改查性能不受影响
  • 联合唯一索引(invoice_year, invoice_number)是高效的:整数类型的索引比字符串索引更快,前缀为年份的联合索引在按年份查询发票时,能快速定位到目标数据范围
  • 不要直接用字符串字段存储2023-344这类格式:字符串索引的查询效率远低于整数索引,且无法自动维护年度自增逻辑,还会增加存储成本

不推荐的方案

  • 直接修改MySQL自增列:MySQL自增列无法按年份重置,强行用触发器实现会导致锁表、性能下降等问题
  • 用单一字符串字段存储格式化ID:无法保证唯一性,索引性能差,维护成本高

内容的提问来源于stack exchange,提问作者Yusuf Bouzekri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 17:06:16