From 4e10359a5b0f9c2c3e0785077d204247e92c236a Mon Sep 17 00:00:00 2001 From: laoboli <1293528695@qq.com> Date: Wed, 25 Jun 2025 09:36:27 +0800 Subject: [PATCH] feat: step train. --- controllers/step_train.go | 84 +++++++++++++++++++++++++++++++++++++++ main.go | 9 ++++- models/step_train.go | 38 ++++++++++++++++++ routes/routes.go | 6 +++ 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 controllers/step_train.go create mode 100644 models/step_train.go diff --git a/controllers/step_train.go b/controllers/step_train.go new file mode 100644 index 0000000..79b92c7 --- /dev/null +++ b/controllers/step_train.go @@ -0,0 +1,84 @@ +package controllers + +import ( + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hr_receiver/config" + "hr_receiver/models" + "net/http" +) + +type StepTrainingController struct { + DB *gorm.DB +} + +func NewStepTrainingController() *StepTrainingController { + return &StepTrainingController{DB: config.DB} +} + +// 接收训练记录 +func (tc *StepTrainingController) CreateTrainingRecord(c *gin.Context) { + var record models.StepTrainRecord + + // 绑定并验证JSON数据 + if err := c.ShouldBindJSON(&record); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 使用事务保存数据[4](@ref) + err := tc.DB.Transaction(func(tx *gorm.DB) error { + // 保存主记录 + if err := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "train_id"}}, // 指定冲突的列 + DoUpdates: clause.Assignments(map[string]interface{}{ + "max_heart_rate": record.MaxHeartRate, + "start_time": record.StartTime, + "end_time": record.EndTime, + "duration": record.Duration, + "dead_zone": record.DeadZone, + "name": record.Name, + "evaluation": record.Evaluation, + }), + }).Omit("HeartRates", "StrideFreqs").Create(&record).Error; err != nil { + return err + } + + // 保存关联的心率数据 + for i := range record.HeartRates { + if err := tx.Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "identifier"}}, // 指定冲突的列 + DoUpdates: clause.Assignments(map[string]interface{}{"value": record.HeartRates[i].Value, "time": record.HeartRates[i].Time}), + }, + ).Create(&record.HeartRates[i]).Error; err != nil { + + return err + } + } + for i := range record.StrideFreqs { + if err := tx.Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "identifier"}}, // 指定冲突的列 + DoUpdates: clause.Assignments(map[string]interface{}{"value": record.StrideFreqs[i].Value, "time": record.StrideFreqs[i].Time}), + }, + ).Create(&record.StrideFreqs[i]).Error; err != nil { + + return err + } + } + + return nil + }) + + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusCreated, gin.H{ + "message": "数据保存成功", + "id": record.TrainId, + }) +} diff --git a/main.go b/main.go index b5884ce..7b0cd02 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,14 @@ func main() { config.DB.Debug() // 自动迁移模型 - config.DB.AutoMigrate(&models.TrainRecord{}, &models.TrainingData{}, &models.Belt{}, &models.HeartRate{}, &models.BeltAnalysis{}) + config.DB.AutoMigrate(&models.TrainRecord{}, + &models.TrainingData{}, + &models.Belt{}, + &models.HeartRate{}, + &models.BeltAnalysis{}, + &models.StepTrainRecord{}, + &models.StepHeartRate{}, + &models.StepStrideFreq{}) // 启动服务 r := routes.SetupRouter() diff --git a/models/step_train.go b/models/step_train.go new file mode 100644 index 0000000..cf193a3 --- /dev/null +++ b/models/step_train.go @@ -0,0 +1,38 @@ +package models + +import "gorm.io/gorm" + +type StepStrideFreq struct { + gorm.Model + TrainId uint `gorm:"column:train_id; index" json:"trainId"` // 外键关联训练记录[4](@ref) + Time int64 `gorm:"type:bigint" json:"time"` // 保持与前端一致的毫秒时间戳[3](@ref) + Value int `gorm:"type:int" json:"value"` + PredictValue int `gorm:"type:int" json:"predictValue"` + Identifier string `gorm:"uniqueIndex;type:varchar(255)" json:"identifier"` +} + +// 对应Flutter的HeartRate结构 +type StepHeartRate struct { + gorm.Model + TrainId uint `gorm:"column:train_id; index" json:"trainId"` // 外键关联训练记录[4](@ref) + Time int64 `gorm:"type:bigint" json:"time"` // 保持与前端一致的毫秒时间戳[3](@ref) + Value int `gorm:"type:int" json:"value"` + HeartRateType int `gorm:"type:int" json:"predictValue"` + Identifier string `gorm:"uniqueIndex;type:varchar(255)" json:"identifier"` +} + +// 对应Flutter的TrainRecord结构 +type StepTrainRecord struct { + gorm.Model + TrainId uint `gorm:"uniqueIndex" json:"tid"` // 对应Dart的tid字段 + StartTime int64 `gorm:"type:bigint" json:"time"` // 开始时间戳 + EndTime int64 `gorm:"type:bigint" json:"endTime"` // 结束时间戳[3](@ref) + Name string `gorm:"size:100" json:"name"` + RunType string `gorm:"size:100" json:"RunType"` + MaxHeartRate int `gorm:"type:int" json:"maxHeartRate"` + Duration int `gorm:"type:int" json:"duration"` // 持续时间(秒) + DeadZone int `gorm:"type:int" json:"deadZone"` + Evaluation string `gorm:"size:50" json:"evaluation"` + HeartRates []StepHeartRate `gorm:"foreignKey:TrainId;references:TrainId" json:"HeartRates"` + StrideFreqs []StepStrideFreq `gorm:"foreignKey:TrainId;references:TrainId" json:"strideFreqs"` +} diff --git a/routes/routes.go b/routes/routes.go index 4a69fe4..cd6a98d 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -12,6 +12,7 @@ func SetupRouter() *gin.Engine { r := gin.Default() r.Use(middleware.GzipMiddleware()) trainingController := controllers.NewTrainingController() + stepTrainController := controllers.NewStepTrainingController() v1 := r.Group("/api/v1") { @@ -21,6 +22,11 @@ func SetupRouter() *gin.Engine { records.GET("/analysis", trainingController.HandleCurveAnalysis) // 可扩展其他路由:GET, PUT, DELETE等 } + steps := v1.Group("/step") //.Use(middleware.AuthMiddleware()) + { + steps.POST("", stepTrainController.CreateTrainingRecord) + // 可扩展其他路由:GET, PUT, DELETE等 + } auth := v1.Group("/auth") { auth.GET("/token", func(c *gin.Context) {