Laravel实现每日重置的带日期自增编码方案求助
Laravel实现按日期重置的自增编码方案
问题分析
你之前的方案基于全局id生成序号,属于全局递增逻辑,无法实现次日重置计数的需求。要解决这个问题,核心是按日期分组维护当日的自增序号,下面给出两种可行的实现方案。
方案一:新增字段存储日期与当日序号(推荐,高效且稳定)
1. 修改迁移文件
添加存储记录日期和当日序号的字段,并添加联合唯一索引避免重复:
public function up() { Schema::create('records', function (Blueprint $table) { $table->id(); $table->date('record_date'); // 存储记录所属的日期 $table->unsignedInteger('daily_sequence'); // 当日的自增序号 // 其他业务字段... // 联合唯一索引:确保同一日期下序号不重复 $table->unique(['record_date', 'daily_sequence']); }); }
执行迁移:php artisan migrate
2. 模型中自动填充序号与日期
在Record模型的启动方法中,监听创建事件,自动计算并填充当日序号:
use Carbon\Carbon; use Illuminate\Support\Facades\DB; class Record extends Model { protected $fillable = ['record_date', 'daily_sequence', ...]; // 补充你的业务字段 protected static function boot() { parent::boot(); // 创建记录时自动计算当日序号 static::creating(function ($model) { DB::transaction(function () use ($model) { $today = Carbon::today()->toDateString(); // 加锁查询当日最后一条记录的序号,避免并发冲突 $lastSequence = static::where('record_date', $today)->lockForUpdate()->max('daily_sequence'); $model->daily_sequence = $lastSequence ? $lastSequence + 1 : 1; $model->record_date = $today; }); }); // 定义全局查询作用域,自动拼接编码 static::addGlobalScope('withCode', function ($query) { $query->select('*', DB::raw("CONCAT('Z', DATE_FORMAT(record_date, '%m%d'), '-', LPAD(daily_sequence, 5, '0')) as CODE")); }); } }
通过全局查询作用域自动拼接编码,无需在控制器中循环处理。
3. 控制器中直接使用
public function get_records(Request $request){ $records = Record::get(); return $records; // 返回结果中自动包含CODE字段,格式如:Z0331-00001、Z0401-00001 }
方案二:无新增字段,动态计算序号(适合小数据量场景)
如果不想修改表结构,可以在创建记录时动态计算当日序号,但需注意并发冲突问题:
1. 模型中添加生成编码的方法
use Carbon\Carbon; use Illuminate\Support\Facades\DB; class Record extends Model { // 生成当日新编码 public static function generateTodayCode() { $today = Carbon::today(); $dateStr = $today->format('md'); DB::transaction(function () use (&$sequence) { // 查询当日最后一条记录的编码,提取序号 $lastRecord = static::whereDate('created_at', $today)->lockForUpdate()->latest()->first(); $sequence = $lastRecord ? intval(substr($lastRecord->code, 6)) + 1 : 1; }); $sequenceStr = str_pad($sequence, 5, '0', STR_PAD_LEFT); return "Z{$dateStr}-{$sequenceStr}"; } // 访问器获取已存在记录的编码 public function getCodeAttribute() { $dateStr = Carbon::parse($this->created_at)->format('md'); // 计算该记录是当日的第几条 $sequence = static::whereDate('created_at', $this->created_at)->where('id', '<=', $this->id)->count(); $sequenceStr = str_pad($sequence, 5, '0', STR_PAD_LEFT); return "Z{$dateStr}-{$sequenceStr}"; } }
2. 创建记录时使用
public function store(Request $request) { $code = Record::generateTodayCode(); Record::create([ 'code' => $code, // 其他业务字段... ]); }
注意:此方案在数据量大时性能较差,且如果删除当日记录,后续生成的序号会出现断层。
注意事项
- 方案一的
lockForUpdate和事务是为了避免高并发场景下的序号重复问题; - 如果使用方案一,不需要在数据库中存储最终编码,通过访问器或查询拼接即可,节省存储;
- 若需要修改历史记录的编码,只需调整
record_date和daily_sequence字段,编码会自动更新。
内容的提问来源于stack exchange,提问作者Keyboard Corporation
相关产品推荐
相关产品推荐

