diff --git a/controllers/train.go b/controllers/train.go index aabb410..f818ce2 100644 --- a/controllers/train.go +++ b/controllers/train.go @@ -7,10 +7,15 @@ import ( ) import ( + "encoding/json" "errors" + "fmt" + "time" + "github.com/gin-gonic/gin" "gorm.io/gorm" "gorm.io/gorm/clause" + "gorm.io/gorm/schema" "hr_receiver/config" "hr_receiver/models" "math" @@ -97,6 +102,122 @@ func (tc *TrainingController) CreateTrainingRecord(c *gin.Context) { }) } +type trainingSessionRequest struct { + Tid int `json:"tid"` + Time int64 `json:"time"` + TestTime int64 `json:"testTime"` + EndTime int64 `json:"endTime"` + Name string `json:"name"` + RunType string `json:"runType"` + Gender string `json:"gender"` + Age int `json:"age"` + MaxHeartRate int `json:"maxHeartRate"` + Duration int `json:"duration"` + PeopleNum int `json:"peopleNum"` + Evaluation string `json:"evaluation"` + AiResult string `json:"aiResult"` + IsStart bool `json:"isStart"` + RegionID uint32 `json:"regionId"` + AppName string `json:"appName"` +} + +func (tc *TrainingController) UploadTrainingSession(c *gin.Context) { + var req trainingSessionRequest + if err := c.ShouldBindJSON(&req); err != nil { + writeError(c, http.StatusBadRequest, err.Error()) + return + } + + trainID := fmt.Sprintf("%d", req.Time) + now := time.Now().UnixMilli() + flavorType := "heartrate" + + identifier := schema.NamingStrategy{}.IndexName( + "mqtt_training_session", + fmt.Sprintf("%s_%d_%s", flavorType, req.RegionID, trainID), + ) + + rawPayload, _ := json.Marshal(req) + + if req.IsStart { + record := &models.MqttTrainingSessionRecord{ + Identifier: identifier, + TestID: trainID, + EventType: "start_test", + RegionID: req.RegionID, + FlavorType: flavorType, + RawFlavor: "hr", + AppName: req.AppName, + TrainId: trainID, + PeopleNum: req.PeopleNum, + StartedAt: &req.Time, + PublishedAt: now, + ReceivedAt: now, + RawPayload: string(rawPayload), + } + err := tc.DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "identifier"}}, + DoUpdates: clause.Assignments(map[string]interface{}{"started_at": *record.StartedAt, "train_id": record.TrainId, "updated_at": time.Now()}), + }).Create(record).Error + if err != nil { + writeError(c, http.StatusInternalServerError, "failed to save session") + return + } + writeSuccess(c, http.StatusOK, "session start registered", nil) + return + } + + var existing models.MqttTrainingSessionRecord + err := tc.DB.Where("train_id = ?", trainID).First(&existing).Error + if err != nil { + record := &models.MqttTrainingSessionRecord{ + Identifier: identifier, + TestID: trainID, + EventType: "stop_test", + RegionID: req.RegionID, + FlavorType: flavorType, + RawFlavor: "hr", + AppName: req.AppName, + TrainId: trainID, + PeopleNum: req.PeopleNum, + StartedAt: &req.Time, + EndedAt: &req.EndTime, + PublishedAt: now, + ReceivedAt: now, + RawPayload: string(rawPayload), + } + err = tc.DB.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "identifier"}}, + DoUpdates: clause.Assignments(map[string]interface{}{ + "ended_at": *record.EndedAt, + "people_num": record.PeopleNum, + "event_type": record.EventType, + "updated_at": time.Now(), + }), + }).Create(record).Error + if err != nil { + writeError(c, http.StatusInternalServerError, "failed to create session") + return + } + writeSuccess(c, http.StatusOK, "session created", nil) + return + } + + updates := map[string]interface{}{ + "event_type": "stop_test", + "ended_at": req.EndTime, + "people_num": req.PeopleNum, + "received_at": now, + "raw_payload": string(rawPayload), + "updated_at": time.Now(), + } + if err := tc.DB.Model(&existing).Updates(updates).Error; err != nil { + writeError(c, http.StatusInternalServerError, "failed to update session") + return + } + writeSuccess(c, http.StatusOK, "session updated", nil) +} + // analysis_response.go type AnalysisResponse struct { Status string `json:"status"` // 状态码 diff --git a/models/mqtt_data.go b/models/mqtt_data.go index dca78e7..f3e1c7c 100644 --- a/models/mqtt_data.go +++ b/models/mqtt_data.go @@ -66,6 +66,8 @@ type MqttTrainingSessionRecord struct { FlavorType string `gorm:"size:64;index" json:"flavorType"` RawFlavor string `gorm:"size:64" json:"rawFlavor"` AppName string `gorm:"size:255" json:"appName"` + TrainId string `gorm:"size:255;index" json:"trainId"` + PeopleNum int `gorm:"type:int" json:"peopleNum"` StartedAt *int64 `gorm:"type:bigint;index" json:"startedAt"` EndedAt *int64 `gorm:"type:bigint;index" json:"endedAt"` PublishedAt int64 `gorm:"type:bigint;index" json:"publishedAt"` diff --git a/mqtt/listener.go b/mqtt/listener.go index ea06184..954a454 100644 --- a/mqtt/listener.go +++ b/mqtt/listener.go @@ -34,6 +34,7 @@ type trainingSessionPayload struct { Type string `json:"type"` EventType string `json:"eventType"` TestID string `json:"testId"` + TrainId string `json:"trainId"` RegionID string `json:"regionId"` Flavor string `json:"flavor"` AppName string `json:"appName"` @@ -400,6 +401,7 @@ func buildTrainingSessionRecord(topic string, payload []byte, now int64) (*model FlavorType: flavorType, RawFlavor: event.Flavor, AppName: event.AppName, + TrainId: event.TrainId, PublishedAt: publishedAt, ReceivedAt: now, RawPayload: string(payload), diff --git a/routes/routes.go b/routes/routes.go index eae6b0f..2b3ef76 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -51,6 +51,7 @@ func SetupRouter() *gin.Engine { records := v1.Group("/train-records") //.Use(middleware.AuthMiddleware()) { records.POST("", trainingController.CreateTrainingRecord) + records.POST("/session", trainingController.UploadTrainingSession) records.GET("/analysis", trainingController.HandleCurveAnalysis) records.POST("/analysis-by-ai", trainingController.AnalyzeByAI) // 可扩展其他路由:GET, PUT, DELETE等