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

Go语言控制器中如何将记录保存至两个关联数据表

问题分析与解决方案

核心错误点

  1. 错误实例化Horary对象:创建排班数据时误用models.Employe替代models.Horary,导致数据未写入horary表,反而错误存入employee表,无法获取有效Horary ID关联员工。
  2. 结构体外键配置错误:Employe中PinEmploye的FOREIGNKEY标签属于无效配置,且ScheduleId缺少明确的外键关联标签,GORM无法识别其与Horary表的关联关系。
  3. 请求字段解析失效:Employe结构体中的Arrival和Departure被注释,JSON解析时无法获取这两个字段值,创建Horary会传入空时间,可能触发数据库约束错误。
  4. 错误处理不当:log.Fatal会直接终止整个服务,不适用于HTTP请求场景,应改为日志记录而非进程退出。

修正后的代码

1. 修正结构体定义

type Employe struct {
    ID         int       `json:"id" gorm:"primaryKey;autoIncrement"`
    PinEmploye string    `json:"pinEmploye"` // 移除错误的FOREIGNKEY标签
    FirstName  string    `json:"first_name"`
    LastName   string    `json:"last_name"`
    Company    string    `json:"company"`
    Position   string    `json:"position"`
    ScheduleId int       `json:"schedule_id" gorm:"foreignKey:ScheduleId;references:ID"` // 明确关联Horary的ID
    Arrival    time.Time `json:"arrival"` // 取消注释以接收请求中的排班时间
    Departure  time.Time `json:"departure"` // 取消注释
    CreatedAt  time.Time `json:"fechacreacion" gorm:"autoCreateTime"` // 自动生成创建时间
}

type Horary struct {
    ID        int       `json:"id" gorm:"primaryKey;autoIncrement"`
    Arrival   time.Time `json:"arrival"`
    Departure time.Time `json:"departure"`
}

2. 修正控制器Save函数

func Save(writer http.ResponseWriter, request *http.Request) {
    employe := models.Employe{}
    db := common.GetConnection()

    // 解析请求体
    err := json.NewDecoder(request.Body).Decode(&employe)
    if err != nil {
        log.Printf("解析请求体失败: %v", err)
        common.SendError(writer, http.StatusBadRequest)
        return
    }

    // 生成员工PIN
    randomBytes := make([]byte, 2)
    _, err = rand.Read(randomBytes)
    if err != nil {
        log.Printf("生成PIN失败: %v", err)
        common.SendError(writer, http.StatusBadRequest)
        return
    }
    employe.PinEmploye = hex.EncodeToString(randomBytes)

    // 正确实例化Horary并保存
    horary := models.Horary{
        Arrival:   employe.Arrival,
        Departure: employe.Departure,
    }
    if err = db.Create(&horary).Error; err != nil {
        log.Printf("保存排班信息失败: %v", err)
        common.SendError(writer, http.StatusInternalServerError)
        return
    }

    // 关联排班ID到员工并保存
    employe.ScheduleId = horary.ID
    if err = db.Create(&employe).Error; err != nil {
        log.Printf("保存员工信息失败: %v", err)
        common.SendError(writer, http.StatusInternalServerError)
        return
    }

    // 返回响应
    respJson, _ := json.Marshal(employe)
    common.SendResponse(writer, http.StatusCreated, respJson)
    fmt.Println(employe)
}

额外优化建议

  • 事务一致性保障:将员工和排班的保存操作包裹在GORM事务中,避免单步失败导致数据不一致:
    tx := db.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()
    
    if err := tx.Create(&horary).Error; err != nil {
        tx.Rollback()
        log.Printf("保存排班失败: %v", err)
        common.SendError(writer, http.StatusInternalServerError)
        return
    }
    
    employe.ScheduleId = horary.ID
    if err := tx.Create(&employe).Error; err != nil {
        tx.Rollback()
        log.Printf("保存员工失败: %v", err)
        common.SendError(writer, http.StatusInternalServerError)
        return
    }
    
    tx.Commit()
    
  • 时间格式适配:若请求时间格式非Go默认的RFC3339,需指定自定义解析格式,或在结构体标签中添加gorm:"type:datetime"确保数据库字段匹配。
  • 调试友好性:开发环境下可在错误响应中返回具体错误信息,便于快速定位问题。

内容的提问来源于stack exchange,提问作者Edwin Fernando Pirajan Arevalo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 15:50:26