冲突检测必须在数据库层面加锁,应用层查后再写会导致竞态;应使用事务+select ... for update行锁、datetime字段类型、time.time映射及正确重叠判断条件,并统一在model层封装带事务的checkconflict方法。

冲突检测的核心逻辑必须在数据库层面加锁
直接在应用层查完再写,必然出现竞态——两个请求同时查到“当前无预约”,然后都插入成功。Beego 本身不解决这个问题,得靠 SELECT ... FOR UPDATE 或唯一约束兜底。
推荐做法是:用事务包裹查询 + 插入,并在查询会议室可用时段时显式加行锁:
tx, _ := orm.GetOrm().Raw("SELECT id FROM meeting_room_slots WHERE room_id = ? AND start_time ? FOR UPDATE", roomId, endTime, startTime).QueryRows(&slotIds)
注意点:
-
FOR UPDATE只在 InnoDB 且事务中生效,Beego 的orm.Begin()必须显式调用 - 不要用
time.Now().Format("2006-01-02 15:04:05")拼 SQL,用参数化防止注入和时区错乱 - 如果用 PostgreSQL,改用
SELECT ... FOR UPDATE OF meeting_room_slots,语法略有不同
时间重叠判断不能只靠字符串比较
常见错误是把 start_time 和 end_time 当成字符串做 >/ 判断,结果 MySQL 按字典序比,<code>"10:00" 会小于 "9:30"。
正确做法是确保字段类型为 DATETIME 或 TIMESTAMP,并在 Beego 模型中用 time.Time 类型映射:
type MeetingBooking struct {
Id int `orm:"auto"`
RoomId int `orm:"column(room_id)"`
StartTime time.Time `orm:"column(start_time);type(datetime)"`
EndTime time.Time `orm:"column(end_time);type(datetime)"`
}
重叠条件统一写成:
WHERE (start_time ?)
即:新预约的开始时间 已有记录的结束时间,且新预约的结束时间 <code>> 已有记录的开始时间。
Beego 中如何安全封装冲突检测方法
别把检测逻辑散落在 controller 里。建议在 model 层写一个带事务的方法,返回 error 而不是布尔值——便于区分“冲突”和“数据库异常”:
func (m *MeetingBooking) CheckConflict(tx orm.TxOrmer, roomId int, start, end time.Time) error {
var count int
err := tx.Raw("SELECT COUNT(*) FROM meeting_bookings WHERE room_id = ? AND start_time ?", roomId, end, start).QueryRow(&count)
if err != nil {
return err
}
if count > 0 {
return errors.New("booking conflict detected")
}
return nil
}
调用时注意:
- 传入的是
tx(事务对象),不是全局orm,否则锁失效 - 错误要区分处理:
errors.Is(err, sql.ErrNoRows)不需要,但err == nil才代表可插入 - Beego v2.x 默认关闭 prepared statement,高并发下建议开启以提升
Raw()性能
前端传参和时区容易被忽略的坑
用户选的是本地时间,但服务器可能在 UTC 时区。如果 Beego 的 time.Local 没配对,time.Parse 解析出来的时间会偏移。
解决方案:
- 前端统一传 ISO8601 格式带时区的字符串,如
"2024-06-15T14:00:00+08:00" - Beego 启动时设置:
time.Local = time.FixedZone("CST", 8*60*60)(中国标准时间) - 数据库连接 URL 加上
loc=Asia%2FShanghai(MySQL)或timezone=Asia/Shanghai(PostgreSQL) - 验证方式:打印
start.Unix()和数据库存的值是否一致,不一致就说明时区链断了
冲突检测失效往往不是逻辑错,而是时间没对齐。只要有一环用系统默认时区,整条链就不可信。











